From daa301045844a422219833e7f5a146839e099100 Mon Sep 17 00:00:00 2001 From: Bradley Nelson Date: Wed, 17 Jun 2026 17:06:30 -0600 Subject: [PATCH 1/6] init new frontend --- .github/workflows/ci.yml | 77 +- Dockerfile | 15 +- build.rs | 9 + devenv.nix | 2 +- frontend/.gitignore | 8 + frontend/.npmrc | 2 + frontend/.nvmrc | 1 + frontend/.prettierignore | 13 + frontend/.prettierrc | 8 + frontend/.stylelintignore | 8 + frontend/.stylelintrc.json | 32 + frontend/eslint.config.js | 32 + frontend/package-lock.json | 6261 +++++++++++++++++ frontend/package.json | 45 + frontend/src/app.d.ts | 12 + frontend/src/app.html | 31 + frontend/src/hooks.client.ts | 19 + frontend/src/lib/api/client.test.ts | 130 + frontend/src/lib/api/client.ts | 153 + frontend/src/lib/api/csrf.ts | 19 + frontend/src/lib/api/endpoints/admin.ts | 101 + frontend/src/lib/api/endpoints/auth.ts | 62 + frontend/src/lib/api/endpoints/device.ts | 26 + frontend/src/lib/api/endpoints/favorites.ts | 41 + frontend/src/lib/api/endpoints/files.ts | 60 + frontend/src/lib/api/endpoints/folders.ts | 89 + frontend/src/lib/api/endpoints/grants.ts | 59 + frontend/src/lib/api/endpoints/groups.ts | 70 + frontend/src/lib/api/endpoints/music.ts | 103 + frontend/src/lib/api/endpoints/photos.ts | 26 + frontend/src/lib/api/endpoints/profile.ts | 45 + frontend/src/lib/api/endpoints/recent.ts | 32 + frontend/src/lib/api/endpoints/resources.ts | 42 + frontend/src/lib/api/endpoints/share.ts | 91 + frontend/src/lib/api/endpoints/trash.ts | 37 + frontend/src/lib/api/types.ts | 198 + frontend/src/lib/components/AppShell.svelte | 232 + frontend/src/lib/components/FileRow.svelte | 79 + frontend/src/lib/components/Modal.svelte | 112 + .../lib/components/ResourceListShell.svelte | 95 + frontend/src/lib/components/Toaster.svelte | 73 + frontend/src/lib/i18n/i18n.test.ts | 71 + frontend/src/lib/i18n/index.svelte.ts | 202 + frontend/src/lib/icons/Icon.svelte | 41 + frontend/src/lib/icons/registry.ts | 523 ++ frontend/src/lib/stores/files.svelte.ts | 53 + frontend/src/lib/stores/session.svelte.ts | 69 + frontend/src/lib/stores/theme.svelte.ts | 43 + frontend/src/lib/stores/ui.svelte.ts | 32 + frontend/src/lib/styles/app.css | 10 + frontend/src/lib/styles/base/a11y.css | 146 + frontend/src/lib/styles/base/animations.css | 18 + frontend/src/lib/styles/base/forms.css | 60 + frontend/src/lib/styles/base/reset.css | 58 + frontend/src/lib/styles/base/typography.css | 82 + frontend/src/lib/styles/base/variables.css | 694 ++ frontend/src/lib/styles/legacy.css | 13 + frontend/src/lib/styles/legacy/auth.css | 805 +++ frontend/src/lib/styles/legacy/breadcrumb.css | 65 + frontend/src/lib/styles/legacy/buttons.css | 259 + frontend/src/lib/styles/legacy/content.css | 113 + .../src/lib/styles/legacy/fileManager.css | 17 + .../src/lib/styles/legacy/resourceList.css | 1087 +++ frontend/src/lib/styles/legacy/sidebar.css | 258 + frontend/src/lib/styles/legacy/skeleton.css | 93 + frontend/src/lib/styles/legacy/topbar.css | 257 + frontend/src/lib/utils/display.ts | 27 + frontend/src/lib/utils/format.test.ts | 23 + frontend/src/lib/utils/format.ts | 13 + frontend/src/lib/utils/legacyHash.test.ts | 34 + frontend/src/lib/utils/legacyHash.ts | 37 + frontend/src/routes/+layout.svelte | 62 + frontend/src/routes/+layout.ts | 6 + frontend/src/routes/+page.svelte | 11 + frontend/src/routes/admin/+page.svelte | 452 ++ frontend/src/routes/device/+page.svelte | 177 + frontend/src/routes/favorites/+page.svelte | 88 + .../src/routes/files/[...path]/+page.svelte | 398 ++ frontend/src/routes/groups/+page.svelte | 306 + frontend/src/routes/login/+page.svelte | 107 + frontend/src/routes/music/+page.svelte | 360 + .../src/routes/nextcloud/error/+page.svelte | 49 + .../src/routes/nextcloud/login/+page.svelte | 129 + .../src/routes/nextcloud/success/+page.svelte | 34 + frontend/src/routes/photos/+page.svelte | 113 + frontend/src/routes/profile/+page.svelte | 212 + frontend/src/routes/recent/+page.svelte | 85 + frontend/src/routes/s/[token]/+page.svelte | 294 + .../src/routes/shared-with-me/+page.svelte | 62 + frontend/src/routes/shared/+page.svelte | 62 + frontend/src/routes/trash/+page.svelte | 127 + frontend/static/.gitkeep | 0 frontend/static/locales/ar.json | 980 +++ frontend/static/locales/de.json | 980 +++ frontend/static/locales/en.json | 1027 +++ frontend/static/locales/es.json | 980 +++ frontend/static/locales/fa.json | 980 +++ frontend/static/locales/fr.json | 980 +++ frontend/static/locales/hi.json | 980 +++ frontend/static/locales/it.json | 980 +++ frontend/static/locales/ja.json | 980 +++ frontend/static/locales/ko.json | 980 +++ frontend/static/locales/nl.json | 980 +++ frontend/static/locales/pl.json | 980 +++ frontend/static/locales/pt.json | 980 +++ frontend/static/locales/ru.json | 980 +++ frontend/static/locales/zh-TW.json | 980 +++ frontend/static/locales/zh.json | 980 +++ frontend/svelte.config.js | 37 + frontend/tsconfig.json | 14 + frontend/vite.config.ts | 33 + frontend/vitest-setup.ts | 1 + justfile | 40 +- src/interfaces/web/mod.rs | 96 +- 114 files changed, 32716 insertions(+), 119 deletions(-) create mode 100644 frontend/.gitignore create mode 100644 frontend/.npmrc create mode 100644 frontend/.nvmrc create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc create mode 100644 frontend/.stylelintignore create mode 100644 frontend/.stylelintrc.json create mode 100644 frontend/eslint.config.js create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/app.d.ts create mode 100644 frontend/src/app.html create mode 100644 frontend/src/hooks.client.ts create mode 100644 frontend/src/lib/api/client.test.ts create mode 100644 frontend/src/lib/api/client.ts create mode 100644 frontend/src/lib/api/csrf.ts create mode 100644 frontend/src/lib/api/endpoints/admin.ts create mode 100644 frontend/src/lib/api/endpoints/auth.ts create mode 100644 frontend/src/lib/api/endpoints/device.ts create mode 100644 frontend/src/lib/api/endpoints/favorites.ts create mode 100644 frontend/src/lib/api/endpoints/files.ts create mode 100644 frontend/src/lib/api/endpoints/folders.ts create mode 100644 frontend/src/lib/api/endpoints/grants.ts create mode 100644 frontend/src/lib/api/endpoints/groups.ts create mode 100644 frontend/src/lib/api/endpoints/music.ts create mode 100644 frontend/src/lib/api/endpoints/photos.ts create mode 100644 frontend/src/lib/api/endpoints/profile.ts create mode 100644 frontend/src/lib/api/endpoints/recent.ts create mode 100644 frontend/src/lib/api/endpoints/resources.ts create mode 100644 frontend/src/lib/api/endpoints/share.ts create mode 100644 frontend/src/lib/api/endpoints/trash.ts create mode 100644 frontend/src/lib/api/types.ts create mode 100644 frontend/src/lib/components/AppShell.svelte create mode 100644 frontend/src/lib/components/FileRow.svelte create mode 100644 frontend/src/lib/components/Modal.svelte create mode 100644 frontend/src/lib/components/ResourceListShell.svelte create mode 100644 frontend/src/lib/components/Toaster.svelte create mode 100644 frontend/src/lib/i18n/i18n.test.ts create mode 100644 frontend/src/lib/i18n/index.svelte.ts create mode 100644 frontend/src/lib/icons/Icon.svelte create mode 100644 frontend/src/lib/icons/registry.ts create mode 100644 frontend/src/lib/stores/files.svelte.ts create mode 100644 frontend/src/lib/stores/session.svelte.ts create mode 100644 frontend/src/lib/stores/theme.svelte.ts create mode 100644 frontend/src/lib/stores/ui.svelte.ts create mode 100644 frontend/src/lib/styles/app.css create mode 100644 frontend/src/lib/styles/base/a11y.css create mode 100644 frontend/src/lib/styles/base/animations.css create mode 100644 frontend/src/lib/styles/base/forms.css create mode 100644 frontend/src/lib/styles/base/reset.css create mode 100644 frontend/src/lib/styles/base/typography.css create mode 100644 frontend/src/lib/styles/base/variables.css create mode 100644 frontend/src/lib/styles/legacy.css create mode 100644 frontend/src/lib/styles/legacy/auth.css create mode 100644 frontend/src/lib/styles/legacy/breadcrumb.css create mode 100644 frontend/src/lib/styles/legacy/buttons.css create mode 100644 frontend/src/lib/styles/legacy/content.css create mode 100644 frontend/src/lib/styles/legacy/fileManager.css create mode 100644 frontend/src/lib/styles/legacy/resourceList.css create mode 100644 frontend/src/lib/styles/legacy/sidebar.css create mode 100644 frontend/src/lib/styles/legacy/skeleton.css create mode 100644 frontend/src/lib/styles/legacy/topbar.css create mode 100644 frontend/src/lib/utils/display.ts create mode 100644 frontend/src/lib/utils/format.test.ts create mode 100644 frontend/src/lib/utils/format.ts create mode 100644 frontend/src/lib/utils/legacyHash.test.ts create mode 100644 frontend/src/lib/utils/legacyHash.ts create mode 100644 frontend/src/routes/+layout.svelte create mode 100644 frontend/src/routes/+layout.ts create mode 100644 frontend/src/routes/+page.svelte create mode 100644 frontend/src/routes/admin/+page.svelte create mode 100644 frontend/src/routes/device/+page.svelte create mode 100644 frontend/src/routes/favorites/+page.svelte create mode 100644 frontend/src/routes/files/[...path]/+page.svelte create mode 100644 frontend/src/routes/groups/+page.svelte create mode 100644 frontend/src/routes/login/+page.svelte create mode 100644 frontend/src/routes/music/+page.svelte create mode 100644 frontend/src/routes/nextcloud/error/+page.svelte create mode 100644 frontend/src/routes/nextcloud/login/+page.svelte create mode 100644 frontend/src/routes/nextcloud/success/+page.svelte create mode 100644 frontend/src/routes/photos/+page.svelte create mode 100644 frontend/src/routes/profile/+page.svelte create mode 100644 frontend/src/routes/recent/+page.svelte create mode 100644 frontend/src/routes/s/[token]/+page.svelte create mode 100644 frontend/src/routes/shared-with-me/+page.svelte create mode 100644 frontend/src/routes/shared/+page.svelte create mode 100644 frontend/src/routes/trash/+page.svelte create mode 100644 frontend/static/.gitkeep create mode 100644 frontend/static/locales/ar.json create mode 100644 frontend/static/locales/de.json create mode 100644 frontend/static/locales/en.json create mode 100644 frontend/static/locales/es.json create mode 100644 frontend/static/locales/fa.json create mode 100644 frontend/static/locales/fr.json create mode 100644 frontend/static/locales/hi.json create mode 100644 frontend/static/locales/it.json create mode 100644 frontend/static/locales/ja.json create mode 100644 frontend/static/locales/ko.json create mode 100644 frontend/static/locales/nl.json create mode 100644 frontend/static/locales/pl.json create mode 100644 frontend/static/locales/pt.json create mode 100644 frontend/static/locales/ru.json create mode 100644 frontend/static/locales/zh-TW.json create mode 100644 frontend/static/locales/zh.json create mode 100644 frontend/svelte.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 frontend/vitest-setup.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f584e87e..087901de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,15 +32,7 @@ jobs: with: filters: | frontend: - - 'static/**' - - 'biome.json' - - '.grit' - - '.stylelintrc.json' - - 'jsconfig.json' - # Audit scripts that gate frontend correctness — touch - # them and the frontend job must re-run. - - 'tools/check-missing-translations.py' - - 'tools/check-icons.py' + - 'frontend/**' backend: - 'src/**' - 'Cargo.toml' @@ -59,56 +51,31 @@ jobs: - 'src/application/adapters/plugin_user_lifecycle_hook.rs' frontend-check: - name: Frontend — CSS and JS checks (format, lint, css-rules, types) + name: Frontend — svelte-check, ESLint, Stylelint, Prettier needs: changes if: needs.changes.outputs.frontend == 'true' runs-on: ubuntu-latest + defaults: + run: + working-directory: frontend steps: - uses: actions/checkout@v4 - - name: Install Biome - uses: biomejs/setup-biome@v2 - - - name: Run Biome check - run: biome ci static/ - - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 25 + node-version: 24 + cache: npm + cache-dependency-path: frontend/package-lock.json - # because we are not using package.json - - name: Install Stylelint, TypeScript and plugins - run: | - npm install --global \ - stylelint@17 \ - postcss@8 \ - stylelint-value-no-unknown-custom-properties@6 \ - typescript + - name: Install dependencies + run: npm ci - - name: Run Stylelint - run: npx stylelint "static/css/**/*.{css,scss}" + - name: Check (svelte-check + eslint + stylelint + prettier) + run: npm run check - - name: Run TypeScript check - run: tsc -p jsconfig.json --noEmit - - - name: Check locale files are at parity with en.json - # Python 3 stdlib only — no setup step needed. - # `--check-only` keeps the CI log terse; failures still surface - # via exit code (the script returns 1 when any non-English - # locale is missing a key present in en.json). Mirrors the - # `--check-only` flag on `tools/check-icons.py` below. - # Run locally without --check-only to see the missing keys. - run: python3 tools/check-missing-translations.py --check-only - - - name: Check FA icons referenced in static/ are registered - # `--check-only` skips the Font-Awesome clone and the icons.js - # patch — it just scans `fas fa-` references and diffs - # them against OxiIcons. Exit 1 if any used icon is absent - # from the registry. Run locally without --check-only to - # auto-add missing entries from a checked-out Font-Awesome - # source. - run: python3 tools/check-icons.py --check-only + - name: Unit tests + run: npm run test:unit rust-fmt: name: Rustfmt @@ -268,14 +235,16 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + cache-dependency-path: frontend/package-lock.json + - name: Build SPA (Vite -> static-dist/) + working-directory: frontend + run: npm ci && npm run build - run: cargo build --release - # build.rs runs the deconflict pass and js_bundle_validate() — any - # duplicate declaration or parse error in the JS bundle fails here. - - name: Validate JS bundle (node --check) - # belt-and-suspenders: node --check parses the bundle without executing it. - # Catches SyntaxErrors that OXC's parse check inside build.rs would also - # catch, but gives a human-readable error line in the CI log. - run: node --check static-dist/js/app.*.js - uses: actions/upload-artifact@v4 with: name: oxicloud-release diff --git a/Dockerfile b/Dockerfile index 117edafa..1f34c157 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,16 @@ FROM rust:1.96-alpine3.24 AS base RUN apk --no-cache upgrade && \ apk add --no-cache musl-dev pkgconfig gcc perl make +# ─── Stage 1b: Build the SvelteKit frontend (Vite) ─────────────────────────── +# Produces the SPA in /static-dist. `npm ci` is cached unless the lockfile +# changes; the Rust build no longer bundles assets (see build.rs). +FROM node:24-alpine AS frontend +WORKDIR /frontend +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci +COPY frontend/ ./ +RUN npm run build + # ─── Stage 2: Cache dependencies ───────────────────────────────────────────── FROM base AS cacher WORKDIR /app @@ -39,6 +49,9 @@ COPY templates templates # Build with all optimizations (DATABASE_URL only needed at compile-time for sqlx) ARG DATABASE_URL="postgres://postgres:postgres@localhost/oxicloud" RUN DATABASE_URL="${DATABASE_URL}" cargo build --release +# The SPA is built by the frontend stage; bring it in for the runtime copy below. +# (build.rs no longer generates static-dist unless OXICLOUD_LEGACY_ASSETS=1.) +COPY --from=frontend /static-dist ./static-dist # ─── Stage 4: Minimal runtime image ────────────────────────────────────────── FROM alpine:3.24.0 @@ -71,7 +84,7 @@ COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN sed -i 's/\r//' /usr/local/bin/entrypoint.sh && \ chmod 755 /usr/local/bin/entrypoint.sh -# Copy processed static files (bundled/minified by build.rs in release) +# Copy the built SPA (produced by the Vite frontend stage) COPY --from=builder --chown=oxicloud:oxicloud /app/static-dist /app/static # Create storage directory with proper permissions RUN mkdir -p /app/storage && chown -R oxicloud:oxicloud /app/storage diff --git a/build.rs b/build.rs index e493e2be..1ae891bb 100644 --- a/build.rs +++ b/build.rs @@ -40,9 +40,18 @@ fn main() { println!("cargo:rerun-if-changed=static"); println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=OXICLOUD_LEGACY_ASSETS"); git_status(); + // Post-cutover (Svelte/Vite): the frontend is built by Vite into + // `static-dist/` and the Rust web layer serves it directly — no `include_str!` + // HTML, no Rust-side bundling. The legacy pure-Rust asset pipeline below is + // retained, behind `OXICLOUD_LEGACY_ASSETS=1`, for one-release rollback only. + if env_or("OXICLOUD_LEGACY_ASSETS", "0") != "1" { + return; + } + // ── Guard: Docker cacher stage has no static/ ──────────────────────────── if !static_dir.exists() { for name in HTML_INCLUDE { diff --git a/devenv.nix b/devenv.nix index cf2ebd75..e73ce889 100644 --- a/devenv.nix +++ b/devenv.nix @@ -22,7 +22,7 @@ cargo-audit # frontend tooling (no root package.json — these are expected as global bins) - nodejs_22 + nodejs_24 biome typescript # provides `tsc` stylelint diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..eda1f418 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +/build/ +/.svelte-kit/ +/package-lock.json.bak +.DS_Store +*.local +vite.config.ts.timestamp-* +vite.config.js.timestamp-* diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 00000000..ebb2d237 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1,2 @@ +engine-strict=false +save-exact=false diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 00000000..a45fd52c --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 00000000..1ddced4a --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,13 @@ +build/ +.svelte-kit/ +package/ +node_modules/ +package.json +package-lock.json +src/lib/i18n/locales/ +# vendored / generated — kept byte-faithful to their source +src/lib/styles/base/ +src/lib/styles/legacy/ +src/lib/styles/legacy.css +src/lib/icons/registry.ts +static/locales/ diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 00000000..95730232 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,8 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/frontend/.stylelintignore b/frontend/.stylelintignore new file mode 100644 index 00000000..7020a179 --- /dev/null +++ b/frontend/.stylelintignore @@ -0,0 +1,8 @@ +build/ +.svelte-kit/ +node_modules/ +# Ported verbatim from static/css/base — treated as vendored design tokens. +# New component styles (Svelte diff --git a/frontend/src/lib/components/FileRow.svelte b/frontend/src/lib/components/FileRow.svelte new file mode 100644 index 00000000..1a153aac --- /dev/null +++ b/frontend/src/lib/components/FileRow.svelte @@ -0,0 +1,79 @@ + + +
  • + + + {name} + {#if subtitle}{subtitle}{/if} + + {#if date}{date}{/if} + {#if actions}{@render actions()}{/if} +
  • + + diff --git a/frontend/src/lib/components/Modal.svelte b/frontend/src/lib/components/Modal.svelte new file mode 100644 index 00000000..0402b4cf --- /dev/null +++ b/frontend/src/lib/components/Modal.svelte @@ -0,0 +1,112 @@ + + + + +{#if open} + + +{/if} + + diff --git a/frontend/src/lib/components/ResourceListShell.svelte b/frontend/src/lib/components/ResourceListShell.svelte new file mode 100644 index 00000000..a357d88d --- /dev/null +++ b/frontend/src/lib/components/ResourceListShell.svelte @@ -0,0 +1,95 @@ + + +
    + {#if toolbar} +
    {@render toolbar()}
    + {/if} + + {#if error} + + {:else if loading && empty} +

    {t('common.loading', 'Loading…')}

    + {:else if empty} +

    {emptyText ?? t('common.empty', 'Nothing here yet.')}

    + {:else} +
      + {@render children()} +
    + {#if hasMore} + + {/if} + {/if} +
    + + diff --git a/frontend/src/lib/components/Toaster.svelte b/frontend/src/lib/components/Toaster.svelte new file mode 100644 index 00000000..a0954178 --- /dev/null +++ b/frontend/src/lib/components/Toaster.svelte @@ -0,0 +1,73 @@ + + +
    + {#each ui.toasts as toast (toast.id)} +
    + {toast.message} + +
    + {/each} +
    + + diff --git a/frontend/src/lib/i18n/i18n.test.ts b/frontend/src/lib/i18n/i18n.test.ts new file mode 100644 index 00000000..4bc2951b --- /dev/null +++ b/frontend/src/lib/i18n/i18n.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, it } from 'vitest'; +import { getNestedValue, interpolate, resolveBrowserLocale } from './index.svelte'; + +describe('resolveBrowserLocale', () => { + it('matches an exact full tag', () => { + expect(resolveBrowserLocale(['zh-TW'])).toBe('zh-TW'); + expect(resolveBrowserLocale(['fr-FR', 'fr'])).toBe('fr'); + }); + + it('maps Traditional Chinese variants to zh-TW', () => { + expect(resolveBrowserLocale(['zh-Hant'])).toBe('zh-TW'); + expect(resolveBrowserLocale(['zh-HK'])).toBe('zh-TW'); + expect(resolveBrowserLocale(['zh-MO'])).toBe('zh-TW'); + }); + + it('maps Simplified/other Chinese to zh', () => { + expect(resolveBrowserLocale(['zh-CN'])).toBe('zh'); + expect(resolveBrowserLocale(['zh'])).toBe('zh'); + }); + + it('falls back to the primary subtag', () => { + expect(resolveBrowserLocale(['de-AT'])).toBe('de'); + }); + + it('defaults to en when nothing matches', () => { + expect(resolveBrowserLocale(['xx-YY'])).toBe('en'); + }); +}); + +describe('getNestedValue', () => { + const dict = { + 'flat.key': 'flat value', + nav: { files: 'Files', shared: 'Shared' }, + button: { save_changes: 'Save changes' } + }; + + it('resolves a direct key that contains dots', () => { + expect(getNestedValue(dict, 'flat.key')).toBe('flat value'); + }); + + it('resolves dotted nested paths', () => { + expect(getNestedValue(dict, 'nav.files')).toBe('Files'); + }); + + it('returns null for missing keys', () => { + expect(getNestedValue(dict, 'nav.missing')).toBeNull(); + expect(getNestedValue(undefined, 'nav.files')).toBeNull(); + }); + + it('applies the prefix_suffix underscore fallback', () => { + expect(getNestedValue(dict, 'button_save_changes')).toBe('Save changes'); + }); +}); + +describe('interpolate', () => { + it('replaces {{param}} placeholders', () => { + expect(interpolate('Hello {{name}}', { name: 'Ada' })).toBe('Hello Ada'); + }); + + it('trims whitespace inside placeholders', () => { + expect(interpolate('Send to {{ email }}', { email: 'a@b.c' })).toBe('Send to a@b.c'); + }); + + it('leaves unknown placeholders intact', () => { + expect(interpolate('Hi {{name}}', {})).toBe('Hi {{name}}'); + }); + + it('coerces non-string params', () => { + expect(interpolate('{{count}} items', { count: 5 })).toBe('5 items'); + }); +}); diff --git a/frontend/src/lib/i18n/index.svelte.ts b/frontend/src/lib/i18n/index.svelte.ts new file mode 100644 index 00000000..2ab2089c --- /dev/null +++ b/frontend/src/lib/i18n/index.svelte.ts @@ -0,0 +1,202 @@ +/** + * Reactive i18n — ported from static/js/core/i18n.js. + * + * Kept as a bespoke module (rather than svelte-i18n) so the 16 existing locale + * JSON files work byte-for-byte: they use `{{param}}` interpolation, dot-notation + * nested keys, and a prefix_suffix underscore-fallback heuristic that ICU-based + * libraries don't model. `t()` reads module-level runes, so any component that + * calls it re-renders when the locale changes. + * + * Storage key `oxicloud-locale` and the server round-trip via + * PATCH /api/auth/me/profile are preserved for cross-device/email parity. + */ +import { apiFetch } from '$lib/api/client'; +import { getCsrfHeaders } from '$lib/api/csrf'; + +// Keep in sync with the locale files in static/locales (and, post-cutover, +// frontend/static/locales). Mirrors AVAILABLE_LOCALES in the legacy selector. +export const SUPPORTED_LOCALES = [ + 'en', + 'es', + 'zh', + 'zh-TW', + 'fa', + 'fr', + 'de', + 'pt', + 'nl', + 'it', + 'hi', + 'ar', + 'ru', + 'ja', + 'ko', + 'pl' +] as const; + +export type Locale = (typeof SUPPORTED_LOCALES)[number]; + +const STORAGE_KEY = 'oxicloud-locale'; + +type Dict = Record; + +/** + * Resolve the best supported locale from a browser language list. + * Priority: exact full-tag > Chinese script/region heuristics > primary subtag. + */ +export function resolveBrowserLocale( + langs: readonly string[] = typeof navigator !== 'undefined' + ? (navigator.languages ?? [navigator.language || 'en']) + : ['en'] +): Locale { + const lowerSupported = SUPPORTED_LOCALES.map((l) => l.toLowerCase()); + + for (const bl of langs) { + const idx = lowerSupported.indexOf(bl.toLowerCase()); + if (idx !== -1) return SUPPORTED_LOCALES[idx]; + } + for (const bl of langs) { + const tag = bl.toLowerCase(); + if (!tag.startsWith('zh')) continue; + const isTraditional = tag.includes('hant') || /\b(tw|hk|mo)\b/.test(tag); + const target: Locale = isTraditional ? 'zh-TW' : 'zh'; + if (SUPPORTED_LOCALES.includes(target)) return target; + } + for (const bl of langs) { + const primary = bl.substring(0, 2).toLowerCase(); + const match = SUPPORTED_LOCALES.find((l) => l === primary); + if (match) return match; + } + return 'en'; +} + +/** Resolve a dot-notation key with a prefix_suffix underscore fallback. */ +export function getNestedValue(obj: Dict | undefined, path: string): string | null { + if (obj && typeof obj === 'object' && path in obj) { + const value = obj[path]; + return typeof value === 'string' ? value : null; + } + + const keys = path.split('.'); + let current: unknown = obj; + for (const key of keys) { + if (current && typeof current === 'object' && key in (current as Dict)) { + current = (current as Dict)[key]; + } else { + if (path.includes('_') && !path.includes('.')) { + const [prefix, ...parts] = path.split('_'); + const suffix = parts.join('_'); + const branch = obj?.[prefix]; + if (branch && typeof branch === 'object' && suffix in (branch as Dict)) { + const v = (branch as Dict)[suffix]; + return typeof v === 'string' ? v : null; + } + } + return null; + } + } + return typeof current === 'string' ? current : null; +} + +/** Replace `{{param}}` placeholders; leaves unknown placeholders intact. */ +export function interpolate(text: string, params: Record): string { + return text.replace(/{{\s*([^}]+)\s*}}/g, (_, key: string) => { + const k = key.trim(); + return params[k] !== undefined ? String(params[k]) : `{{${key}}}`; + }); +} + +// ── Reactive state ───────────────────────────────────────────────────────── + +const dicts = $state>({}); +const store = $state<{ locale: string; loaded: boolean }>({ + locale: resolveBrowserLocale(), + loaded: false +}); + +async function loadDict(locale: string): Promise { + if (dicts[locale]) return dicts[locale]; + try { + const res = await fetch(`/locales/${locale}.json`); + if (!res.ok) throw new Error(`locale ${locale} ${res.status}`); + dicts[locale] = (await res.json()) as Dict; + } catch (err) { + console.error('i18n: failed to load locale', locale, err); + dicts[locale] = {}; + } + return dicts[locale]; +} + +/** + * Translate a key. + * - `t(key)` / `t(key, params)` — interpolation params object. + * - `t(key, fallback)` — string fallback used when the key is missing. + * - `t(key, params, fallback)` — both; the fallback is also interpolated. + */ +export function t( + key: string, + paramsOrFallback: string | Record = {}, + fallbackArg?: string +): string { + const isStringForm = typeof paramsOrFallback === 'string'; + const params = isStringForm ? {} : paramsOrFallback; + const fallback = isStringForm ? paramsOrFallback : (fallbackArg ?? null); + + const localeData = dicts[store.locale]; + if (!localeData) { + return fallback ? interpolate(fallback, params) : (key.split('.').pop() ?? key); + } + + let value = getNestedValue(localeData, key); + if (!value && store.locale !== 'en' && dicts.en) { + value = getNestedValue(dicts.en, key); + } + if (!value) return fallback ? interpolate(fallback, params) : key; + return interpolate(value, params); +} + +export async function initI18n(): Promise { + const saved = typeof localStorage !== 'undefined' ? localStorage.getItem(STORAGE_KEY) : null; + if (saved && (SUPPORTED_LOCALES as readonly string[]).includes(saved)) { + store.locale = saved; + } + await loadDict(store.locale); + if (store.locale !== 'en') await loadDict('en'); + store.loaded = true; +} + +export async function setLocale(locale: Locale): Promise { + if (!(SUPPORTED_LOCALES as readonly string[]).includes(locale)) { + console.error(`Locale not supported: ${locale}`); + return false; + } + await loadDict(locale); + store.locale = locale; + if (typeof localStorage !== 'undefined') localStorage.setItem(STORAGE_KEY, locale); + persistLocaleToServer(locale); + return true; +} + +/** Fire-and-forget server persistence; anonymous callers 401 and that's fine. */ +function persistLocaleToServer(locale: string): void { + apiFetch('/api/auth/me/profile', { + method: 'PATCH', + headers: { 'Content-Type': 'application/json', ...getCsrfHeaders() }, + credentials: 'same-origin', + body: JSON.stringify({ preferred_locale: locale }) + }).catch((err: unknown) => { + console.debug('locale: server persistence skipped', err); + }); +} + +export const i18n = { + t, + setLocale, + get locale() { + return store.locale; + }, + get loaded() { + return store.loaded; + }, + supported: SUPPORTED_LOCALES +}; diff --git a/frontend/src/lib/icons/Icon.svelte b/frontend/src/lib/icons/Icon.svelte new file mode 100644 index 00000000..95b4fdee --- /dev/null +++ b/frontend/src/lib/icons/Icon.svelte @@ -0,0 +1,41 @@ + + +{#if entry} + + {#if title}{title}{/if} + + +{/if} + + diff --git a/frontend/src/lib/icons/registry.ts b/frontend/src/lib/icons/registry.ts new file mode 100644 index 00000000..ac633f09 --- /dev/null +++ b/frontend/src/lib/icons/registry.ts @@ -0,0 +1,523 @@ +// AUTO-PORTED from static/js/core/icons.js (Font Awesome Free 6.7.2, CC BY 4.0). +// Each entry: [viewBox-width, path-d]. All icons use viewBox "0 0 {width} 512" +// and fill="currentColor". Keys use FA5 class names (without the "fa-" prefix). +// Do not edit by hand; regenerate from the source registry if icons change. + +export type IconEntry = readonly [number, string]; + +export const OxiIcons: Record = { + "arrow-down": [ + 512, + "M169.4 502.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 402.7 224 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 370.7-105.4-105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z" + ], + "arrow-down-short-wide": [ + 576, + "M246.6 374.6l-96 96c-12.5 12.5-32.8 12.5-45.3 0l-96-96c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L96 370.7 96 64c0-17.7 14.3-32 32-32s32 14.3 32 32l0 306.7 41.4-41.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3zM320 32l32 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 128l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 128l160 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-160 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 128l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z" + ], + "arrow-down-wide-short": [ + 576, + "M246.6 374.6l-96 96c-12.5 12.5-32.8 12.5-45.3 0l-96-96c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L96 370.7 96 64c0-17.7 14.3-32 32-32s32 14.3 32 32l0 306.7 41.4-41.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3zM320 480c-17.7 0-32-14.3-32-32s14.3-32 32-32l32 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32 0zm0-128c-17.7 0-32-14.3-32-32s14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-96 0zm0-128c-17.7 0-32-14.3-32-32s14.3-32 32-32l160 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-160 0zm0-128c-17.7 0-32-14.3-32-32s14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L320 96z" + ], + "arrow-left": [ + 448, + "M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.2 288 416 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-306.7 0L214.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z" + ], + "arrow-up": [ + 512, + "M214.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 109.3 160 480c0 17.7 14.3 32 32 32s32-14.3 32-32l0-370.7 105.4 105.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z" + ], + "arrow-up-a-z": [ + 576, + "M183.6 42.4C177.5 35.8 169 32 160 32s-17.5 3.8-23.6 10.4l-88 96c-11.9 13-11.1 33.3 2 45.2s33.3 11.1 45.2-2L128 146.3 128 448c0 17.7 14.3 32 32 32s32-14.3 32-32l0-301.7 32.4 35.4c11.9 13 32.2 13.9 45.2 2s13.9-32.2 2-45.2l-88-96zM320 320c0 17.7 14.3 32 32 32l50.7 0-73.4 73.4c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l128 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-50.7 0 73.4-73.4c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-128 0c-17.7 0-32 14.3-32 32zM416 32c-12.1 0-23.2 6.8-28.6 17.7l-64 128-16 32c-7.9 15.8-1.5 35 14.3 42.9s35 1.5 42.9-14.3l7.2-14.3 88.4 0 7.2 14.3c7.9 15.8 27.1 22.2 42.9 14.3s22.2-27.1 14.3-42.9l-16-32-64-128C439.2 38.8 428.1 32 416 32zM395.8 176L416 135.6 436.2 176l-40.4 0z" + ], + "arrows-alt": [ + 512, + "M278.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-64 64c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l32 0 0 96-96 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-32 96 0 0 96-32 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l64 64c12.5 12.5 32.8 12.5 45.3 0l64-64c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-32 0 0-96 96 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 32-96 0 0-96 32 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-64-64z" + ], + "backward": [ + 512, + "M204.3 43.1C215.9 32 233 28.9 247.7 35.2S272 56 272 72l0 136.3 172.3-165.1C455.9 32 473 28.9 487.7 35.2S512 56 512 72l0 368c0 16-9.6 30.5-24.3 36.8s-31.8 3.2-43.4-7.9L272 303.7 272 440c0 16-9.6 30.5-24.3 36.8s-31.8 3.2-43.4-7.9l-192-184C4.5 277.3 0 266.9 0 256s4.5-21.3 12.3-28.9l192-184z" + ], + "ban": [ + 512, + "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM159.3 388.7L388.7 159.3c4.6-4.6 11.5-5.9 17.4-3.5c14.5 6 26.4 15.3 35.1 27c3.8 5.2 3.2 12.3-1.2 16.8L210.2 428.4c-4.4 4.4-11.6 5-16.8 1.2c-11.7-8.7-21-20.6-27-35.1c-2.5-5.9-1.1-12.8 3.5-17.4z" + ], + "bars": [ + 512, + "M96 160C96 142.3 110.3 128 128 128L512 128C529.7 128 544 142.3 544 160C544 177.7 529.7 192 512 192L128 192C110.3 192 96 177.7 96 160zM96 320C96 302.3 110.3 288 128 288L512 288C529.7 288 544 302.3 544 320C544 337.7 529.7 352 512 352L128 352C110.3 352 96 337.7 96 320zM544 480C544 497.7 529.7 512 512 512L128 512C110.3 512 96 497.7 96 480C96 462.3 110.3 448 128 448L512 448C529.7 448 544 462.3 544 480z" + ], + "bell": [ + 448, + "M224 0c-17.7 0-32 14.3-32 32l0 19.2C119 66 64 130.6 64 208l0 18.8c0 47-17.3 92.4-48.5 127.6l-7.4 8.3c-8.4 9.4-10.4 22.9-5.3 34.4S19.4 416 32 416l384 0c12.6 0 24-7.4 29.2-18.9s3.1-25-5.3-34.4l-7.4-8.3C401.3 319.2 384 273.9 384 226.8l0-18.8c0-77.4-55-142-128-156.8L256 32c0-17.7-14.3-32-32-32zm45.3 493.3c12-12 18.7-28.3 18.7-45.3l-64 0-64 0c0 17 6.7 33.3 18.7 45.3s28.3 18.7 45.3 18.7s33.3-6.7 45.3-18.7z" + ], + "bell-slash": [ + 640, + "M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7l-90.2-70.7c.2-.4 .4-.9 .6-1.3c5.2-11.5 3.1-25-5.3-34.4l-7.4-8.3C497.3 319.2 480 273.9 480 226.8l0-18.8c0-77.4-55-142-128-156.8L352 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 19.2c-42.6 8.6-79 34.2-102 69.3L38.8 5.1zM406.2 416L160 222.1l0 4.8c0 47-17.3 92.4-48.5 127.6l-7.4 8.3c-8.4 9.4-10.4 22.9-5.3 34.4S115.4 416 128 416l278.2 0zm-40.9 77.3c12-12 18.7-28.3 18.7-45.3l-64 0-64 0c0 17 6.7 33.3 18.7 45.3s28.3 18.7 45.3 18.7s33.3-6.7 45.3-18.7z" + ], + "box": [ + 448, + "M50.7 58.5L0 160l208 0 0-128L93.7 32C75.5 32 58.9 42.3 50.7 58.5zM240 160l208 0L397.3 58.5C389.1 42.3 372.5 32 354.3 32L240 32l0 128zm208 32L0 192 0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-224z" + ], + "broom": [ + 576, + "M566.6 54.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192-34.7-34.7c-4.2-4.2-10-6.6-16-6.6c-12.5 0-22.6 10.1-22.6 22.6l0 29.1L364.3 320l29.1 0c12.5 0 22.6-10.1 22.6-22.6c0-6-2.4-11.8-6.6-16l-34.7-34.7 192-192zM341.1 353.4L222.6 234.9c-42.7-3.7-85.2 11.7-115.8 42.3l-8 8C76.5 307.5 64 337.7 64 369.2c0 6.8 7.1 11.2 13.2 8.2l51.1-25.5c5-2.5 9.5 4.1 5.4 7.9L7.3 473.4C2.7 477.6 0 483.6 0 489.9C0 502.1 9.9 512 22.1 512l173.3 0c38.8 0 75.9-15.4 103.4-42.8c30.6-30.6 45.9-73.1 42.3-115.8z" + ], + "building-circle-check": [ + 576, + "M96 0C60.7 0 32 28.7 32 64l0 384c0 35.3 28.7 64 64 64l180 0c-10.5-14.6-19-30.7-25.1-48l-74.9 0 0-80c0-17.7 14.3-32 32-32l32 0c2 0 4 .2 5.9 .5 6-23.6 16.3-45.4 30.1-64.5l-4 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 4c27.5-19.8 60.3-32.4 96-35.4L416 64c0-35.3-28.7-64-64-64L96 0zm32 112c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM272 96l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM128 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM576 400a144 144 0 1 0 -288 0 144 144 0 1 0 288 0zm-86.6-60.9c7.1 5.2 8.7 15.2 3.5 22.3l-64 88c-2.8 3.8-7 6.2-11.7 6.5s-9.3-1.3-12.6-4.6l-40-40c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l26.8 26.8 53-72.9c5.2-7.1 15.2-8.7 22.4-3.5z" + ], + "building-circle-xmark": [ + 576, + "M96 0C60.7 0 32 28.7 32 64l0 384c0 35.3 28.7 64 64 64l180 0c-10.5-14.6-19-30.7-25.1-48l-74.9 0 0-80c0-17.7 14.3-32 32-32l32 0c2 0 4 .2 5.9 .5 6-23.6 16.3-45.4 30.1-64.5l-4 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 4c27.5-19.8 60.3-32.4 96-35.4L416 64c0-35.3-28.7-64-64-64L96 0zm32 112c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM272 96l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM128 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM432 544a144 144 0 1 0 0-288 144 144 0 1 0 0 288zm22.6-144l36.7 36.7c6.2 6.2 6.2 16.4 0 22.6s-16.4 6.2-22.6 0l-36.7-36.7-36.7 36.7c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l36.7-36.7-36.7-36.7c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l36.7 36.7 36.7-36.7c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6L454.6 400z" + ], + "calendar": [ + 512, + "M120 0c13.3 0 24 10.7 24 24l0 40 160 0 0-40c0-13.3 10.7-24 24-24s24 10.7 24 24l0 40 32 0c35.3 0 64 28.7 64 64l0 288c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l32 0 0-40c0-13.3 10.7-24 24-24zm0 112l-56 0c-8.8 0-16 7.2-16 16l0 48 352 0 0-48c0-8.8-7.2-16-16-16l-264 0zM48 224l0 192c0 8.8 7.2 16 16 16l320 0c8.8 0 16-7.2 16-16l0-192-352 0z" + ], + "camera": [ + 512, + "M149.1 64.8L138.7 96 64 96C28.7 96 0 124.7 0 160L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-256c0-35.3-28.7-64-64-64l-74.7 0-10.4-31.2C356.4 45.2 338.1 32 317.4 32L194.6 32c-20.7 0-39 13.2-45.5 32.8zM256 192a96 96 0 1 1 0 192 96 96 0 1 1 0-192z" + ], + "caret-down": [ + 320, + "M137.4 374.6c12.5 12.5 32.8 12.5 45.3 0l128-128c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8L32 192c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l128 128z" + ], + "chart-pie": [ + 576, + "M304 240l0-223.4c0-9 7-16.6 16-16.6C443.7 0 544 100.3 544 224c0 9-7.6 16-16.6 16L304 240zM32 272C32 150.7 122.1 50.3 239 34.3c9.2-1.3 17 6.1 17 15.4L256 288 412.5 444.5c6.7 6.7 6.2 17.7-1.5 23.1C371.8 495.6 323.8 512 272 512C139.5 512 32 404.6 32 272zm526.4 16c9.3 0 16.6 7.8 15.4 17c-7.7 55.9-34.6 105.6-73.9 142.3c-6 5.6-15.4 5.2-21.2-.7L320 288l238.4 0z" + ], + "check": [ + 448, + "M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" + ], + "check-circle": [ + 512, + "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z" + ], + "check-double": [ + 448, + "M342.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 178.7l-57.4-57.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l80 80c12.5 12.5 32.8 12.5 45.3 0l160-160zm96 128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 402.7 54.6 297.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l256-256z" + ], + "chevron-down": [ + 512, + "M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z" + ], + "chevron-left": [ + 320, + "M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z" + ], + "chevron-right": [ + 320, + "M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" + ], + "circle-notch": [ + 512, + "M222.7 32.1c5 16.9-4.6 34.8-21.5 39.8C121.8 95.6 64 169.1 64 256c0 106 86 192 192 192s192-86 192-192c0-86.9-57.8-160.4-137.1-184.1c-16.9-5-26.6-22.9-21.5-39.8s22.9-26.6 39.8-21.5C434.9 42.1 512 140 512 256c0 141.4-114.6 256-256 256S0 397.4 0 256C0 140 77.1 42.1 182.9 10.6c16.9-5 34.8 4.6 39.8 21.5z" + ], + "clock": [ + 512, + "M256 0a256 256 0 1 1 0 512A256 256 0 1 1 256 0zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z" + ], + "cloud": [ + 640, + "M0 336c0 79.5 64.5 144 144 144l368 0c70.7 0 128-57.3 128-128c0-61.9-44-113.6-102.4-125.4c4.1-10.7 6.4-22.4 6.4-34.6c0-53-43-96-96-96c-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32C167.6 32 96 103.6 96 192c0 2.7 .1 5.4 .2 8.1C40.2 219.8 0 273.2 0 336z" + ], + "cloud-upload-alt": [ + 640, + "M144 480C64.5 480 0 415.5 0 336c0-62.8 40.2-116.2 96.2-135.9c-.1-2.7-.2-5.4-.2-8.1c0-88.4 71.6-160 160-160c59.3 0 111 32.2 138.7 80.2C409.9 102 428.3 96 448 96c53 0 96 43 96 96c0 12.2-2.3 23.8-6.4 34.6C596 238.4 640 290.1 640 352c0 70.7-57.3 128-128 128l-368 0zm79-217c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l39-39L296 392c0 13.3 10.7 24 24 24s24-10.7 24-24l0-134.1 39 39c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-80-80c-9.4-9.4-24.6-9.4-33.9 0l-80 80z" + ], + "cog": [ + 512, + "M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z" + ], + "cogs": [ + 640, + "M308.5 135.3c7.1-6.3 9.9-16.2 6.2-25c-2.3-5.3-4.8-10.5-7.6-15.5L304 89.4c-3-5-6.3-9.9-9.8-14.6c-5.7-7.6-15.7-10.1-24.7-7.1l-28.2 9.3c-10.7-8.8-23-16-36.2-20.9L199 27.1c-1.9-9.3-9.1-16.7-18.5-17.8C173.9 8.4 167.2 8 160.4 8l-.7 0c-6.8 0-13.5 .4-20.1 1.2c-9.4 1.1-16.6 8.6-18.5 17.8L115 56.1c-13.3 5-25.5 12.1-36.2 20.9L50.5 67.8c-9-3-19-.5-24.7 7.1c-3.5 4.7-6.8 9.6-9.9 14.6l-3 5.3c-2.8 5-5.3 10.2-7.6 15.6c-3.7 8.7-.9 18.6 6.2 25l22.2 19.8C32.6 161.9 32 168.9 32 176s.6 14.1 1.7 20.9L11.5 216.7c-7.1 6.3-9.9 16.2-6.2 25c2.3 5.3 4.8 10.5 7.6 15.6l3 5.2c3 5.1 6.3 9.9 9.9 14.6c5.7 7.6 15.7 10.1 24.7 7.1l28.2-9.3c10.7 8.8 23 16 36.2 20.9l6.1 29.1c1.9 9.3 9.1 16.7 18.5 17.8c6.7 .8 13.5 1.2 20.4 1.2s13.7-.4 20.4-1.2c9.4-1.1 16.6-8.6 18.5-17.8l6.1-29.1c13.3-5 25.5-12.1 36.2-20.9l28.2 9.3c9 3 19 .5 24.7-7.1c3.5-4.7 6.8-9.5 9.8-14.6l3.1-5.4c2.8-5 5.3-10.2 7.6-15.5c3.7-8.7 .9-18.6-6.2-25l-22.2-19.8c1.1-6.8 1.7-13.8 1.7-20.9s-.6-14.1-1.7-20.9l22.2-19.8zM112 176a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zM504.7 500.5c6.3 7.1 16.2 9.9 25 6.2c5.3-2.3 10.5-4.8 15.5-7.6l5.4-3.1c5-3 9.9-6.3 14.6-9.8c7.6-5.7 10.1-15.7 7.1-24.7l-9.3-28.2c8.8-10.7 16-23 20.9-36.2l29.1-6.1c9.3-1.9 16.7-9.1 17.8-18.5c.8-6.7 1.2-13.5 1.2-20.4s-.4-13.7-1.2-20.4c-1.1-9.4-8.6-16.6-17.8-18.5L583.9 307c-5-13.3-12.1-25.5-20.9-36.2l9.3-28.2c3-9 .5-19-7.1-24.7c-4.7-3.5-9.6-6.8-14.6-9.9l-5.3-3c-5-2.8-10.2-5.3-15.6-7.6c-8.7-3.7-18.6-.9-25 6.2l-19.8 22.2c-6.8-1.1-13.8-1.7-20.9-1.7s-14.1 .6-20.9 1.7l-19.8-22.2c-6.3-7.1-16.2-9.9-25-6.2c-5.3 2.3-10.5 4.8-15.6 7.6l-5.2 3c-5.1 3-9.9 6.3-14.6 9.9c-7.6 5.7-10.1 15.7-7.1 24.7l9.3 28.2c-8.8 10.7-16 23-20.9 36.2L315.1 313c-9.3 1.9-16.7 9.1-17.8 18.5c-.8 6.7-1.2 13.5-1.2 20.4s.4 13.7 1.2 20.4c1.1 9.4 8.6 16.6 17.8 18.5l29.1 6.1c5 13.3 12.1 25.5 20.9 36.2l-9.3 28.2c-3 9-.5 19 7.1 24.7c4.7 3.5 9.5 6.8 14.6 9.8l5.4 3.1c5 2.8 10.2 5.3 15.5 7.6c8.7 3.7 18.6 .9 25-6.2l19.8-22.2c6.8 1.1 13.8 1.7 20.9 1.7s14.1-.6 20.9-1.7l19.8 22.2zM464 304a48 48 0 1 1 0 96 48 48 0 1 1 0-96z" + ], + "compact-disc": [ + 512, + "M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zM88 256H56c0-105.9 86.1-192 192-192v32c-88.2 0-160 71.8-160 160zm160 96c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96zm0-128c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32z" + ], + "copy": [ + 448, + "M208 0L332.1 0c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9L448 336c0 26.5-21.5 48-48 48l-192 0c-26.5 0-48-21.5-48-48l0-288c0-26.5 21.5-48 48-48zM48 128l80 0 0 64-64 0 0 256 192 0 0-32 64 0 0 48c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 176c0-26.5 21.5-48 48-48z" + ], + "crown": [ + 576, + "M309 106c11.4-7 19-19.7 19-34c0-22.1-17.9-40-40-40s-40 17.9-40 40c0 14.4 7.6 27 19 34L209.7 220.6c-9.1 18.2-32.7 23.4-48.6 10.7L72 160c5-6.7 8-15 8-24c0-22.1-17.9-40-40-40S0 113.9 0 136s17.9 40 40 40c.2 0 .5 0 .7 0L86.4 427.4c5.5 30.4 32 52.6 63 52.6l277.2 0c30.9 0 57.4-22.1 63-52.6L535.3 176c.2 0 .5 0 .7 0c22.1 0 40-17.9 40-40s-17.9-40-40-40s-40 17.9-40 40c0 9 3 17.3 8 24l-89.1 71.3c-15.9 12.7-39.5 7.5-48.6-10.7L309 106z" + ], + "database": [ + 448, + "M448 80l0 48c0 44.2-100.3 80-224 80S0 172.2 0 128L0 80C0 35.8 100.3 0 224 0S448 35.8 448 80zM393.2 214.7c20.8-7.4 39.9-16.9 54.8-28.6L448 288c0 44.2-100.3 80-224 80S0 332.2 0 288L0 186.1c14.9 11.8 34 21.2 54.8 28.6C99.7 230.7 159.5 240 224 240s124.3-9.3 169.2-25.3zM0 346.1c14.9 11.8 34 21.2 54.8 28.6C99.7 390.7 159.5 400 224 400s124.3-9.3 169.2-25.3c20.8-7.4 39.9-16.9 54.8-28.6l0 85.9c0 44.2-100.3 80-224 80S0 476.2 0 432l0-85.9z" + ], + "desktop": [ + 576, + "M64 0C28.7 0 0 28.7 0 64L0 352c0 35.3 28.7 64 64 64l176 0-10.7 32L160 448c-17.7 0-32 14.3-32 32s14.3 32 32 32l256 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-69.3 0L336 416l176 0c35.3 0 64-28.7 64-64l0-288c0-35.3-28.7-64-64-64L64 0zM512 64l0 224L64 288 64 64l448 0z" + ], + "download": [ + 512, + "M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 242.7-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7 288 32zM64 352c-35.3 0-64 28.7-64 64l0 32c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-32c0-35.3-28.7-64-64-64l-101.5 0-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352 64 352zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z" + ], + "ellipsis-v": [ + 128, + "M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z" + ], + "envelope": [ + 512, + "M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48L48 64zM0 176L0 384c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-208L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z" + ], + "exclamation-circle": [ + 512, + "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z" + ], + "exclamation-triangle": [ + 512, + "M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z" + ], + "expand": [ + 448, + "M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z" + ], + "external-link-alt": [ + 512, + "M290.4 19.8C295.4 7.8 307.1 0 320 0L480 0c17.7 0 32 14.3 32 32l0 160c0 12.9-7.8 24.6-19.8 29.6s-25.7 2.2-34.9-6.9L400 157.3 246.6 310.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L354.7 112 297.4 54.6c-9.2-9.2-11.9-22.9-6.9-34.9zM0 176c0-44.2 35.8-80 80-80l80 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-80 0c-8.8 0-16 7.2-16 16l0 256c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16l0-80c0-17.7 14.3-32 32-32s32 14.3 32 32l0 80c0 44.2-35.8 80-80 80L80 512c-44.2 0-80-35.8-80-80L0 176z" + ], + "eye": [ + 576, + "M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z" + ], + "file": [ + 384, + "M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 288c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128z" + ], + "file-alt": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM112 256l160 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-160 0c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64l160 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-160 0c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64l160 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-160 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z" + ], + "file-archive": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM96 48c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16zm0 64c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16zm0 64c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16zm-6.3 71.8c3.7-14 16.4-23.8 30.9-23.8l14.8 0c14.5 0 27.2 9.7 30.9 23.8l23.5 88.2c1.4 5.4 2.1 10.9 2.1 16.4c0 35.2-28.8 63.7-64 63.7s-64-28.5-64-63.7c0-5.5 .7-11.1 2.1-16.4l23.5-88.2zM112 336c-8.8 0-16 7.2-16 16s7.2 16 16 16l32 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-32 0z" + ], + "file-audio": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zm2 226.3c37.1 22.4 62 63.1 62 109.7s-24.9 87.3-62 109.7c-7.6 4.6-17.4 2.1-22-5.4s-2.1-17.4 5.4-22C269.4 401.5 288 370.9 288 336s-18.6-65.5-46.5-82.3c-7.6-4.6-10-14.4-5.4-22s14.4-10 22-5.4zm-91.9 30.9c6 2.5 9.9 8.3 9.9 14.8l0 128c0 6.5-3.9 12.3-9.9 14.8s-12.9 1.1-17.4-3.5L113.4 376 80 376c-8.8 0-16-7.2-16-16l0-48c0-8.8 7.2-16 16-16l33.4 0 35.3-35.3c4.6-4.6 11.5-5.9 17.4-3.5zm51 34.9c6.6-5.9 16.7-5.3 22.6 1.3C249.8 304.6 256 319.6 256 336s-6.2 31.4-16.3 42.7c-5.9 6.6-16 7.1-22.6 1.3s-7.1-16-1.3-22.6c5.1-5.7 8.1-13.1 8.1-21.3s-3.1-15.7-8.1-21.3c-5.9-6.6-5.3-16.7 1.3-22.6z" + ], + "file-code": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM153 289l-31 31 31 31c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0L71 337c-9.4-9.4-9.4-24.6 0-33.9l48-48c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9zM265 255l48 48c9.4 9.4 9.4 24.6 0 33.9l-48 48c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l31-31-31-31c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0z" + ], + "file-excel": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM155.7 250.2L192 302.1l36.3-51.9c7.6-10.9 22.6-13.5 33.4-5.9s13.5 22.6 5.9 33.4L221.3 344l46.4 66.2c7.6 10.9 5 25.8-5.9 33.4s-25.8 5-33.4-5.9L192 385.8l-36.3 51.9c-7.6 10.9-22.6 13.5-33.4 5.9s-13.5-22.6-5.9-33.4L162.7 344l-46.4-66.2c-7.6-10.9-5-25.8 5.9-33.4s25.8-5 33.4 5.9z" + ], + "file-image": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM64 256a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm152 32c5.3 0 10.2 2.6 13.2 6.9l88 128c3.4 4.9 3.7 11.3 1 16.5s-8.2 8.6-14.2 8.6l-88 0-40 0-48 0-48 0c-5.8 0-11.1-3.1-13.9-8.1s-2.8-11.2 .2-16.1l48-80c2.9-4.8 8.1-7.8 13.7-7.8s10.8 2.9 13.7 7.8l12.8 21.4 48.3-70.2c3-4.3 7.9-6.9 13.2-6.9z" + ], + "file-pdf": [ + 512, + "M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 144-208 0c-35.3 0-64 28.7-64 64l0 144-48 0c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM176 352l32 0c30.9 0 56 25.1 56 56s-25.1 56-56 56l-16 0 0 32c0 8.8-7.2 16-16 16s-16-7.2-16-16l0-48 0-80c0-8.8 7.2-16 16-16zm32 80c13.3 0 24-10.7 24-24s-10.7-24-24-24l-16 0 0 48 16 0zm96-80l32 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-32 0c-8.8 0-16-7.2-16-16l0-128c0-8.8 7.2-16 16-16zm32 128c8.8 0 16-7.2 16-16l0-64c0-8.8-7.2-16-16-16l-16 0 0 96 16 0zm80-112c0-8.8 7.2-16 16-16l48 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0 0 32 32 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0 0 48c0 8.8-7.2 16-16 16s-16-7.2-16-16l0-64 0-64z" + ], + "file-powerpoint": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM136 240l68 0c42 0 76 34 76 76s-34 76-76 76l-44 0 0 32c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-56 0-104c0-13.3 10.7-24 24-24zm68 104c15.5 0 28-12.5 28-28s-12.5-28-28-28l-44 0 0 56 44 0z" + ], + "file-video": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM64 288c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32l0 96c0 17.7-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32l0-96zM300.9 397.9L256 368l0-64 44.9-29.9c2-1.3 4.4-2.1 6.8-2.1c6.8 0 12.3 5.5 12.3 12.3l0 103.4c0 6.8-5.5 12.3-12.3 12.3c-2.4 0-4.8-.7-6.8-2.1z" + ], + "file-word": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM111 257.1l26.8 89.2 31.6-90.3c3.4-9.6 12.5-16.1 22.7-16.1s19.3 6.4 22.7 16.1l31.6 90.3L273 257.1c3.8-12.7 17.2-19.9 29.9-16.1s19.9 17.2 16.1 29.9l-48 160c-3 10-12 16.9-22.4 17.1s-19.8-6.2-23.2-16.1L192 336.6l-33.3 95.3c-3.4 9.8-12.8 16.3-23.2 16.1s-19.5-7.1-22.4-17.1l-48-160c-3.8-12.7 3.4-26.1 16.1-29.9s26.1 3.4 29.9 16.1z" + ], + "folder": [ + 512, + "M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" + ], + "folder-open": [ + 576, + "M88.7 223.8L0 375.8 0 96C0 60.7 28.7 32 64 32l117.5 0c17 0 33.3 6.7 45.3 18.7l26.5 26.5c12 12 28.3 18.7 45.3 18.7L416 96c35.3 0 64 28.7 64 64l0 32-336 0c-22.8 0-43.8 12.1-55.3 31.8zm27.6 16.1C122.1 230 132.6 224 144 224l400 0c11.5 0 22 6.1 27.7 16.1s5.7 22.2-.1 32.1l-112 192C453.9 474 443.4 480 432 480L32 480c-11.5 0-22-6.1-27.7-16.1s-5.7-22.2 .1-32.1l112-192z" + ], + "folder-plus": [ + 512, + "M512 416c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l128 0c20.1 0 39.1 9.5 51.2 25.6l19.2 25.6c6 8.1 15.5 12.8 25.6 12.8l160 0c35.3 0 64 28.7 64 64l0 256zM232 376c0 13.3 10.7 24 24 24s24-10.7 24-24l0-64 64 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-64 0 0-64c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 64-64 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l64 0 0 64z" + ], + "forward": [ + 512, + "M371.7 43.1C360.1 32 343 28.9 328.3 35.2S304 56 304 72l0 136.3-172.3-165.1C120.1 32 103 28.9 88.3 35.2S64 56 64 72l0 368c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9L304 303.7 304 440c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9l192-184c7.9-7.5 12.3-18 12.3-28.9s-4.5-21.3-12.3-28.9l-192-184z" + ], + "github": [ + 496, + "M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" + ], + "globe": [ + 512, + "M352 256c0 22.2-1.2 43.6-3.3 64l-185.3 0c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64l185.3 0c2.2 20.4 3.3 41.8 3.3 64zm28.8-64l123.1 0c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64l-123.1 0c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32l-116.7 0c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0l-176.6 0c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0L18.6 160C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192l123.1 0c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64L8.1 320C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6l176.6 0c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352l116.7 0zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6l116.7 0z" + ], + "grip-vertical": [ + 320, + "M128 40c0-22.1-17.9-40-40-40L40 0C17.9 0 0 17.9 0 40L0 88c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zm0 192c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM0 424l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 40c0-22.1-17.9-40-40-40L232 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM192 232l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 424c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48z" + ], + "hdd": [ + 512, + "M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 184.4c-17-15.2-39.4-24.4-64-24.4L64 256c-24.6 0-47 9.2-64 24.4L0 96zM64 288l384 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64zM320 416a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm128-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z" + ], + "home": [ + 640, + "M341.8 72.6C329.5 61.2 310.5 61.2 298.3 72.6L74.3 280.6C64.7 289.6 61.5 303.5 66.3 315.7C71.1 327.9 82.8 336 96 336L112 336L112 512C112 547.3 140.7 576 176 576L464 576C499.3 576 528 547.3 528 512L528 336L544 336C557.2 336 569 327.9 573.8 315.7C578.6 303.5 575.4 289.5 565.8 280.6L341.8 72.6zM304 384L336 384C362.5 384 384 405.5 384 432L384 528L256 528L256 432C256 405.5 277.5 384 304 384z" + ], + "id-card": [ + 576, + "M0 96l576 0c0-35.3-28.7-64-64-64L64 32C28.7 32 0 60.7 0 96zm0 32L0 416c0 35.3 28.7 64 64 64l448 0c35.3 0 64-28.7 64-64l0-288L0 128zM64 405.3c0-29.5 23.9-53.3 53.3-53.3l117.3 0c29.5 0 53.3 23.9 53.3 53.3c0 5.9-4.8 10.7-10.7 10.7L74.7 416c-5.9 0-10.7-4.8-10.7-10.7zM176 192a64 64 0 1 1 0 128 64 64 0 1 1 0-128zm176 16c0-8.8 7.2-16 16-16l128 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-128 0c-8.8 0-16-7.2-16-16zm0 64c0-8.8 7.2-16 16-16l128 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-128 0c-8.8 0-16-7.2-16-16zm0 64c0-8.8 7.2-16 16-16l128 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-128 0c-8.8 0-16-7.2-16-16z" + ], + "images": [ + 576, + "M160 32c-35.3 0-64 28.7-64 64l0 224c0 35.3 28.7 64 64 64l352 0c35.3 0 64-28.7 64-64l0-224c0-35.3-28.7-64-64-64L160 32zM396 138.7l96 144c4.9 7.4 5.4 16.8 1.2 24.6S480.9 320 472 320l-144 0-48 0-80 0c-9.2 0-17.6-5.3-21.6-13.6s-2.9-18.2 2.9-25.4l64-80c4.6-5.7 11.4-9 18.7-9s14.2 3.3 18.7 9l17.3 21.6 56-84C360.5 132 368 128 376 128s15.5 4 20 10.7zM192 128a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zM48 120c0-13.3-10.7-24-24-24S0 106.7 0 120L0 344c0 75.1 60.9 136 136 136l320 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-320 0c-48.6 0-88-39.4-88-88l0-224z" + ], + "infinity": [ + 640, + "M0 256c0-88.4 71.6-160 160-160 50.4 0 97.8 23.7 128 64l32 42.7 32-42.7c30.2-40.3 77.6-64 128-64 88.4 0 160 71.6 160 160S568.4 416 480 416c-50.4 0-97.8-23.7-128-64l-32-42.7-32 42.7c-30.2 40.3-77.6 64-128 64-88.4 0-160-71.6-160-160zm280 0l-43.2-57.6c-18.1-24.2-46.6-38.4-76.8-38.4-53 0-96 43-96 96s43 96 96 96c30.2 0 58.7-14.2 76.8-38.4L280 256zm80 0l43.2 57.6c18.1 24.2 46.6 38.4 76.8 38.4 53 0 96-43 96-96s-43-96-96-96c-30.2 0-58.7 14.2-76.8 38.4L360 256z" + ], + "info-circle": [ + 512, + "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z" + ], + "key": [ + 512, + "M336 352c97.2 0 176-78.8 176-176S433.2 0 336 0S160 78.8 160 176c0 18.7 2.9 36.8 8.3 53.7L7 391c-4.5 4.5-7 10.6-7 17l0 80c0 13.3 10.7 24 24 24l80 0c13.3 0 24-10.7 24-24l0-40 40 0c13.3 0 24-10.7 24-24l0-40 40 0c6.4 0 12.5-2.5 17-7l33.3-33.3c16.9 5.4 35 8.3 53.7 8.3zM376 96a40 40 0 1 1 0 80 40 40 0 1 1 0-80z" + ], + "keyboard": [ + 576, + "M64 64C28.7 64 0 92.7 0 128L0 384c0 35.3 28.7 64 64 64l448 0c35.3 0 64-28.7 64-64l0-256c0-35.3-28.7-64-64-64L64 64zm16 64l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM64 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zm16 80l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zm80-176c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zm16 80l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM160 336c0-8.8 7.2-16 16-16l224 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-224 0c-8.8 0-16-7.2-16-16l0-32zM272 128l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM256 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM368 128l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM352 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM464 128l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zM448 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zm16 80l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16z" + ], + "layer-group": [ + 512, + "M232.5 5.2c14.9-6.9 32.1-6.9 47 0l218.6 101c8.5 3.9 13.9 12.4 13.9 21.8s-5.4 17.9-13.9 21.8l-218.6 101c-14.9 6.9-32.1 6.9-47 0L13.9 149.8C5.4 145.8 0 137.3 0 128s5.4-17.9 13.9-21.8L232.5 5.2zM48.1 218.4l164.3 75.9c27.7 12.8 59.6 12.8 87.3 0l164.3-75.9 34.1 15.8c8.5 3.9 13.9 12.4 13.9 21.8s-5.4 17.9-13.9 21.8l-218.6 101c-14.9 6.9-32.1 6.9-47 0L13.9 277.8C5.4 273.8 0 265.3 0 256s5.4-17.9 13.9-21.8l34.1-15.8zM13.9 362.2l34.1-15.8 164.3 75.9c27.7 12.8 59.6 12.8 87.3 0l164.3-75.9 34.1 15.8c8.5 3.9 13.9 12.4 13.9 21.8s-5.4 17.9-13.9 21.8l-218.6 101c-14.9 6.9-32.1 6.9-47 0L13.9 405.8C5.4 401.8 0 393.3 0 384s5.4-17.9 13.9-21.8z" + ], + "level-up-alt": [ + 384, + "M169.4 9.4c12.5-12.5 32.8-12.5 45.3 0l128 128c9.2 9.2 11.9 22.9 6.9 34.9S332.9 192 320 192l-64 0 0 160c0 88.4-71.6 160-160 160l-64 0c-17.7 0-32-14.3-32-32l0-64c0-17.7 14.3-32 32-32l64 0c17.7 0 32-14.3 32-32l0-160-64 0c-12.9 0-24.6-7.8-29.6-19.8s-2.2-25.7 6.9-34.9l128-128z" + ], + "link": [ + 576, + "M419.5 96c-16.6 0-32.7 4.5-46.8 12.7-15.8-16-34.2-29.4-54.5-39.5 28.2-24 64.1-37.2 101.3-37.2 86.4 0 156.5 70 156.5 156.5 0 41.5-16.5 81.3-45.8 110.6l-71.1 71.1c-29.3 29.3-69.1 45.8-110.6 45.8-86.4 0-156.5-70-156.5-156.5 0-1.5 0-3 .1-4.5 .5-17.7 15.2-31.6 32.9-31.1s31.6 15.2 31.1 32.9c0 .9 0 1.8 0 2.6 0 51.1 41.4 92.5 92.5 92.5 24.5 0 48-9.7 65.4-27.1l71.1-71.1c17.3-17.3 27.1-40.9 27.1-65.4 0-51.1-41.4-92.5-92.5-92.5zM275.2 173.3c-1.9-.8-3.8-1.9-5.5-3.1-12.6-6.5-27-10.2-42.1-10.2-24.5 0-48 9.7-65.4 27.1L91.1 258.2c-17.3 17.3-27.1 40.9-27.1 65.4 0 51.1 41.4 92.5 92.5 92.5 16.5 0 32.6-4.4 46.7-12.6 15.8 16 34.2 29.4 54.6 39.5-28.2 23.9-64 37.2-101.3 37.2-86.4 0-156.5-70-156.5-156.5 0-41.5 16.5-81.3 45.8-110.6l71.1-71.1c29.3-29.3 69.1-45.8 110.6-45.8 86.6 0 156.5 70.6 156.5 156.9 0 1.3 0 2.6 0 3.9-.4 17.7-15.1 31.6-32.8 31.2s-31.6-15.1-31.2-32.8c0-.8 0-1.5 0-2.3 0-33.7-18-63.3-44.8-79.6z" + ], + "list": [ + 512, + "M40 48C26.7 48 16 58.7 16 72l0 48c0 13.3 10.7 24 24 24l48 0c13.3 0 24-10.7 24-24l0-48c0-13.3-10.7-24-24-24L40 48zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L192 64zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zM16 232l0 48c0 13.3 10.7 24 24 24l48 0c13.3 0 24-10.7 24-24l0-48c0-13.3-10.7-24-24-24l-48 0c-13.3 0-24 10.7-24 24zM40 368c-13.3 0-24 10.7-24 24l0 48c0 13.3 10.7 24 24 24l48 0c13.3 0 24-10.7 24-24l0-48c0-13.3-10.7-24-24-24l-48 0z" + ], + "location-crosshairs": [ + 576, + "M288-16c17.7 0 32 14.3 32 32l0 18.3c98.1 14 175.7 91.6 189.7 189.7l18.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-18.3 0c-14 98.1-91.6 175.7-189.7 189.7l0 18.3c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-18.3C157.9 463.7 80.3 386.1 66.3 288L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l18.3 0C80.3 125.9 157.9 48.3 256 34.3L256 16c0-17.7 14.3-32 32-32zM128 256a160 160 0 1 0 320 0 160 160 0 1 0 -320 0zm160-96a96 96 0 1 1 0 192 96 96 0 1 1 0-192z" + ], + "lock": [ + 448, + "M144 144l0 48 160 0 0-48c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192l0-48C80 64.5 144.5 0 224 0s144 64.5 144 144l0 48 16 0c35.3 0 64 28.7 64 64l0 192c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 256c0-35.3 28.7-64 64-64l16 0z" + ], + "moon": [ + 384, + "M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z" + ], + "music": [ + 512, + "M468 7c7.6 6.1 12 15.3 12 25l0 304c0 44.2-43 80-96 80s-96-35.8-96-80 43-80 96-80c11.2 0 22 1.6 32 4.6l0-116.7-224 49.8 0 206.3c0 44.2-43 80-96 80s-96-35.8-96-80 43-80 96-80c11.2 0 22 1.6 32 4.6L128 96c0-15 10.4-28 25.1-31.2l288-64c9.5-2.1 19.4 .2 27 6.3z" + ], + "oxiexport": [ + 576, + "M384.5 24l0 72-64 0c-79.5 0-144 64.5-144 144 0 93.4 82.8 134.8 100.6 142.6 2.2 1 4.6 1.4 7.1 1.4l2.5 0c9.8 0 17.8-8 17.8-17.8 0-8.3-5.9-15.5-12.8-20.3-8.9-6.2-19.2-18.2-19.2-40.5 0-45 36.5-81.5 81.5-81.5l30.5 0 0 72c0 9.7 5.8 18.5 14.8 22.2s19.3 1.7 26.2-5.2l136-136c9.4-9.4 9.4-24.6 0-33.9L425.5 7c-6.9-6.9-17.2-8.9-26.2-5.2S384.5 14.3 384.5 24zm-272 72c-44.2 0-80 35.8-80 80l0 256c0 44.2 35.8 80 80 80l256 0c44.2 0 80-35.8 80-80l0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 32c0 8.8-7.2 16-16 16l-256 0c-8.8 0-16-7.2-16-16l0-256c0-8.8 7.2-16 16-16l16 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-16 0z" + ], + "oxiimport": [ + 576, + "m 360.55,24 v 72 h 64 c 79.5,0 144,64.5 144,144 0,93.4 -82.8,134.8 -100.6,142.6 -2.2,1 -4.6,1.4 -7.1,1.4 h -2.5 c -9.8,0 -17.8,-8 -17.8,-17.8 0,-8.3 5.9,-15.5 12.8,-20.3 8.9,-6.2 19.2,-18.2 19.2,-40.5 0,-45 -36.5,-81.5 -81.5,-81.5 h -30.5 v 72 c 0,9.7 -5.8,18.5 -14.8,22.2 -9,3.7 -19.3,1.7 -26.2,-5.2 l -136,-136 c -9.4,-9.4 -9.4,-24.6 0,-33.9 l 136,-136 c 6.9,-6.9 17.2,-8.9 26.2,-5.2 9,3.7 14.8,12.5 14.8,22.2 z M 112.5,96 c -44.2,0 -80,35.8 -80,80 v 256 c 0,44.2 35.8,80 80,80 h 256 c 44.2,0 80,-35.8 80,-80 v -32 c 0,-17.7 -14.3,-32 -32,-32 -17.7,0 -32,14.3 -32,32 v 32 c 0,8.8 -7.2,16 -16,16 h -256 c -8.8,0 -16,-7.2 -16,-16 V 176 c 0,-8.8 7.2,-16 16,-16 h 16 c 17.7,0 32,-14.3 32,-32 0,-17.7 -14.3,-32 -32,-32 z" + ], + "paper-plane": [ + 576, + "M290.5 287.7L491.4 86.9 359 456.3 290.5 287.7zM457.4 53L256.6 253.8 88 185.3 457.4 53zM38.1 216.8l205.8 83.6 83.6 205.8c5.3 13.1 18.1 21.7 32.3 21.7 14.7 0 27.8-9.2 32.8-23.1L570.6 8c3.5-9.8 1-20.6-6.3-28s-18.2-9.8-28-6.3L39.4 151.7c-13.9 5-23.1 18.1-23.1 32.8 0 14.2 8.6 27 21.7 32.3z" + ], + "pause": [ + 384, + "M48 32C21.5 32 0 53.5 0 80L0 432c0 26.5 21.5 48 48 48l64 0c26.5 0 48-21.5 48-48l0-352c0-26.5-21.5-48-48-48L48 32zm224 0c-26.5 0-48 21.5-48 48l0 352c0 26.5 21.5 48 48 48l64 0c26.5 0 48-21.5 48-48l0-352c0-26.5-21.5-48-48-48l-64 0z" + ], + "pen": [ + 512, + "M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z" + ], + "pencil-alt": [ + 512, + "M36.4 353.2c4.1-14.6 11.8-27.9 22.6-38.7l181.2-181.2 33.9-33.9c16.6 16.6 51.3 51.3 104 104l33.9 33.9-33.9 33.9-181.2 181.2c-10.7 10.7-24.1 18.5-38.7 22.6L30.4 510.6c-8.3 2.3-17.3 0-23.4-6.2S-1.4 489.3 .9 481L36.4 353.2zm55.6-3.7c-4.4 4.7-7.6 10.4-9.3 16.6l-24.1 86.9 86.9-24.1c6.4-1.8 12.2-5.1 17-9.7L91.9 349.5zm354-146.1c-16.6-16.6-51.3-51.3-104-104L308 65.5C334.5 39 349.4 24.1 352.9 20.6 366.4 7 384.8-.6 404-.6S441.6 7 455.1 20.6l35.7 35.7C504.4 69.9 512 88.3 512 107.4s-7.6 37.6-21.2 51.1c-3.5 3.5-18.4 18.4-44.9 44.9z" + ], + "people-roof": [ + 576, + "M302.3-12.6c-9-4.5-19.6-4.5-28.6 0l-256 128C1.9 123.3-4.5 142.5 3.4 158.3s27.1 22.2 42.9 14.3L288 51.8 529.7 172.6c15.8 7.9 35 1.5 42.9-14.3s1.5-35-14.3-42.9l-256-128zM288 272a56 56 0 1 0 0-112 56 56 0 1 0 0 112zm0 48c-53 0-96 43-96 96l0 32c0 17.7 14.3 32 32 32l128 0c17.7 0 32-14.3 32-32l0-32c0-53-43-96-96-96zM160 256a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zm352 0a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM112 336c-44.2 0-80 35.8-80 80l0 33.1c0 17 13.8 30.9 30.9 30.9l87.8 0c-4.3-9.8-6.7-20.6-6.7-32l0-48c0-18.4 3.5-36 9.8-52.2-12.2-7.5-26.5-11.8-41.8-11.8zM425.4 480l87.8 0c17 0 30.9-13.8 30.9-30.9l0-33.1c0-44.2-35.8-80-80-80-15.3 0-29.6 4.3-41.8 11.8 6.3 16.2 9.8 33.8 9.8 52.2l0 48c0 11.4-2.4 22.2-6.7 32z" + ], + "play": [ + 384, + "M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z" + ], + "plus": [ + 448, + "M256 64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 160-160 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l160 0 0 160c0 17.7 14.3 32 32 32s32-14.3 32-32l0-160 160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-160 0 0-160z" + ], + "question-circle": [ + 512, + "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM169.8 165.3c7.9-22.3 29.1-37.3 52.8-37.3l58.3 0c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24l0-13.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1l-58.3 0c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z" + ], + "repeat": [ + 512, + "M470.6 118.6c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S352 19.1 352 32l0 32-160 0C86 64 0 150 0 256 0 273.7 14.3 288 32 288s32-14.3 32-32c0-70.7 57.3-128 128-128l160 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64zM41.4 393.4c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9S160 492.9 160 480l0-32 160 0c106 0 192-86 192-192 0-17.7-14.3-32-32-32s-32 14.3-32 32c0 70.7-57.3 128-128 128l-160 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64z" + ], + "save": [ + 448, + "M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm0 96c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32L96 224c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z" + ], + "search": [ + 512, + "M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z" + ], + "search-minus": [ + 512, + "M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM136 184c-13.3 0-24 10.7-24 24s10.7 24 24 24l144 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-144 0z" + ], + "search-plus": [ + 512, + "M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM184 296c0 13.3 10.7 24 24 24s24-10.7 24-24l0-64 64 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-64 0 0-64c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 64-64 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l64 0 0 64z" + ], + "server": [ + 512, + "M64 32C28.7 32 0 60.7 0 96l0 64c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-64c0-35.3-28.7-64-64-64L64 32zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64l0 64c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-64c0-35.3-28.7-64-64-64L64 288zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z" + ], + "share-alt": [ + 448, + "M352 224c53 0 96-43 96-96s-43-96-96-96s-96 43-96 96c0 4 .2 8 .7 11.9l-94.1 47C145.4 170.2 121.9 160 96 160c-53 0-96 43-96 96s43 96 96 96c25.9 0 49.4-10.2 66.6-26.9l94.1 47c-.5 3.9-.7 7.8-.7 11.9c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-25.9 0-49.4 10.2-66.6 26.9l-94.1-47c.5-3.9 .7-7.8 .7-11.9s-.2-8-.7-11.9l94.1-47C302.6 213.8 326.1 224 352 224z" + ], + "shield-alt": [ + 512, + "M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8l0 378.1C394 378 431.1 230.1 432 141.4L256 66.8s0 0 0 0z" + ], + "shuffle": [ + 512, + "M403.8 34.4c12-5 25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64c-9.2 9.2-22.9 11.9-34.9 6.9S384 204.9 384 192l0-32-32 0c-10.1 0-19.6 4.7-25.6 12.8l-32.4 43.2-40-53.3 21.2-28.3C293.3 110.2 321.8 96 352 96l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6zM154 296l40 53.3-21.2 28.3C154.7 401.8 126.2 416 96 416l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c10.1 0 19.6-4.7 25.6-12.8L154 296zM438.6 470.6c-9.2 9.2-22.9 11.9-34.9 6.9S384 460.9 384 448l0-32-32 0c-30.2 0-58.7-14.2-76.8-38.4L121.6 172.8c-6-8.1-15.5-12.8-25.6-12.8l-64 0c-17.7 0-32-14.3-32-32S14.3 96 32 96l64 0c30.2 0 58.7 14.2 76.8 38.4L326.4 339.2c6 8.1 15.5 12.8 25.6 12.8l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64z" + ], + "sign-in-alt": [ + 512, + "M217.9 105.9L340.7 228.7c7.2 7.2 11.3 17.1 11.3 27.3s-4.1 20.1-11.3 27.3L217.9 406.1c-6.4 6.4-15 9.9-24 9.9c-18.7 0-33.9-15.2-33.9-33.9l0-62.1L32 320c-17.7 0-32-14.3-32-32l0-64c0-17.7 14.3-32 32-32l128 0 0-62.1c0-18.7 15.2-33.9 33.9-33.9c9 0 17.6 3.6 24 9.9zM352 416l64 0c17.7 0 32-14.3 32-32l0-256c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c53 0 96 43 96 96l0 256c0 53-43 96-96 96l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z" + ], + "sign-out-alt": [ + 512, + "M377.9 105.9L500.7 228.7c7.2 7.2 11.3 17.1 11.3 27.3s-4.1 20.1-11.3 27.3L377.9 406.1c-6.4 6.4-15 9.9-24 9.9c-18.7 0-33.9-15.2-33.9-33.9l0-62.1-128 0c-17.7 0-32-14.3-32-32l0-64c0-17.7 14.3-32 32-32l128 0 0-62.1c0-18.7 15.2-33.9 33.9-33.9c9 0 17.6 3.6 24 9.9zM160 96L96 96c-17.7 0-32 14.3-32 32l0 256c0 17.7 14.3 32 32 32l64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-64 0c-53 0-96-43-96-96L0 128C0 75 43 32 96 32l64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32z" + ], + "sliders-h": [ + 512, + "M0 416c0 17.7 14.3 32 32 32l54.7 0c12.3 28.3 40.5 48 73.3 48s61-19.7 73.3-48L480 448c17.7 0 32-14.3 32-32s-14.3-32-32-32l-246.7 0c-12.3-28.3-40.5-48-73.3-48s-61 19.7-73.3 48L32 384c-17.7 0-32 14.3-32 32zm128 0a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zM320 256a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm32-80c-32.8 0-61 19.7-73.3 48L32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l246.7 0c12.3 28.3 40.5 48 73.3 48s61-19.7 73.3-48l54.7 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-54.7 0c-12.3-28.3-40.5-48-73.3-48zM192 128a32 32 0 1 1 0-64 32 32 0 1 1 0 64zm73.3-64C253 35.7 224.8 16 192 16s-61 19.7-73.3 48L32 64C14.3 64 0 78.3 0 96s14.3 32 32 32l86.7 0c12.3 28.3 40.5 48 73.3 48s61-19.7 73.3-48L480 128c17.7 0 32-14.3 32-32s-14.3-32-32-32L265.3 64z" + ], + "spinner": [ + 512, + "M304 48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zm0 416a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM48 304a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm464-48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM142.9 437A48 48 0 1 0 75 369.1 48 48 0 1 0 142.9 437zm0-294.2A48 48 0 1 0 75 75a48 48 0 1 0 67.9 67.9zM369.1 437A48 48 0 1 0 437 369.1 48 48 0 1 0 369.1 437z" + ], + "star": [ + 576, + "M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z" + ], + "star-outline": [ + 576, + "M287.9 0c9.2 0 17.6 5.2 21.6 13.5l68.6 141.3 153.2 22.6c9 1.3 16.5 7.6 19.3 16.3s.5 18.1-5.9 24.5L439.6 319.9l24.6 145.7c1.5 9-2.2 18.1-9.7 23.5s-17.3 6-25.3 1.7l-137-73.2L155.2 490.8c-8 4.3-17.8 3.7-25.3-1.7s-11.2-14.5-9.7-23.5l24.6-145.7L39.6 218.2c-6.4-6.4-8.7-15.9-5.9-24.5s10.3-14.9 19.3-16.3l153.2-22.6L274.3 13.5C278.3 5.2 286.7 0 295.9 0h-8zm0 79L235.4 187.2c-3.5 7.1-10.2 12.1-18.1 13.3L99 218.9l85.8 85.1c5.5 5.5 8.1 13.3 6.8 21L171.3 444.7l111.5-59.5c7-3.7 15.3-3.7 22.3 0l111.5 59.5-20.3-119.7c-1.3-7.7 1.2-15.5 6.8-21l85.8-85.1-118.3-17.4c-7.8-1.2-14.6-6.1-18.1-13.3L287.9 79z" + ], + "sun": [ + 512, + "M375.7 19.7c-1.5-8-6.9-14.7-14.4-17.8s-16.1-2.2-22.8 2.4L256 61 173.5 4.2c-6.7-4.6-15.3-5.5-22.8-2.4s-12.9 9.8-14.4 17.8l-18.1 98.5L19.7 136.3c-8 1.5-14.7 6.9-17.8 14.4s-2.2 16.1 2.4 22.8L61 256 4.2 338.5c-4.6 6.7-5.5 15.3-2.4 22.8s9.8 13 17.8 14.4l98.5 18.1 18.1 98.5c1.5 8 6.9 14.7 14.4 17.8s16.1 2.2 22.8-2.4L256 451l82.5 56.8c6.7 4.6 15.3 5.5 22.8 2.4s12.9-9.8 14.4-17.8l18.1-98.5 98.5-18.1c8-1.5 14.7-6.9 17.8-14.4s2.2-16.1-2.4-22.8L451 256l56.8-82.5c4.6-6.7 5.5-15.3 2.4-22.8s-9.8-12.9-17.8-14.4l-98.5-18.1L375.7 19.7zM269.6 110l65.6-45.2 14.4 78.3c1.8 9.8 9.5 17.5 19.3 19.3l78.3 14.4L402 242.4c-5.7 8.2-5.7 19 0 27.2l45.2 65.6-78.3 14.4c-9.8 1.8-17.5 9.5-19.3 19.3l-14.4 78.3L269.6 402c-8.2-5.7-19-5.7-27.2 0l-65.6 45.2-14.4-78.3c-1.8-9.8-9.5-17.5-19.3-19.3L64.8 335.2 110 269.6c5.7-8.2 5.7-19 0-27.2L64.8 176.8l78.3-14.4c9.8-1.8 17.5-9.5 19.3-19.3l14.4-78.3L242.4 110c8.2 5.7 19 5.7 27.2 0zM256 368a112 112 0 1 0 0-224 112 112 0 1 0 0 224zM192 256a64 64 0 1 1 128 0 64 64 0 1 1 -128 0z" + ], + "terminal": [ + 576, + "M9.4 86.6C-3.1 74.1-3.1 53.9 9.4 41.4s32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L178.7 256 9.4 86.6zM256 416l288 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z" + ], + "th": [ + 512, + "M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32zm88 64l0 64-88 0 0-64 88 0zm56 0l88 0 0 64-88 0 0-64zm240 0l0 64-88 0 0-64 88 0zM64 224l88 0 0 64-88 0 0-64zm232 0l0 64-88 0 0-64 88 0zm64 0l88 0 0 64-88 0 0-64zM152 352l0 64-88 0 0-64 88 0zm56 0l88 0 0 64-88 0 0-64zm240 0l0 64-88 0 0-64 88 0z" + ], + "times": [ + 384, + "M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z" + ], + "times-circle": [ + 512, + "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z" + ], + "trash": [ + 448, + "M135.2 17.7C140.6 6.8 151.7 0 163.8 0L284.2 0c12.1 0 23.2 6.8 28.6 17.7L320 32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 96C14.3 96 0 81.7 0 64S14.3 32 32 32l96 0 7.2-14.3zM32 128l384 0 0 320c0 35.3-28.7 64-64 64L96 512c-35.3 0-64-28.7-64-64l0-320zm96 64c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16z" + ], + "trash-alt": [ + 448, + "M135.2 17.7C140.6 6.8 151.7 0 163.8 0L284.2 0c12.1 0 23.2 6.8 28.6 17.7L320 32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 96C14.3 96 0 81.7 0 64S14.3 32 32 32l96 0 7.2-14.3zM32 128l384 0 0 320c0 35.3-28.7 64-64 64L96 512c-35.3 0-64-28.7-64-64l0-320zm96 64c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16z" + ], + "undo": [ + 512, + "M48.5 224L40 224c-13.3 0-24-10.7-24-24L16 72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8L48.5 224z" + ], + "user": [ + 448, + "M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z" + ], + "user-circle": [ + 512, + "M399 384.2C376.9 345.8 335.4 320 288 320l-64 0c-47.4 0-88.9 25.8-111 64.2c35.2 39.2 86.2 63.8 143 63.8s107.8-24.7 143-63.8zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm256 16a72 72 0 1 0 0-144 72 72 0 1 0 0 144z" + ], + "user-group": [ + 640, + "M96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM0 482.3C0 383.8 79.8 304 178.3 304l91.4 0C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7L29.7 512C13.3 512 0 498.7 0 482.3zM609.3 512l-137.8 0c5.4-9.4 8.6-20.3 8.6-32l0-8c0-60.7-27.1-115.2-69.8-151.8c2.4-.1 4.7-.2 7.1-.2l61.4 0C567.8 320 640 392.2 640 481.3c0 17-13.8 30.7-30.7 30.7zM432 256c-31 0-59-12.6-79.3-32.9C372.4 196.5 384 163.6 384 128c0-26.8-6.6-52.1-18.3-74.3C384.3 40.1 407.2 32 432 32c61.9 0 112 50.1 112 112s-50.1 112-112 112z" + ], + "user-plus": [ + 640, + "M96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM0 482.3C0 383.8 79.8 304 178.3 304l91.4 0C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7L29.7 512C13.3 512 0 498.7 0 482.3zM504 312l0-64-64 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l64 0 0-64c0-13.3 10.7-24 24-24s24 10.7 24 24l0 64 64 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-64 0 0 64c0 13.3-10.7 24-24 24s-24-10.7-24-24z" + ], + "user-xmark": [ + 576, + "M254.1 304c98.5 0 178.3 79.8 178.3 178.3 0 16.4-13.3 29.7-29.7 29.7L46.1 512c-16.4 0-29.7-13.3-29.7-29.7 0-98.5 79.8-178.3 178.3-178.3l59.4 0zM530.3 108.1c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-33.9 33.9 33.9 33.9c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-33.9-33.9-33.9 33.9c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l33.9-33.9-33.9-33.9c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l33.9 33.9 33.9-33.9zM224.4 248a120 120 0 1 1 0-240 120 120 0 1 1 0 240z" + ], + "users": [ + 640, + "M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192l42.7 0c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0L21.3 320C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7l42.7 0C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3l-213.3 0zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352l117.3 0C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7l-330.7 0c-14.7 0-26.7-11.9-26.7-26.7z" + ], + "users-cog": [ + 640, + "M144 160A80 80 0 1 0 144 0a80 80 0 1 0 0 160zm368 0A80 80 0 1 0 512 0a80 80 0 1 0 0 160zM0 298.7C0 310.4 9.6 320 21.3 320l213.3 0c.2 0 .4 0 .7 0c-26.6-23.5-43.3-57.8-43.3-96c0-7.6 .7-15 1.9-22.3c-13.6-6.3-28.7-9.7-44.6-9.7l-42.7 0C47.8 192 0 239.8 0 298.7zM320 320c24 0 45.9-8.8 62.7-23.3c2.5-3.7 5.2-7.3 8-10.7c2.7-3.3 5.7-6.1 9-8.3C410 262.3 416 243.9 416 224c0-53-43-96-96-96s-96 43-96 96s43 96 96 96zm65.4 60.2c-10.3-5.9-18.1-16.2-20.8-28.2l-103.2 0C187.7 352 128 411.7 128 485.3c0 14.7 11.9 26.7 26.7 26.7l300.6 0c-2.1-5.2-3.2-10.9-3.2-16.4l0-3c-1.3-.7-2.7-1.5-4-2.3l-2.6 1.5c-16.8 9.7-40.5 8-54.7-9.7c-4.5-5.6-8.6-11.5-12.4-17.6l-.1-.2-.1-.2-2.4-4.1-.1-.2-.1-.2c-3.4-6.2-6.4-12.6-9-19.3c-8.2-21.2 2.2-42.6 19-52.3l2.7-1.5c0-.8 0-1.5 0-2.3s0-1.5 0-2.3l-2.7-1.5zM533.3 192l-42.7 0c-15.9 0-31 3.5-44.6 9.7c1.3 7.2 1.9 14.7 1.9 22.3c0 17.4-3.5 33.9-9.7 49c2.5 .9 4.9 2 7.1 3.3l2.6 1.5c1.3-.8 2.6-1.6 4-2.3l0-3c0-19.4 13.3-39.1 35.8-42.6c7.9-1.2 16-1.9 24.2-1.9s16.3 .6 24.2 1.9c22.5 3.5 35.8 23.2 35.8 42.6l0 3c1.3 .7 2.7 1.5 4 2.3l2.6-1.5c16.8-9.7 40.5-8 54.7 9.7c2.3 2.8 4.5 5.8 6.6 8.7c-2.1-57.1-49-102.7-106.6-102.7zm91.3 163.9c6.3-3.6 9.5-11.1 6.8-18c-2.1-5.5-4.6-10.8-7.4-15.9l-2.3-4c-3.1-5.1-6.5-9.9-10.2-14.5c-4.6-5.7-12.7-6.7-19-3l-2.9 1.7c-9.2 5.3-20.4 4-29.6-1.3s-16.1-14.5-16.1-25.1l0-3.4c0-7.3-4.9-13.8-12.1-14.9c-6.5-1-13.1-1.5-19.9-1.5s-13.4 .5-19.9 1.5c-7.2 1.1-12.1 7.6-12.1 14.9l0 3.4c0 10.6-6.9 19.8-16.1 25.1s-20.4 6.6-29.6 1.3l-2.9-1.7c-6.3-3.6-14.4-2.6-19 3c-3.7 4.6-7.1 9.5-10.2 14.6l-2.3 3.9c-2.8 5.1-5.3 10.4-7.4 15.9c-2.6 6.8 .5 14.3 6.8 17.9l2.9 1.7c9.2 5.3 13.7 15.8 13.7 26.4s-4.5 21.1-13.7 26.4l-3 1.7c-6.3 3.6-9.5 11.1-6.8 17.9c2.1 5.5 4.6 10.7 7.4 15.8l2.4 4.1c3 5.1 6.4 9.9 10.1 14.5c4.6 5.7 12.7 6.7 19 3l2.9-1.7c9.2-5.3 20.4-4 29.6 1.3s16.1 14.5 16.1 25.1l0 3.4c0 7.3 4.9 13.8 12.1 14.9c6.5 1 13.1 1.5 19.9 1.5s13.4-.5 19.9-1.5c7.2-1.1 12.1-7.6 12.1-14.9l0-3.4c0-10.6 6.9-19.8 16.1-25.1s20.4-6.6 29.6-1.3l2.9 1.7c6.3 3.6 14.4 2.6 19-3c3.7-4.6 7.1-9.4 10.1-14.5l2.4-4.2c2.8-5.1 5.3-10.3 7.4-15.8c2.6-6.8-.5-14.3-6.8-17.9l-3-1.7c-9.2-5.3-13.7-15.8-13.7-26.4s4.5-21.1 13.7-26.4l3-1.7zM472 384a40 40 0 1 1 80 0 40 40 0 1 1 -80 0z" + ], + "vial": [ + 512, + "M342.6 9.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l9.4 9.4L28.1 342.6C10.1 360.6 0 385 0 410.5L0 416c0 53 43 96 96 96l5.5 0c25.5 0 49.9-10.1 67.9-28.1L448 205.3l9.4 9.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-32-32-96-96-32-32zM205.3 256L352 109.3 402.7 160l-96 96-101.5 0z" + ], + "volume": [ + 512, + "M48 352l48 0 134.1 119.2c6.4 5.7 14.6 8.8 23.1 8.8 19.2 0 34.8-15.6 34.8-34.8l0-378.4c0-19.2-15.6-34.8-34.8-34.8-8.5 0-16.7 3.1-23.1 8.8L96 160 48 160c-26.5 0-48 21.5-48 48l0 96c0 26.5 21.5 48 48 48zM441.1 107c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C443.3 170.7 464 210.9 464 256s-20.7 85.3-53.2 111.8c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5c43.2-35.2 70.9-88.9 70.9-149s-27.7-113.8-70.9-149zm-60.5 74.5c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C361.1 227.6 368 241 368 256s-6.9 28.4-17.7 37.3c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5C402.1 312.9 416 286.1 416 256s-13.9-56.9-35.5-74.5z" + ], + "volume-up": [ + 640, + "M533.6 32.5c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C557.5 113.8 592 180.8 592 256s-34.5 142.2-88.7 186.3c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5C598.5 426.7 640 346.2 640 256S598.5 85.2 533.6 32.5zM473.1 107c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C475.3 170.7 496 210.9 496 256s-20.7 85.3-53.2 111.8c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5c43.2-35.2 70.9-88.9 70.9-149s-27.7-113.8-70.9-149zm-60.5 74.5c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C393.1 227.6 400 241 400 256s-6.9 28.4-17.7 37.3c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5C434.1 312.9 448 286.1 448 256s-13.9-56.9-35.4-74.5zM80 352l48 0 134.1 119.2c6.4 5.7 14.6 8.8 23.1 8.8 19.2 0 34.8-15.6 34.8-34.8l0-378.4c0-19.2-15.6-34.8-34.8-34.8-8.5 0-16.7 3.1-23.1 8.8L128 160 80 160c-26.5 0-48 21.5-48 48l0 96c0 26.5 21.5 48 48 48z" + ], + "world": [ + 512, + "M351.9 280l-190.9 0c2.9 64.5 17.2 123.9 37.5 167.4 11.4 24.5 23.7 41.8 35.1 52.4 11.2 10.5 18.9 12.2 22.9 12.2s11.7-1.7 22.9-12.2c11.4-10.6 23.7-28 35.1-52.4 20.3-43.5 34.6-102.9 37.5-167.4zM160.9 232l190.9 0C349 167.5 334.7 108.1 314.4 64.6 303 40.2 290.7 22.8 279.3 12.2 268.1 1.7 260.4 0 256.4 0s-11.7 1.7-22.9 12.2c-11.4 10.6-23.7 28-35.1 52.4-20.3 43.5-34.6 102.9-37.5 167.4zm-48 0C116.4 146.4 138.5 66.9 170.8 14.7 78.7 47.3 10.9 131.2 1.5 232l111.4 0zM1.5 280c9.4 100.8 77.2 184.7 169.3 217.3-32.3-52.2-54.4-131.7-57.9-217.3L1.5 280zm398.4 0c-3.5 85.6-25.6 165.1-57.9 217.3 92.1-32.7 159.9-116.5 169.3-217.3l-111.4 0zm111.4-48C501.9 131.2 434.1 47.3 342 14.7 374.3 66.9 396.4 146.4 399.9 232l111.4 0z" + ], + "exchange-alt": [ + 512, + "M502.6 150.6l-96 96c-9.2 9.2-22.9 11.9-34.9 6.9S352 236.9 352 224l0-64-320 0c-17.7 0-32-14.3-32-32S14.3 96 32 96l320 0 0-64c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l96 96c12.5 12.5 12.5 32.8 0 45.3zm-397.3 352l-96-96c-12.5-12.5-12.5-32.8 0-45.3l96-96c9.2-9.2 22.9-11.9 34.9-6.9S160 275.1 160 288l0 64 320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-320 0 0 64c0 12.9-7.8 24.6-19.8 29.6s-25.7 2.2-34.9-6.9z" + ], + "flag-checkered": [ + 448, + "M32 0C49.7 0 64 14.3 64 32l0 16 69-17.2c38.1-9.5 78.3-5.1 113.5 12.5 46.3 23.2 100.8 23.2 147.1 0l9.6-4.8C423.8 28.1 448 43.1 448 66.1l0 279.7c0 13.3-8.3 25.3-20.8 30l-34.7 13c-46.2 17.3-97.6 14.6-141.7-7.4-37.9-19-81.4-23.7-122.5-13.4L64 384 64 480c0 17.7-14.3 32-32 32S0 497.7 0 480L0 32C0 14.3 14.3 0 32 0zM64 187.1l64-13.9 0 65.5-64 13.9 0 65.5 48.8-12.2c5.1-1.3 10.1-2.4 15.2-3.3l0-63.9 38.9-8.4c8.3-1.8 16.7-2.5 25.1-2.1l0-64c13.6 .4 27.2 2.6 40.4 6.4l23.6 6.9 0 66.7-41.7-12.3c-7.3-2.1-14.8-3.4-22.3-3.8l0 71.4c21.8 1.9 43.3 6.7 64 14.4l0-69.8 22.7 6.7c13.5 4 27.3 6.4 41.3 7.4l0-64.2c-7.8-.8-15.6-2.3-23.2-4.5l-40.8-12 0-62c-13-3.8-25.8-8.8-38.2-15-8.2-4.1-16.9-7-25.8-8.8l0 72.4c-13-.4-26 .8-38.7 3.6l-25.3 5.5 0-75.2-64 16 0 73.1zM320 335.7c16.8 1.5 33.9-.7 50-6.8l14-5.2 0-71.7-7.9 1.8c-18.4 4.3-37.3 5.7-56.1 4.5l0 77.4zm64-149.4l0-70.8c-20.9 6.1-42.4 9.1-64 9.1l0 69.4c13.9 1.4 28 .5 41.7-2.6l22.3-5.2z" + ], + "id-badge": [ + 384, + "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-384c0-35.3-28.7-64-64-64L64 0zm96 352l64 0c44.2 0 80 35.8 80 80 0 8.8-7.2 16-16 16L96 448c-8.8 0-16-7.2-16-16 0-44.2 35.8-80 80-80zm-24-96a56 56 0 1 1 112 0 56 56 0 1 1 -112 0zM152 64l80 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24z" + ], + "lock-open": [ + 576, + "M384 96c0-35.3 28.7-64 64-64s64 28.7 64 64l0 32c0 17.7 14.3 32 32 32s32-14.3 32-32l0-32c0-70.7-57.3-128-128-128S320 25.3 320 96l0 64-160 0c-35.3 0-64 28.7-64 64l0 224c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-224c0-35.3-28.7-64-64-64l-32 0 0-64z" + ], + "adjust": [ + 512, + "M448 256c0-106-86-192-192-192l0 384c106 0 192-86 192-192zM0 256a256 256 0 1 1 512 0 256 256 0 1 1 -512 0z" + ] +}; + +export type IconName = keyof typeof OxiIcons; diff --git a/frontend/src/lib/stores/files.svelte.ts b/frontend/src/lib/stores/files.svelte.ts new file mode 100644 index 00000000..e539da98 --- /dev/null +++ b/frontend/src/lib/stores/files.svelte.ts @@ -0,0 +1,53 @@ +/** + * Files view state — replaces the navigation-related fields of the legacy `app` + * state object (currentFolder, currentFolderInfo, breadcrumbPath, view mode, + * section, selection). Dialog/context-menu targets stay component-local until a + * view proves they must be shared. + */ +import type { FolderItem } from '$lib/api/types'; + +export type ViewMode = 'grid' | 'list'; +export type Section = + | 'files' + | 'shared' + | 'shared-with-me' + | 'recent' + | 'favorites' + | 'trash' + | 'photos' + | 'music'; + +const VIEW_KEY = 'oxicloud_view_mode'; + +function readViewMode(): ViewMode { + if (typeof localStorage === 'undefined') return 'grid'; + return localStorage.getItem(VIEW_KEY) === 'list' ? 'list' : 'grid'; +} + +class FilesStore { + currentFolder = $state(null); + currentFolderInfo = $state(null); + breadcrumbPath = $state>([]); + viewMode = $state(readViewMode()); + section = $state
    ('files'); + isSearchMode = $state(false); + selection = $state>(new Set()); + + setViewMode(mode: ViewMode): void { + this.viewMode = mode; + if (typeof localStorage !== 'undefined') localStorage.setItem(VIEW_KEY, mode); + } + + clearSelection(): void { + this.selection = new Set(); + } + + toggleSelected(id: string): void { + const next = new Set(this.selection); + if (next.has(id)) next.delete(id); + else next.add(id); + this.selection = next; + } +} + +export const files = new FilesStore(); diff --git a/frontend/src/lib/stores/session.svelte.ts b/frontend/src/lib/stores/session.svelte.ts new file mode 100644 index 00000000..0be554aa --- /dev/null +++ b/frontend/src/lib/stores/session.svelte.ts @@ -0,0 +1,69 @@ +/** + * Session store — the authenticated user and derived flags. + * + * Replaces the user-related fields of the legacy `app` state object + * (isExternalUser, userHomeFolderId/Name). `isExternalUser` drives default + * routing: externals (magic-link / OIDC-only / OCM recipients) have no home + * folder and land on the shared-with-me view. + */ +import { fetchMe, tryRefresh } from '$lib/api/endpoints/auth'; +import { listRootFolders } from '$lib/api/endpoints/folders'; +import type { User } from '$lib/api/types'; + +class SessionStore { + user = $state(null); + loaded = $state(false); + homeFolderId = $state(null); + homeFolderName = $state(null); + + isExternalUser = $derived(this.user?.is_external ?? false); + isAuthenticated = $derived(this.user !== null); + + /** + * Resolve the session once. Probes /api/auth/me; on 401 it makes a single + * refresh attempt and re-probes. Never redirects — the layout guard decides + * what to do with an unauthenticated result. Idempotent: subsequent calls + * return the cached result (so client-side navigation doesn't re-probe). + */ + async load(): Promise { + if (this.loaded) return this.user; + try { + let me = await fetchMe(); + if (!me && (await tryRefresh())) { + me = await fetchMe(); + } + this.user = me; + } catch { + this.user = null; + } + this.loaded = true; + return this.user; + } + + /** + * Resolve the home folder (first entry of GET /api/folders). Externals + * (grant-only) have no home folder, so this is skipped for them. + */ + async loadHomeFolder(): Promise { + if (this.homeFolderId) return this.homeFolderId; + if (this.isExternalUser) return null; + try { + const folders = await listRootFolders(); + if (folders.length > 0) { + this.homeFolderId = folders[0].id; + this.homeFolderName = folders[0].name; + } + } catch { + /* leave null — caller handles */ + } + return this.homeFolderId; + } + + reset(): void { + this.user = null; + this.homeFolderId = null; + this.homeFolderName = null; + } +} + +export const session = new SessionStore(); diff --git a/frontend/src/lib/stores/theme.svelte.ts b/frontend/src/lib/stores/theme.svelte.ts new file mode 100644 index 00000000..93af4c8e --- /dev/null +++ b/frontend/src/lib/stores/theme.svelte.ts @@ -0,0 +1,43 @@ +/** + * Theme store — light / dark / auto. + * + * Mirrors the legacy behaviour: persists to the `oxicloud_theme` localStorage + * key and reflects the choice on ``. `auto` removes the + * attribute so the OS `prefers-color-scheme` takes over. The anti-FOUC inline + * script in app.html applies the stored value before first paint; this store + * owns runtime changes from the UI. + */ +export type Theme = 'light' | 'dark' | 'auto'; + +const STORAGE_KEY = 'oxicloud_theme'; + +function readInitial(): Theme { + if (typeof localStorage === 'undefined') return 'auto'; + const v = localStorage.getItem(STORAGE_KEY); + return v === 'light' || v === 'dark' ? v : 'auto'; +} + +const store = $state<{ theme: Theme }>({ theme: readInitial() }); + +function apply(theme: Theme): void { + if (typeof document === 'undefined') return; + const html = document.documentElement; + if (theme === 'light' || theme === 'dark') html.setAttribute('data-color-scheme', theme); + else html.removeAttribute('data-color-scheme'); +} + +export function setTheme(theme: Theme): void { + store.theme = theme; + if (typeof localStorage !== 'undefined') { + if (theme === 'auto') localStorage.removeItem(STORAGE_KEY); + else localStorage.setItem(STORAGE_KEY, theme); + } + apply(theme); +} + +export const theme = { + get current() { + return store.theme; + }, + set: setTheme +}; diff --git a/frontend/src/lib/stores/ui.svelte.ts b/frontend/src/lib/stores/ui.svelte.ts new file mode 100644 index 00000000..f20c3b19 --- /dev/null +++ b/frontend/src/lib/stores/ui.svelte.ts @@ -0,0 +1,32 @@ +/** + * Transient UI state — toasts now; cross-component dialog targets are added as + * the views that need them land (Phases 2–4). Component-local state is preferred; + * only state that must cross component boundaries belongs here. + */ +export type ToastKind = 'info' | 'success' | 'error' | 'warning'; + +export interface Toast { + id: number; + message: string; + kind: ToastKind; +} + +class UiStore { + toasts = $state([]); + #seq = 0; + + notify(message: string, kind: ToastKind = 'info', timeoutMs = 4000): number { + const id = ++this.#seq; + this.toasts = [...this.toasts, { id, message, kind }]; + if (timeoutMs > 0 && typeof setTimeout !== 'undefined') { + setTimeout(() => this.dismiss(id), timeoutMs); + } + return id; + } + + dismiss(id: number): void { + this.toasts = this.toasts.filter((t) => t.id !== id); + } +} + +export const ui = new UiStore(); diff --git a/frontend/src/lib/styles/app.css b/frontend/src/lib/styles/app.css new file mode 100644 index 00000000..ccdc6a98 --- /dev/null +++ b/frontend/src/lib/styles/app.css @@ -0,0 +1,10 @@ +/* Global stylesheet: design tokens + base layer, imported once in +layout.svelte. + * Ported from static/css/base/*. Component-specific styles live in each + * component's scoped \3c style> block. */ +@import url('./base/variables.css'); +@import url('./base/reset.css'); +@import url('./base/typography.css'); +@import url('./base/forms.css'); +@import url('./base/animations.css'); +@import url('./base/a11y.css'); +@import url('./legacy.css'); diff --git a/frontend/src/lib/styles/base/a11y.css b/frontend/src/lib/styles/base/a11y.css new file mode 100644 index 00000000..534973d2 --- /dev/null +++ b/frontend/src/lib/styles/base/a11y.css @@ -0,0 +1,146 @@ +/* ============================================================ + * Accessibility baseline — keyboard focus. + * + * Pointer / programmatic focus stays ring-free (no "ring on every + * click" noise); KEYBOARD focus (:focus-visible) always gets a clear + * accent ring. Every interactive element inherits this automatically, + * so components only need their own :focus-visible rule when they want + * a custom ring — and must never strip it for keyboard users. + * + * The outline follows the element's border-radius in modern browsers, + * so rounded controls get a rounded ring for free. + * ============================================================ */ + +:focus:not(:focus-visible) { + outline: none; +} + +:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 2px; +} + +/* Skip link — visually hidden until focused, then slides in at top-left. + Lets keyboard users jump straight to
    . */ +.skip-link { + position: absolute; + top: var(--space-2); + left: var(--space-2); + z-index: var(--z-max); + padding: var(--space-2) var(--space-4); + background: var(--color-bg-surface); + color: var(--color-text); + border-radius: var(--radius-md); + box-shadow: var(--shadow-lg); + transform: translateY(-150%); + transition: transform var(--motion-fast) var(--ease-standard); +} + +.skip-link:focus { + transform: translateY(0); +} + +/* ── prefers-reduced-motion ────────────────────────────────── + Vestibular safety: near-instant transitions/animations and no + smooth scroll for users who ask the OS to reduce motion. */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* ── prefers-contrast: more ────────────────────────────────── + Collapse the muted text tiers up to the stronger secondary tier and + thicken the focus ring. The high-contrast border ramp (raw colour) lives + in base/variables.css — the token layer — so this file stays hex-free. */ +@media (prefers-contrast: more) { + :root { + --color-text-muted: var(--color-text-secondary); + --color-text-subtle: var(--color-text-secondary); + --color-text-faint: var(--color-text-secondary); + } + + :focus-visible { + outline-width: 3px; + } +} + +/* ── forced-colors (Windows High Contrast) ─────────────────── + Custom colors are overridden by the OS; ensure the keyboard + focus ring uses a real system colour. */ +@media (forced-colors: active) { + :focus-visible { + outline-color: Highlight; + } +} + +/* ── Global error-boundary toast (js/core/errorBoundary.js) ─── */ +.error-toast { + position: fixed; + bottom: var(--space-5); + left: 50%; + z-index: var(--z-toast); + max-width: min(90vw, 420px); + padding: var(--space-3) var(--space-4); + background: var(--color-error-bg); + color: var(--color-error-text); + border: 1px solid var(--color-badge-error-border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + font-size: var(--text-sm); + transform: translate(-50%, calc(100% + var(--space-5))); + transition: transform var(--motion-base) var(--ease-standard); +} + +.error-toast.is-visible { + transform: translate(-50%, 0); +} + +/* ── Print: drop the app chrome, show clean content ────────── */ +@media print { + .sidebar, + .sidebar-overlay, + .top-bar, + .actions-bar, + .page-sticky-header, + .cmdk-overlay, + .skip-link, + .error-toast { + display: none !important; + } + + .content-area, + .main-content { + overflow: visible !important; + } + + * { + box-shadow: none !important; + } +} + +/* ── Touch targets ─────────────────────────────────────────── + ≥44px hit areas for the key controls on touch/phone widths. */ +@media (max-width: 768px) { + .sidebar-toggle, + .search-toggle-btn, + .search-back-btn, + .notif-bell-btn, + .user-avatar-btn { + min-width: 44px; + min-height: 44px; + } + + .nav-item { + min-height: 44px; + } + + .files-list-view .file-item { + min-height: 48px; + } +} diff --git a/frontend/src/lib/styles/base/animations.css b/frontend/src/lib/styles/base/animations.css new file mode 100644 index 00000000..34b144ea --- /dev/null +++ b/frontend/src/lib/styles/base/animations.css @@ -0,0 +1,18 @@ +/* ============================================================ + * Canonical keyframes — single source for shared animations. + * + * Loaded early via main.css so every component and view reuses + * these by name instead of redefining them. This file replaced + * 6 duplicate `@keyframes spin` definitions (spinner / admin / + * music / photos / profile / share-public). + * + * NOTE: `oxi-spin` (icons.css) and `smdSpin` (shareModal.css) are + * still defined locally — folding them in needs touching their + * `animation-name` consumers, deferred to Fase 1. + * ============================================================ */ + +@keyframes spin { + to { + transform: rotate(360deg); + } +} diff --git a/frontend/src/lib/styles/base/forms.css b/frontend/src/lib/styles/base/forms.css new file mode 100644 index 00000000..319d0987 --- /dev/null +++ b/frontend/src/lib/styles/base/forms.css @@ -0,0 +1,60 @@ +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: var(--space-2); + font-weight: var(--weight-medium); + color: var(--color-text-secondary); +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: var(--space-2-5); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + font-size: var(--text-base); +} + +.form-group textarea { + resize: vertical; + min-height: 80px; +} + +.button { + padding: var(--space-2) var(--space-4); + border: none; + border-radius: var(--radius-md); + cursor: pointer; + font-size: var(--text-base); + transition: background-color 0.2s; +} + +.primary { + background-color: var(--color-accent); + color: var(--color-danger-text); +} + +.primary:hover { + background-color: var(--color-accent-hover); +} + +.secondary { + background-color: var(--color-border); + color: var(--color-text-secondary); +} + +.secondary:hover { + background-color: var(--color-border-medium); +} + +.danger { + background-color: var(--color-danger-bg); + color: var(--color-danger-text); +} + +.danger:hover { + background-color: var(--color-danger-bg-hover); +} diff --git a/frontend/src/lib/styles/base/reset.css b/frontend/src/lib/styles/base/reset.css new file mode 100644 index 00000000..14a0d617 --- /dev/null +++ b/frontend/src/lib/styles/base/reset.css @@ -0,0 +1,58 @@ +/* Honor the user's browser font-size / zoom preference (rem-relative). */ +html { + font-size: 100%; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: var(--font-sans); +} + +body { + display: flex; + height: 100vh; /* fallback for browsers without dvh */ + /* biome-ignore lint/suspicious/noDuplicateProperties: explicit fallback */ + height: 100dvh; + font-size: var(--text-base); + line-height: var(--leading-normal); + background-color: var(--color-bg-page); + overflow: hidden; +} + +html[dir="rtl"] .fa-arrow-left::before { + content: "\f061"; +} + +html[dir="rtl"] .fa-sign-out-alt { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); +} +/* Utility: hide elements without inline style="" (CSP-safe) */ +.hidden { + display: none !important; +} + +/* Brand-tinted text selection + caret. */ +::selection { + background: var(--color-accent-ring-strong); + color: var(--color-text-heading); +} + +:root { + caret-color: var(--color-accent); +} + +/* Visually hidden but exposed to assistive tech (a11y-only labels/headings). */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} diff --git a/frontend/src/lib/styles/base/typography.css b/frontend/src/lib/styles/base/typography.css new file mode 100644 index 00000000..6314e616 --- /dev/null +++ b/frontend/src/lib/styles/base/typography.css @@ -0,0 +1,82 @@ +/* ============================================================ + * Typography utilities — Fase 0 foundations. + * + * Semantic heading roles DECOUPLED from element level, so any + * h1–h6 can carry the correct visual weight while the document + * keeps a correct, accessible heading order. All values route + * through the type/leading/weight/tracking tokens in variables.css. + * + * Views are migrated onto these classes in Fase 1 (replacing the + * per-page raw font-size/weight headings the audit flagged). + * ============================================================ */ + +/* Page title — one per page (the h1 role). */ +.heading-page { + font-size: var(--text-2xl); + line-height: var(--leading-tight); + font-weight: var(--weight-bold); + letter-spacing: var(--tracking-tight); + color: var(--color-text-heading); +} + +/* Section heading. */ +.heading-section { + font-size: var(--text-xl); + line-height: var(--leading-snug); + font-weight: var(--weight-semibold); + letter-spacing: var(--tracking-tight); + color: var(--color-text-heading); +} + +/* Card / panel heading. */ +.heading-card { + font-size: var(--text-md); + line-height: var(--leading-snug); + font-weight: var(--weight-semibold); + color: var(--color-text-heading); +} + +/* Eyebrow / overline label (uppercase caps with tracking). */ +.heading-eyebrow { + font-size: var(--text-xs); + line-height: var(--leading-normal); + font-weight: var(--weight-semibold); + letter-spacing: var(--tracking-widest); + text-transform: uppercase; + color: var(--color-text-muted); +} + +/* Constrain running text to a comfortable measure (~65ch). */ +.prose { + max-width: var(--measure-prose); +} + +/* Headings wrap with balanced line lengths (no single orphan word). */ +h1, +h2, +h3, +.heading-page, +.heading-section, +.heading-card, +.page-title { + text-wrap: balance; +} + +/* Running prose wraps "pretty" (avoids orphans and short last lines). */ +.prose, +.empty-state p, +.about-description, +.auth-subtitle, +.auth-hint, +.language-subtitle { + text-wrap: pretty; +} + +/* Tabular figures for numeric UI so digits align and don't jitter as they + change (storage readouts, badges, stat counters). */ +.storage-info, +.user-menu-storage-text, +.notif-badge, +.stat-value { + font-variant-numeric: tabular-nums; +} diff --git a/frontend/src/lib/styles/base/variables.css b/frontend/src/lib/styles/base/variables.css new file mode 100644 index 00000000..541f09c6 --- /dev/null +++ b/frontend/src/lib/styles/base/variables.css @@ -0,0 +1,694 @@ +/* + * OxiCloud design tokens. + * + * Single source of truth for light + dark colours. Each token whose value + * differs between modes uses `light-dark(LIGHT, DARK)`, which the browser + * resolves against the page's `color-scheme`. + * + * Mode switching: + * • `` in declares + * both schemes are supported. + * • `:root { color-scheme: light dark }` (default) lets the UA follow the + * OS preference (`prefers-color-scheme`). + * • `html[data-color-scheme="light"]` / `…="dark"` force a specific mode. + * `theme-init.js` sets the attribute from localStorage. + * + * Fallback: browsers that don't support `light-dark()` (Chrome < 123 / + * Safari < 17.5 / Firefox < 120) hit a `@supports not (...)` block in + * `themes/dark.css` that still applies the old `[data-theme="dark"]` overrides. + */ + +:root { + /* Default: follow the OS preference. Overridden by html[data-color-scheme]. */ + color-scheme: light dark; + + /* ════════════════════════════════════════════════════════════════ + * NON-COLOR DESIGN SCALES (Fase 0 — fundamentos) + * + * Single source of truth for spacing, radius, typography, z-index, + * motion, elevation, breakpoints and density. Components are migrated + * onto these in Fase 1; until then raw px still coexist. Do NOT add + * raw px for spacing/radius/font-size in new code — consume a token. + * ════════════════════════════════════════════════════════════════ */ + + /* ── Layout shell ──────────────────────────────────────────── */ + /* Fluid sidebar: tracks viewport but clamped to a sane band. */ + --sidebar-width: clamp(220px, 18vw, 280px); + --sidebar-width-min: 200px; /* resizable rail floor (Fase 1) */ + --sidebar-width-max: 320px; /* resizable rail ceiling (Fase 1) */ + --sidebar-width-collapsed: 72px; /* icon-rail mode (Fase 1) */ + --gutter: var(--space-6); /* shared topbar/content horizontal gutter (drops to 16px on phones) */ + --grid-card-min: 200px; /* min width of a grid card (tightens on phones) */ + + /* ── Spacing — 4px grid ────────────────────────────────────── */ + /* Direct steps are multiples of 4; half-steps (0-5/1-5/2-5/3-5) + * cover the high-frequency 2/6/10/14px raw values found in audit. */ + --space-0: 0; + --space-px: 1px; + --space-0-5: 2px; + --space-1: 4px; + --space-1-5: 6px; + --space-2: 8px; + --space-2-5: 10px; + --space-3: 12px; + --space-3-5: 14px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-7: 28px; + --space-8: 32px; + --space-9: 36px; + --space-10: 40px; + --space-11: 44px; + --space-12: 48px; + --space-14: 56px; + --space-16: 64px; + --space-20: 80px; + --space-24: 96px; + + /* ── Radius ─────────────────────────────────────────────────── */ + --radius-none: 0; + --radius-xs: 2px; + --radius-sm: 4px; + --radius-md: 6px; + --radius-lg: 8px; + --radius-xl: 10px; + --radius-2xl: 12px; + --radius-3xl: 16px; + --radius-4xl: 20px; + --radius-full: 9999px; + /* Semantic default — resolves the legacy `var(--radius, 12px)` fallbacks + * in share-public.css / device-verify.css (token was never defined). */ + --radius: var(--radius-2xl); + + /* ── Typography ────────────────────────────────────────────── */ + /* Font families (single source — reset.css `*` consumes --font-sans). */ + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; + + /* Modular size scale in rem (root = 16px) so it honors user zoom. + * --text-base (14px) is the app body default. */ + --text-2xs: 0.6875rem; /* 11px */ + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.8125rem; /* 13px */ + --text-base: 0.875rem; /* 14px */ + --text-md: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.75rem; /* 28px */ + --text-4xl: 2rem; /* 32px */ + --text-5xl: 2.5rem; /* 40px */ + --text-6xl: 3rem; /* 48px */ + + /* Line-heights (leading) — unitless ratios. */ + --leading-none: 1; + --leading-tight: 1.25; + --leading-snug: 1.375; + --leading-normal: 1.5; + --leading-relaxed: 1.625; + --leading-loose: 1.8; + + /* Font weights — numeric only (no bold/normal keywords). */ + --weight-normal: 400; + --weight-medium: 500; + --weight-semibold: 600; + --weight-bold: 700; + --weight-extrabold: 800; + + /* Letter-spacing (tracking) — em-relative. */ + --tracking-tighter: -0.02em; + --tracking-tight: -0.01em; + --tracking-normal: 0; + --tracking-wide: 0.02em; + --tracking-wider: 0.04em; + --tracking-widest: 0.08em; + + /* Prose measure — comfortable line length for running text. */ + --measure-prose: 65ch; + + /* Icon glyph sizing — separate axis from the text scale. */ + --icon-xs: 12px; + --icon-sm: 14px; + --icon-md: 16px; + --icon-lg: 20px; + --icon-xl: 24px; + + /* ── Z-index — semantic stacking layers ────────────────────── */ + /* Gaps left between layers so new surfaces slot in without renumber. */ + --z-below: -1; + --z-base: 0; + --z-raised: 10; + --z-sticky: 100; + --z-dropdown: 1000; + --z-overlay: 2000; + --z-drawer: 2500; + --z-modal: 3000; + --z-popover: 3500; + --z-toast: 4000; + --z-tooltip: 5000; + --z-notification: 6000; + --z-max: 9999; + + /* ── Motion — durations + easing curves ────────────────────── */ + --motion-instant: 0ms; + --motion-fast: 120ms; + --motion-base: 160ms; + --motion-moderate: 200ms; + --motion-slow: 300ms; + --motion-slower: 500ms; + --motion-spinner: 1s; + --spin-duration: var(--motion-spinner); + /* Decelerate is the default for entrances / positive feedback. */ + --ease-standard: cubic-bezier(0.2, 0, 0, 1); + --ease-emphasized: cubic-bezier(0.3, 0, 0, 1); + --ease-in: cubic-bezier(0.4, 0, 1, 1); + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + + /* ── Elevation — composed box-shadow recipes ───────────────── */ + /* Full recipes (not bare alphas) layered on the --color-shadow-* + * alpha tokens below, so they adapt to light/dark automatically. */ + --shadow-xs: 0 1px 2px var(--color-shadow-xs); + --shadow-sm: 0 1px 3px var(--color-shadow-sm), 0 1px 2px var(--color-shadow-xs); + --shadow-md: 0 4px 6px var(--color-shadow-sm), 0 2px 4px var(--color-shadow-xs); + --shadow-lg: 0 10px 15px var(--color-shadow-md), 0 4px 6px var(--color-shadow-sm); + --shadow-xl: 0 20px 25px var(--color-shadow-md), 0 8px 10px var(--color-shadow-sm); + --shadow-2xl: 0 25px 50px var(--color-shadow-lg); + + /* ── Breakpoints (reference tokens) ────────────────────────── */ + /* NOTE: @media cannot consume custom properties. These are the canonical + * values for JS (matchMedia) and documentation; consuming them in @media + * needs @custom-media via a postcss build step — pending dep approval. */ + --bp-xs: 480px; + --bp-sm: 640px; + --bp-md: 768px; + --bp-lg: 1024px; + --bp-xl: 1280px; + + /* ── Density — comfortable (default) vs compact ────────────── */ + /* Gated by html[data-density="compact"] below. Consumed by list rows + * and controls in Fase 1. */ + --density-row-py: var(--space-3); /* 12px */ + --density-row-px: var(--space-3-5); /* 14px */ + --density-gap: var(--space-3); + --density-control-h: 40px; + + /* Backgrounds */ + --color-bg-page: light-dark(#f5f7fa, #0f172a); + --color-bg-surface: light-dark(#ffffff, #1e293b); + --color-bg-input: light-dark(#f9fafb, #0f172a); + --color-bg-hover: light-dark(#f8fafc, #334155); + --color-bg-muted: light-dark(#f0f3f7, #1a2540); + --color-bg-subtle: light-dark(#f8f9fa, #162032); + --color-bg-alt: light-dark(#f7fafc, #0f172a); + --color-bg-input-alt: light-dark(#edf2f7, #253045); + --color-bg-empty: light-dark(#f0f0f0, #253045); + + /* Borders */ + --color-border: light-dark(#e2e8f0, #334155); + --color-border-light: light-dark(#f1f5f9, #334155); + --color-border-medium: light-dark(#cbd5e0, #475569); + --color-border-faint: light-dark(#e0e6ed, #2a3650); + --color-border-subtle: light-dark(#e0e5e8, #2a3650); + --color-border-xfaint: light-dark(#f0f0f0, #1e293b); + --color-border-ddd: light-dark(#ddd, #334155); + + /* Text — collapsed to a few AA-passing tiers; every legacy name is kept as + * an alias so no consumer breaks (migration to the canonical names → Fase 1). + * Each tier clears WCAG AA 4.5:1 on #fff AND the page bg, light and dark. The + * muted/faint/placeholder tiers used to FAIL (2.3–4.0:1) and are now darkened. */ + --color-text: light-dark(#2d3748, #e2e8f0); /* primary body */ + --color-text-heading: light-dark(#1e293b, #f1f5f9); /* headings */ + --color-text-secondary: light-dark(#475569, #cbd5e1); /* strong secondary */ + /* muted/subtle/faint converge: AA 4.5:1 on the grayish page/muted bgs AND + * on the lighter dark hover bg leaves only a narrow passing window. */ + --color-text-muted: light-dark(#5e6a78, #9fadbe); /* muted */ + --color-text-subtle: light-dark(#5e6a78, #9fadbe); /* subtle */ + --color-text-faint: light-dark(#5e6a78, #9fadbe); /* faintest still-AA */ + /* legacy aliases → one of the tiers above */ + --color-text-dark: var(--color-text-secondary); + --color-text-dim: var(--color-text-secondary); + --color-text-black: var(--color-text); + --color-text-gray: var(--color-text-muted); + --color-text-medium: var(--color-text-muted); + --color-text-faint2: var(--color-text-faint); + --color-text-light: var(--color-text-faint); + --color-text-placeholder: var(--color-text-faint); + + /* Accent (orange) — mostly mode-agnostic. */ + --color-accent: #ff5e3a; + --color-accent-hover: light-dark(#e04520, #ff7a5c); + /* AA-compliant accent for TEXT/LINKS: bare #ff5e3a only reaches 3.04:1 on + * white. Link/toggle-link consumers migrate onto this in Fase 2. */ + --color-accent-text: light-dark(#cc3a16, #ff8a5c); + /* Solid foreground on accent fills (replaces reusing --color-danger-text). */ + --color-on-accent: #ffffff; + /* Canonical keyboard focus-ring color (applied globally in Fase 2). */ + --color-focus-ring: #ff5e3a; + /* Canonical logo gradient — unifies the divergent sidebar (#ff5e3a→#ff8a5c) + * vs accent (#ff5e3a→#ff2d55) logo fills. Consumers migrate in Fase 1. */ + --color-logo-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff8a5c 100%); + --color-accent-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%); + --color-accent-shadow: rgba(255, 94, 58, 0.3); + --color-accent-ring: light-dark(rgba(255, 94, 58, 0.1), rgba(255, 94, 58, 0.15)); + --color-accent-tint: light-dark(#fff5f3, #2a1a15); + --color-accent-mid: #ff8a5c; + --color-accent-shadow-lg: rgba(255, 94, 58, 0.4); + --color-accent-bg: rgba(255, 94, 58, 0.06); + --color-accent-bg-sm: rgba(255, 94, 58, 0.08); + --color-accent-ring-dark: rgba(255, 94, 58, 0.15); + --color-accent-ring-strong: rgba(255, 94, 58, 0.2); + --color-accent-ring-xl: rgba(255, 94, 58, 0.4); + --color-accent-ring-xs: rgba(255, 94, 58, 0.05); + --color-accent-glow: rgba(255, 94, 58, 0.2); + --color-accent-glow-soft: rgba(255, 94, 58, 0.1); + + /* Ambient brand backdrop — shared by the external surfaces (login / share / + device). A centred "spotlight" (surface is brighter than page in BOTH + light and dark) seats the card in a pool of light; four warm brand blobs + fill the field so it reads as a deliberate, dimensional canvas rather than + flat near-white. Resolves through light-dark() automatically. */ + --brand-ambient: + radial-gradient(55% 50% at 8% 4%, var(--color-accent-glow), transparent 60%), + radial-gradient(55% 55% at 95% 98%, var(--color-accent-glow), transparent 58%), + radial-gradient(48% 48% at 88% 12%, var(--color-accent-glow-soft), transparent 55%), + radial-gradient(50% 45% at 6% 92%, var(--color-accent-glow-soft), transparent 55%), + radial-gradient(78% 64% at 50% 33%, var(--color-bg-surface), transparent 70%), var(--color-bg-page); + /* Desaturated fractal-noise grain — kills gradient banding and adds a + tactile, "expensive" film. No colour inside the data-URI (token-safe); + applied via a low-opacity overlay pseudo-element. */ + --brand-grain: url("data:image/svg+xml,"); + + /* Feedback — success unified to one restrained emerald; the 6 green variants + * now alias the canonical bg/text. (error-* is the danger tint, kept.) */ + --color-error-bg: light-dark(#fee2e2, #3b1111); + --color-error-text: light-dark(#b91c1c, #fca5a5); + --color-success-bg: light-dark(#dcfce7, #052e16); + --color-success-text: light-dark(#15803d, #86efac); + --color-success-border: #16a34a; + --color-success-alt: #16a34a; + --color-success-bg-alt: var(--color-success-bg); + --color-success-text-alt: var(--color-success-text); + --color-success-bg-green: var(--color-success-bg); + --color-success-text-green: var(--color-success-text); + + /* Dangerous actions — unified on #ef4444 / #dc2626; danger text-alt now uses + * the AA-passing error-text instead of a sub-4.5:1 red. */ + --color-danger-bg: #ef4444; + --color-danger-text: #ffffff; + --color-danger-bg-hover: #dc2626; + --color-danger-alt: #ef4444; + --color-danger-ring: rgba(239, 68, 68, 0.3); + --color-danger-ring-lg: rgba(239, 68, 68, 0.4); + --color-danger-light-bg: light-dark(#fef2f2, #2a0c0c); + --color-danger-lighter: light-dark(#fef2f2, #2a0c0c); + --color-danger-text-alt: var(--color-error-text); + --color-danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); + + /* Warning — unified to one restrained amber. Text tier (#b45309) clears AA; + * the bright #ffc107 gold is replaced by amber-500 for borders/fills. Legacy + * orange/amber variants alias the canonical tokens. */ + --color-warning-bg: light-dark(#fef3c7, #2a2410); + --color-warning-text: light-dark(#b45309, #fbbf24); + --color-warning-border: #f59e0b; + --color-warning-bg-dark: light-dark(#fde68a, #3d2e00); + --color-warning-ring: rgba(245, 158, 11, 0.12); + --color-warning-shadow: rgba(245, 158, 11, 0.4); + --color-warning-orange-bg: var(--color-warning-bg); + --color-warning-orange-border: var(--color-warning-border); + --color-warning-orange-text: var(--color-warning-text); + --color-warning-bg-light: var(--color-warning-bg); + --color-warning-text-amber: var(--color-warning-text); + --color-warning-bg-orange: var(--color-warning-bg); + --color-warning-text-orange: var(--color-warning-text); + + /* Info — unified to one blue (AA text #1d4ed8, with a light-dark dark tier). + * Variants alias the canonical tokens. */ + --color-info-bg: light-dark(#eff6ff, #0c2d48); + --color-info-text: light-dark(#1d4ed8, #93c5fd); + --color-info-border: #3b82f6; + --color-info-blue: #3b82f6; + --color-info-bg-alt: var(--color-info-bg); + --color-info-text-alt: var(--color-info-text); + --color-info-surface: var(--color-info-bg); + + /* Shadows — stronger in dark mode for parity. Dark values form a + * deliberate progression (base 0.3 < md 0.34 < lg 0.38) so elevation + * levels stay perceptually distinct; they used to all collapse to 0.3. */ + --color-shadow: light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3)); + --color-shadow-lg: light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.38)); + --color-shadow-xs: rgba(0, 0, 0, 0.05); + --color-shadow-sm: rgba(0, 0, 0, 0.08); + --color-shadow-md: light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.34)); + --color-shadow-xl: rgba(0, 0, 0, 0.2); + --color-shadow-2xl: rgba(0, 0, 0, 0.25); + --color-shadow-3xl: rgba(0, 0, 0, 0.3); + --color-shadow-4xl: rgba(0, 0, 0, 0.4); + + /* Overlays — same in both modes; they sit on top of arbitrary content. */ + --color-overlay: rgba(0, 0, 0, 0.5); + /* Frosted scrim behind overlay controls (favorite/kebab/checkbox) so they + stay legible on top of any thumbnail, light or dark. */ + --color-scrim-control: light-dark(rgba(255, 255, 255, 0.92), rgba(15, 23, 42, 0.82)); + --color-overlay-light: rgba(0, 0, 0, 0.45); + --color-overlay-heavy: rgba(0, 0, 0, 0.85); + --color-overlay-darkest: rgba(0, 0, 0, 0.92); + --color-overlay-shadow: rgba(0, 0, 0, 0.6); + + /* Foreground / control surfaces on top of dark overlays */ + --color-on-overlay: rgba(255, 255, 255, 0.95); + --color-on-overlay-muted: rgba(255, 255, 255, 0.9); + --color-overlay-button: rgba(255, 255, 255, 0.12); + --color-overlay-button-hover: rgba(255, 255, 255, 0.22); + + /* Items */ + --color-item: var(--color-bg-surface); + --color-item-hover: var(--color-bg-hover); + --color-item-active: light-dark(#f8d2ae, #5a5047); + --color-item-selected: light-dark(#fff8f6, #39281a); + --color-item-hover-accent: light-dark(#fff0ec, #3d342c); + --color-item-hover-blue: #f0f8ff; + --color-item-hover-sky: #e0f2fe; + + /* Sidebar — already dark-leaning in both modes; dark mode goes deeper. */ + --color-sidebar-bg-from: light-dark(#2a3042, #0f172a); + --color-sidebar-bg-to: light-dark(#232838, #0c1322); + --color-sidebar-text: rgba(255, 255, 255, 0.65); + --color-sidebar-text-hover: rgba(255, 255, 255, 0.9); + --color-sidebar-text-active: #ffffff; + --color-sidebar-active-bg: rgba(255, 94, 58, 0.12); + --color-sidebar-hover-bg: rgba(255, 255, 255, 0.06); + --color-sidebar-separator: rgba(255, 255, 255, 0.07); + --color-sidebar-overlay: rgba(0, 0, 0, 0.5); + --color-sidebar-storage-bg: rgba(255, 255, 255, 0.05); + --color-sidebar-storage-border: rgba(255, 255, 255, 0.07); + --color-sidebar-storage-text: rgba(255, 255, 255, 0.8); + --color-sidebar-storage-bar: rgba(255, 255, 255, 0.1); + --color-sidebar-storage-faint: rgba(255, 255, 255, 0.5); + --color-sidebar-logo-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff8a5c 100%); + --color-sidebar-progress: linear-gradient(90deg, #ff5e3a 0%, #ff8a5c 100%); + --color-sidebar-shadow: rgba(255, 94, 58, 0.35); + --color-sidebar-shadow-lg: rgba(255, 94, 58, 0.45); + + /* Calendar dots — regenerated at fixed S=55% L=62%, hues evenly around the + * wheel, so they read as one curated family (not confetti). These also tint + * the sidebar nav icons (sidebar.css :nth-child rules). */ + --color-cal-1: #d36868; + --color-cal-2: #d3a268; + --color-cal-3: #c9d368; + --color-cal-4: #8fd368; + --color-cal-5: #68d37c; + --color-cal-6: #68d3b6; + --color-cal-7: #68b6d3; + --color-cal-8: #687cd3; + --color-cal-9: #8f68d3; + --color-cal-10: #c968d3; + --color-cal-11: #d368a2; + + /* File type badge colors */ + --color-ft-html: #e34c26; + --color-ft-js: #2965f1; + --color-ft-python: #3776ab; + --color-ft-typescript: #3178c6; + --color-ft-rust: #dea584; + --color-ft-go: #00add8; + --color-ft-java: #e76f00; + --color-ft-shell: #555555; + --color-ft-csharp: #68217a; + --color-ft-php: #8892be; + --color-ft-ruby: #cc342d; + --color-ft-swift: #fa7343; + --color-ft-kotlin: #7f52ff; + --color-ft-scala: #e38c00; + --color-ft-angular: #cb171e; + --color-ft-cpp: #9c4221; + --color-ft-docker: #083fa1; + --color-ft-generic-blue: #556ee6; + --color-ft-generic-green: #4eaa25; + --color-ft-generic-gray: #a0aec0; + --color-ft-orange-light: #ffb86c; + --color-ft-yellow: #ffd43b; + --color-ft-orange-alt: #e34c26; + --color-ft-coffeescript: #9c4221; + + /* File type icon background/text pairs */ + --color-ft-folder-bg: #ffeaa7; + --color-ft-folder-tab: #fdcb6e; + --color-ft-doc-bg: #e0ecff; + --color-ft-doc-text: #3171d8; + --color-ft-pdf-bg: #fee2e2; + --color-ft-pdf-text: #e53e3e; + --color-ft-image-bg: #e0f2fe; + --color-ft-image-text: #3b82f6; + --color-ft-video-bg-from: #ede9fe; + --color-ft-video-bg-to: #fce7f3; + --color-ft-video-text: #8b5cf6; + --color-ft-audio-bg: #fef3c7; + --color-ft-audio-text: #f59e0b; + --color-ft-audio-alt-bg: #fff3e0; + --color-ft-spreadsheet-bg: #e6f4ea; + --color-ft-spreadsheet-text: #0d904f; + --color-ft-presentation-bg: #fef3e2; + --color-ft-presentation-text: #d04423; + --color-ft-archive-bg: #f5f0eb; + --color-ft-archive-text: #8d6e63; + --color-ft-installer-bg: #f3e8ff; + --color-ft-installer-text: #7c3aed; + --color-ft-script-bg: #e8f5e9; + --color-ft-script-text: #4eaa25; + --color-ft-config-bg: #f1f3f5; + --color-ft-config-text: #718096; + + /* Multiselect bar — always dark */ + --color-multiselect-bg: #1e293b; + --color-multiselect-border: #334155; + --color-multiselect-text: #ffffff; + --color-multiselect-text-faint: rgba(255, 255, 255, 0.7); + --color-multiselect-hover-bg: rgba(255, 255, 255, 0.1); + --color-multiselect-action-text: #ffffff; + --color-multiselect-action-hover: rgba(255, 255, 255, 0.2); + --color-multiselect-danger-bg: rgba(239, 68, 68, 0.25); + --color-multiselect-danger-text: #fca5a5; + --color-multiselect-danger-active: rgba(239, 68, 68, 0.4); + --color-multiselect-danger-text-active: #ffffff; + + /* Notification */ + --color-notification-bg: light-dark(#ffffff, #1e293b); + --color-notification-badge: #ff3b30; + --color-notification-success: #34c759; + --color-notification-error: #ff3b30; + + /* Photos lightbox — always dark overlay */ + --color-lightbox-overlay: rgba(0, 0, 0, 0.92); + --color-lightbox-btn-bg: rgba(255, 255, 255, 0.12); + --color-lightbox-btn-text: #ffffff; + --color-lightbox-btn-hover: rgba(255, 255, 255, 0.25); + --color-lightbox-gradient-top: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent); + --color-lightbox-gradient-bottom: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent); + --color-lightbox-text-faint: rgba(255, 255, 255, 0.5); + --color-lightbox-text-muted: rgba(255, 255, 255, 0.7); + + /* (Removed: the --color-purple-* family had zero consumers. The music + * gradient and the video file-type purple are separate, retained tokens.) */ + + /* OIDC / auth */ + --color-oidc-bg: var(--color-info-blue); + --color-oidc-shadow: rgba(79, 70, 229, 0.3); + --color-oidc-shadow-lg: rgba(79, 70, 229, 0.4); + + /* Device verify */ + --color-device-verify-text: #ffc107; + --color-device-verify-shadow: rgba(255, 193, 7, 0.5); + --color-device-verify-drop-shadow: rgba(255, 193, 7, 0.4); + --color-device-verify-border: #ffc107; + --color-device-verify-muted: #6c757d; + --color-device-verify-dim: #ccc; + + /* Content area */ + --color-content-muted: #888; + --color-content-bg-warn: light-dark(#ffeaa7, #3d2e00); + --color-content-bg-warn-dark: light-dark(#fdcb6e, #5a4200); + + /* User menu */ + --color-user-menu-header-bg: light-dark(linear-gradient(135deg, #fef5f3 0%, #fdf2f8 100%), linear-gradient(135deg, #1a2332 0%, #1e2940 100%)); + --color-user-menu-header-border: light-dark(#fce7e1, #3a2520); + + /* Share dialog */ + --color-share-link-text: var(--color-info-text); + --color-share-link-hover: var(--color-info-text); + --color-share-remove-text: #b71c1c; + --color-share-owner-text: #757575; + + /* Primary (style.css) */ + /* Demoted: orange is the sole brand/primary — primary now aliases the accent + * (was a competing blue #2563eb). Flips device-verify / share / userMenu to brand. */ + --color-primary: var(--color-accent); + --color-primary-hover: var(--color-accent-hover); + + /* Recent view */ + --color-recent-muted: #6c757d; + --color-recent-border: #6c757d; + + /* Star colors */ + --color-star-text: #fbbf24; + --color-star-text-hover: #f59e0b; + --color-star-active: #d97706; + + /* Card drop target */ + --color-card-drop-tint: rgba(230, 126, 34, 0.08); + --color-card-drop-border: #e67e22; + + /* Neutral backgrounds */ + --color-neutral-warm-bg: #f5f0eb; + --color-neutral-warm-text: #8d6e63; + --color-neutral-bg: #f1f3f5; + + /* Admin/profile blue accent */ + --color-admin-blue: #60a5fa; + --color-admin-blue-bg: rgba(59, 130, 246, 0.1); + --color-admin-blue-bg-sm: rgba(59, 130, 246, 0.15); + + /* Danger hover bg (for logout etc.) */ + --color-danger-hover-bg: rgba(239, 68, 68, 0.1); + + /* Additional success rings */ + --color-success-ring: rgba(72, 187, 120, 0.1); + --color-success-ring-dark: rgba(72, 187, 120, 0.15); + --color-success-text-strong: #2f855a; + --color-success-ring-vivid: rgba(74, 222, 128, 0.1); + --color-success-text-vivid: #86efac; + --color-success-ring-vivid-lg: rgba(74, 222, 128, 0.15); + --color-success-icon-vivid: #4ade80; + --color-secret-green: #059669; + + /* Additional overlays */ + --color-overlay-mid: rgba(0, 0, 0, 0.6); + --color-overlay-video: rgba(0, 0, 0, 0.55); + + /* Progress overlays */ + --color-progress-overlay: rgba(255, 255, 255, 0.95); + --color-progress-overlay-dark: rgba(30, 41, 59, 0.95); + + /* Misc */ + --color-black: #000000; + --color-info-border-light: #90cdf4; + --color-notification-error-ring: rgba(255, 59, 48, 0.1); + --color-accent-second: #ff2d55; + --color-warning-ring-xs: rgba(255, 193, 7, 0.05); + + /* Avatar / profile */ + --color-avatar-gradient: linear-gradient(135deg, #3b82f6, #6366f1); + --color-text-navy: #1a1a2e; + --color-role-admin-bg: #dbeafe; + --color-role-admin-text: #1d4ed8; + --color-dark-mid: #475569; + --color-role-admin-dark-bg: #1e3a5f; + + /* Photo tile */ + --color-photo-check-border: rgba(255, 255, 255, 0.8); + + /* Accent shadow (smaller) */ + --color-accent-shadow-sm: rgba(255, 94, 58, 0.25); + + /* Warning faint background */ + --color-warning-bg-faint: #fffbeb; + + /* Storage progress fill gradients */ + --color-storage-fill-green: linear-gradient(90deg, #059669, #10b981); + --color-storage-fill-orange: linear-gradient(90deg, #d97706, #f59e0b); + --color-storage-fill-red: linear-gradient(90deg, #dc2626, #ef4444); + + /* Error text (dark shade) — light-mode is dark red, dark-mode is light red. */ + --color-error-text-dark: light-dark(#991b1b, #f87171); + + /* Stat warning border */ + --color-stat-warn-border: #fbbf24; + + /* Status badge — success/emerald */ + --color-badge-success-bg: #ecfdf5; + --color-badge-success-bg-medium: #d1fae5; + --color-badge-success-text: #065f46; + --color-badge-success-border: #a7f3d0; + --color-badge-success-fill: #047857; + --color-badge-success-fill-dark: #064e27; + --color-badge-success-fill-faint: #f0fdf4; + --color-badge-green-bg: #ecfdf5; + --color-badge-green-text: #065f46; + + /* Status badge — orange/coral (used by role-chip & user-vignette) */ + --color-badge-orange-bg: light-dark(#fff5f3, #2a1814); + --color-badge-orange-text: light-dark(#ff5e3a, #ff8a65); + + /* Status badge — error/red */ + --color-badge-error-border: #fecaca; + + /* Status badge — warning/amber */ + --color-badge-warning-text: #92400e; + --color-badge-warning-border: #fde68a; + --color-badge-amber-bg: #fef3c7; + --color-badge-amber-text: #f59e0b; + + /* Status badge — indigo/purple */ + --color-badge-indigo-bg: #ede9fe; + --color-badge-indigo-text: #6d28d9; + + /* Status badge — blue (used by role-chip & user-vignette) */ + --color-badge-blue-bg: light-dark(#eff6ff, #0c2d48); + --color-badge-blue-text: light-dark(#1e40af, #93c5fd); + --color-badge-blue-border: #bfdbfe; + + /* Status badge — gray/disabled */ + --color-badge-gray: #d1d5db; + + /* (Removed: the legacy dark-mode badge tokens that lived here had zero + * consumers — the light-dark() badge tokens above are the single source.) */ + + /* Dark structural */ + --color-dark-footer: #162032; + --color-scrollbar-dark: rgba(255, 255, 255, 0.15); + --color-border-dark-faint: rgba(255, 255, 255, 0.03); + + /* Misc */ + --color-bg-off-white: #fafbfd; + --color-danger-shadow: rgba(220, 38, 38, 0.2); + --color-danger-shadow-lg: rgba(220, 38, 38, 0.3); + + --color-music-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --color-music-background: var(--color-bg-surface); + --color-music-public-bg: rgba(74, 144, 217, 0.12); + + --color-video-play: #ffffff; + --color-video-play-shadow: #000000; +} + +/* Explicit user choice overrides the OS preference. `theme-init.js` writes + * the attribute from localStorage on render-blocking startup. */ +html[data-color-scheme="light"] { + color-scheme: light; +} + +html[data-color-scheme="dark"] { + color-scheme: dark; +} + +/* Compact density — tighter rows/controls. Toggled by writing + * data-density="compact" on ; consumed by list/control CSS in Fase 1. */ +html[data-density="compact"] { + --density-row-py: var(--space-2); /* 8px */ + --density-row-px: var(--space-2-5); /* 10px */ + --density-gap: var(--space-2); + --density-control-h: 32px; +} + +/* High-contrast border ramp (prefers-contrast: more). Lives here in the token + * layer — the only place raw colour values belong — so the "no raw hex outside + * variables/themes" invariant holds. The matching text-tier collapse + focus + * ring (token/outline only) stay in base/a11y.css. */ +@media (prefers-contrast: more) { + :root { + --color-border: light-dark(#64748b, #94a3b8); + --color-border-light: light-dark(#64748b, #94a3b8); + --color-border-medium: light-dark(#475569, #cbd5e1); + } +} diff --git a/frontend/src/lib/styles/legacy.css b/frontend/src/lib/styles/legacy.css new file mode 100644 index 00000000..bf290228 --- /dev/null +++ b/frontend/src/lib/styles/legacy.css @@ -0,0 +1,13 @@ +/* Vendored layout/component CSS ported verbatim from the original static/css. + * These are token-based and global; the Svelte components emit the same class + * names and DOM so the new app matches the original look. Kept byte-faithful + * (linters ignore this dir) — restyle via tokens in variables.css, not here. */ +@import url('./legacy/sidebar.css'); +@import url('./legacy/topbar.css'); +@import url('./legacy/content.css'); +@import url('./legacy/buttons.css'); +@import url('./legacy/breadcrumb.css'); +@import url('./legacy/fileManager.css'); +@import url('./legacy/resourceList.css'); +@import url('./legacy/skeleton.css'); +@import url('./legacy/auth.css'); diff --git a/frontend/src/lib/styles/legacy/auth.css b/frontend/src/lib/styles/legacy/auth.css new file mode 100644 index 00000000..526eecf7 --- /dev/null +++ b/frontend/src/lib/styles/legacy/auth.css @@ -0,0 +1,805 @@ +/* ============================================================ + Auth styles for OxiCloud — design tokens from variables.css + ============================================================ */ + +.auth-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100dvh; + width: 100%; + position: relative; + /* Spotlight + warm brand blobs — seats the card in a pool of light + instead of a flat near-white field. */ + background: var(--brand-ambient); +} + +/* Fine film grain over the backdrop (not the card). `overlay` neutralises the + mid-grey noise so it adds texture without shifting brightness; works in both + light and dark. Tune the whole effect with `opacity`. */ +.auth-container::before { + content: ""; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: var(--brand-grain); + background-size: 180px 180px; + opacity: 0.6; + mix-blend-mode: overlay; +} + +/* Keep the card (and every panel) above the grain layer. */ +.auth-panel { + position: relative; + z-index: 1; +} + +.auth-panel { + width: 420px; + max-width: 90%; + margin: 0 auto; + background-color: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-3xl); + box-shadow: var(--shadow-xl); + padding: var(--space-9); + text-align: center; +} + +.auth-logo { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: var(--space-5); +} + +.auth-logo-icon { + width: 52px; + height: 52px; + background: var(--color-accent-gradient); + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + margin-right: var(--space-3); + /* Tighter glow (negative spread) reads more premium than a wide halo. */ + box-shadow: 0 4px 14px -4px var(--color-accent-shadow); +} + +.auth-logo-icon svg { + width: 30px; + height: 30px; + fill: var(--color-on-accent); +} + +.auth-logo-text { + font-size: var(--text-2xl); + font-weight: var(--weight-bold); + color: var(--color-text); +} + +.auth-title { + font-size: 22px; + font-weight: var(--weight-bold); + margin-bottom: var(--space-7); + color: var(--color-text-heading); +} + +.auth-form { + width: 100%; + text-align: left; + + [dir="rtl"] & { + text-align: right; + } +} + +.auth-input-group { + margin-bottom: var(--space-5); +} + +.auth-label { + display: block; + margin-bottom: var(--space-2); + font-size: var(--text-base); + color: var(--color-text-heading); + font-weight: var(--weight-semibold); +} + +.auth-input { + width: 100%; + padding: var(--space-3-5) 18px; + border-radius: var(--radius-2xl); + /* Stronger resting border so fields read as crafted, not flat fills. */ + border: 2px solid var(--color-border-medium); + font-size: 15px; + background-color: var(--color-bg-input); + color: var(--color-text); + transition: all 0.2s ease; +} + +.auth-input::placeholder { + color: var(--color-text-muted); +} + +.auth-input:hover { + border-color: var(--color-accent); + background-color: var(--color-bg-surface); +} + +.auth-input:focus { + outline: none; + border-color: var(--color-accent); + background-color: var(--color-bg-surface); + box-shadow: 0 0 0 3px var(--color-accent-ring); +} + +.auth-input[readonly] { + cursor: default; + /* A locked value, not a placeholder: full-strength text on a subtly + distinct "locked" fill (no dimming that reads as empty). */ + color: var(--color-text); + font-weight: var(--weight-semibold); + background-color: var(--color-bg-input-alt); +} + +.auth-button { + width: 100%; + padding: var(--space-3-5) 18px; + border-radius: var(--radius-2xl); + background: var(--color-accent-gradient); + color: var(--color-on-accent); + font-weight: var(--weight-bold); + border: none; + cursor: pointer; + font-size: var(--text-md); + transition: all 0.3s ease; + margin-top: var(--space-3); + box-shadow: 0 4px 12px var(--color-accent-shadow); +} + +.auth-button:hover { + transform: translateY(-1px); + box-shadow: 0 6px 20px var(--color-accent-shadow-lg); + filter: brightness(1.05); +} + +.auth-button:active { + /* Tactile press — the button dips slightly under the resting plane. */ + transform: translateY(1px); + box-shadow: 0 2px 8px var(--color-accent-shadow); +} + +.auth-button:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + filter: none; +} + +/* Loading — hide the label, show an inline spinner (toggled via .is-loading / + aria-busy by auth.js on submit). */ +.auth-button.is-loading, +.auth-button[aria-busy="true"] { + color: transparent; + pointer-events: none; + position: relative; +} + +.auth-button.is-loading::after, +.auth-button[aria-busy="true"]::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 18px; + height: 18px; + margin: -9px 0 0 -9px; + border: 2px solid var(--color-on-accent); + border-top-color: transparent; + border-radius: var(--radius-full); + animation: spin var(--spin-duration) linear infinite; +} + +.auth-subtitle { + margin: var(--space-5) 0; + color: var(--color-text-muted); + font-size: var(--text-base); +} +.auth-action-wrap { + margin-top: var(--space-5); +} + +/* SSO / OIDC button */ +.auth-button-oidc { + background: linear-gradient(135deg, var(--color-text) 0%, var(--color-text-secondary) 100%); + box-shadow: 0 4px 12px var(--color-shadow-3xl); + display: flex; + align-items: center; + justify-content: center; + gap: var(--space-2-5); +} + +.auth-button-oidc:hover { + box-shadow: 0 6px 20px var(--color-shadow-4xl); +} + +.auth-button-sso { + background: var(--color-oidc-bg); + box-shadow: 0 4px 12px var(--color-oidc-shadow); +} +.auth-button-sso:hover { + box-shadow: 0 6px 20px var(--color-oidc-shadow-lg); +} + +.auth-button-oidc i { + font-size: var(--text-base); +} + +/* Helper text above the magic-link form ("No password? Enter your + email…"). Quieter visual weight than the form labels. */ +.auth-hint { + margin: 0 0 var(--space-3); + font-size: var(--text-sm); + line-height: 1.4; + color: var(--color-text-secondary); +} + +/* Status banner under the magic-link form. Uniform anti-enumeration + message rendered on every successful 2xx; error variant only used + for the 503-not-configured branch or network failures. */ +.auth-status { + margin-top: var(--space-3); + padding: var(--space-2-5) var(--space-3-5); + border-radius: var(--radius-lg); + font-size: var(--text-sm); + line-height: 1.4; +} +.auth-status-success { + background: var(--color-bg-hover); + color: var(--color-text); + border-left: 3px solid var(--color-warning-orange-text); +} +.auth-status-error { + background: var(--color-bg-hover); + color: var(--color-text); + border-left: 3px solid var(--color-warning-orange-text); +} + +/* Divider between password and SSO login */ +.auth-divider { + display: flex; + align-items: center; + margin: var(--space-5) 0; + color: var(--color-text-faint); + font-size: var(--text-sm); +} + +.auth-divider::before, +.auth-divider::after { + content: ""; + flex: 1; + height: 1px; + background: var(--color-border); +} + +.auth-divider span { + padding: 0 var(--space-3); + /* Quiet, deliberate label rather than a stray lowercase letter. */ + text-transform: uppercase; + letter-spacing: var(--tracking-wide, 0.08em); + font-size: var(--text-2xs); + font-weight: var(--weight-semibold); + color: var(--color-text-muted); +} + +/* OIDC-only mode: hide password form */ +.auth-form.hidden { + display: none; +} + +.auth-toggle { + margin-top: 22px; + font-size: var(--text-base); + color: var(--color-text-muted); +} + +.auth-toggle-link { + color: var(--color-accent-text); + cursor: pointer; + text-decoration: none; + font-weight: var(--weight-medium); +} + +.auth-toggle-link:hover { + text-decoration: underline; +} + +.auth-error { + background-color: var(--color-error-bg); + color: var(--color-error-text); + padding: var(--space-3) 18px; + border-radius: var(--radius-2xl); + margin-bottom: var(--space-5); + font-size: var(--text-base); + display: none; +} + +.auth-success { + background-color: var(--color-success-bg); + color: var(--color-success-text); + padding: var(--space-3) 18px; + border-radius: var(--radius-2xl); + margin-bottom: var(--space-5); + font-size: var(--text-base); + display: none; +} + +/* Admin setup panel styles — visibility controlled via .hidden class */ + +.setup-steps { + margin-bottom: var(--space-7); + /* 3 equal columns → circle centres land at 1/6, 1/2, 5/6, so the + connector track can be placed deterministically between them. */ + display: grid; + grid-template-columns: repeat(3, 1fr); + position: relative; +} + +/* Connector track behind the step circles (z-index 0; circles sit above). */ +.setup-steps::before { + content: ""; + position: absolute; + top: 17px; /* half of the 34px circle */ + left: 16.667%; + right: 16.667%; + height: 2px; + background: var(--color-border); + z-index: 0; +} + +.setup-step { + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + align-items: center; +} + +.step-number { + width: 34px; + height: 34px; + background-color: var(--color-bg-input); + border: 2px solid var(--color-border); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-faint); + font-weight: var(--weight-bold); + font-size: var(--text-base); + margin-bottom: var(--space-1-5); + transition: all 0.2s ease; +} + +.step-number.active { + background: var(--color-accent-gradient); + border-color: transparent; + color: var(--color-on-accent); + /* Subtle halo lifts the active step off the connector track. */ + box-shadow: + 0 0 0 4px var(--color-accent-ring), + 0 4px 12px var(--color-accent-shadow); +} + +.step-title { + font-size: var(--text-xs); + color: var(--color-text-faint); + font-weight: var(--weight-medium); +} + +.step-title.active { + color: var(--color-text-heading); + font-weight: var(--weight-semibold); +} + +/* Language selector panel styles */ +.language-selector-panel { + text-align: center; +} + +.language-subtitle { + color: var(--color-text-muted); + font-size: var(--text-md); + margin-bottom: var(--space-6); +} + +/* ====== Compact Language Picker ====== */ +.lang-picker { + position: relative; + margin-bottom: var(--space-6); + text-align: left; +} + +.lang-picker-selected { + display: flex; + align-items: center; + padding: var(--space-3-5) 18px; + border: 2px solid var(--color-border); + border-radius: var(--radius-2xl); + cursor: pointer; + background-color: var(--color-bg-input); + transition: all 0.2s ease; + user-select: none; +} + +.lang-picker-selected:hover { + border-color: var(--color-accent); + background-color: var(--color-bg-surface); +} + +.lang-picker.open .lang-picker-selected { + border-color: var(--color-accent); + background-color: var(--color-bg-surface); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + box-shadow: 0 0 0 3px var(--color-accent-ring); +} + +.lang-picker-flag { + font-size: 26px; + margin-right: var(--space-3-5); + flex-shrink: 0; +} + +.lang-picker-name { + font-size: var(--text-md); + font-weight: var(--weight-semibold); + color: var(--color-text-heading); + flex: 1; +} + +.lang-picker-arrow { + color: var(--color-text-faint); + font-size: var(--text-sm); + transition: transform 0.2s ease; + flex-shrink: 0; +} + +.lang-picker.open .lang-picker-arrow { + transform: rotate(180deg); +} + +/* Dropdown */ +.lang-picker-dropdown { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--color-bg-surface); + border: 2px solid var(--color-accent); + border-top: 1px solid var(--color-border-light); + border-bottom-left-radius: var(--radius-2xl); + border-bottom-right-radius: var(--radius-2xl); + box-shadow: 0 12px 32px var(--color-shadow-lg); + z-index: 100; + overflow: hidden; +} + +.lang-picker.open .lang-picker-dropdown { + display: block; + animation: langPickerSlideDown 0.2s ease; +} + +@keyframes langPickerSlideDown { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Search inside dropdown */ +.lang-picker-search { + position: relative; + padding: var(--space-2-5) var(--space-3-5); + border-bottom: 1px solid var(--color-border-light); +} + +.lang-picker-search i { + position: absolute; + left: 26px; + top: 50%; + transform: translateY(-50%); + color: var(--color-text-faint); + font-size: var(--text-sm); +} + +.lang-picker-search input { + width: 100%; + padding: var(--space-2) var(--space-3) var(--space-2) var(--space-8); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + font-size: var(--text-base); + outline: none; + box-sizing: border-box; + background-color: var(--color-bg-input); + color: var(--color-text); + transition: border-color 0.2s; +} + +.lang-picker-search input::placeholder { + color: var(--color-text-faint); +} + +.lang-picker-search input:focus { + border-color: var(--color-accent); +} + +/* Scrollable list */ +.lang-picker-list { + max-height: 240px; + overflow-y: auto; + padding: var(--space-1-5); +} + +/* Language item in dropdown */ +.lang-picker-item { + display: flex; + align-items: center; + gap: var(--space-2-5); + padding: var(--space-2-5) var(--space-3); + border-radius: var(--radius-lg); + cursor: pointer; + transition: all 0.12s ease; +} + +.lang-picker-item:hover { + background: var(--color-bg-hover); +} + +.lang-picker-item.selected { + background: var(--color-accent-tint); +} + +.lang-picker-item-flag { + font-size: 22px; + flex-shrink: 0; +} + +.lang-picker-item-name { + font-size: 15px; + font-weight: var(--weight-medium); + color: var(--color-text-heading); +} + +.lang-picker-item-english { + font-size: var(--text-sm); + color: var(--color-text-faint); + margin-left: auto; +} + +.lang-picker-item-check { + color: var(--color-accent); + font-size: var(--text-sm); + flex-shrink: 0; +} + +.lang-picker-empty { + text-align: center; + color: var(--color-text-faint); + padding: var(--space-5); + font-size: var(--text-base); +} + +@media (max-width: 480px) { + .auth-panel { + width: 95%; + padding: var(--space-6); + } + + .lang-picker-selected { + padding: var(--space-3) var(--space-3-5); + } + + .lang-picker-list { + max-height: 200px; + } +} + +/* ============================================================ + Premium polish — brand lockup, CTA hierarchy, field icons, + password reveal, progressive disclosure, match feedback. + Icons are token-safe CSS masks (no inline SVG, no raw colour): + the glyph alpha comes from the data-URI, the colour from a token. + ============================================================ */ + +/* — Brand wordmark: the ownable "Oxi" accent lockup (DESIGN-SYSTEM §3) — */ +.brand-oxi { + background: var(--color-accent-gradient); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +/* — CTA hierarchy: exactly one primary per screen. The secondary action + (magic-link, etc.) is a quiet tinted/ghost button, never a 2nd gradient. — */ +.auth-button-secondary { + background: var(--color-accent-tint); + color: var(--color-accent-text); + border: 1.5px solid var(--color-border-medium); + box-shadow: none; +} + +.auth-button-secondary:hover { + background: var(--color-bg-surface); + border-color: var(--color-accent); + box-shadow: none; + filter: none; + transform: translateY(-1px); +} + +.auth-button-secondary:active { + transform: translateY(1px); + box-shadow: none; +} + +/* — Leading field icons (user / mail / lock) via masked pseudo-element — */ +.auth-input-wrap { + position: relative; +} + +.auth-input-wrap .auth-input { + padding-left: 44px; +} + +.auth-input-wrap.has-toggle .auth-input { + padding-right: 44px; +} + +.auth-input-wrap::before { + content: ""; + position: absolute; + left: 16px; + top: 50%; + width: 18px; + height: 18px; + transform: translateY(-50%); + background-color: var(--color-text-muted); + pointer-events: none; + z-index: 1; + transition: background-color 0.2s ease; + -webkit-mask: var(--icon-url, none) center / contain no-repeat; + mask: var(--icon-url, none) center / contain no-repeat; +} + +.auth-input-wrap:focus-within::before { + background-color: var(--color-accent); +} + +.auth-input-wrap--user { + --icon-url: url("data:image/svg+xml,"); +} + +.auth-input-wrap--mail { + --icon-url: url("data:image/svg+xml,"); +} + +.auth-input-wrap--lock { + --icon-url: url("data:image/svg+xml,"); +} + +/* — Password show/hide toggle — */ +.auth-pw-toggle { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + width: 32px; + height: 32px; + border: none; + background: transparent; + cursor: pointer; + border-radius: var(--radius-lg); + display: flex; + align-items: center; + justify-content: center; + z-index: 2; +} + +.auth-pw-toggle::before { + content: ""; + width: 18px; + height: 18px; + background-color: var(--color-text-muted); + transition: background-color 0.2s ease; + --eye: url("data:image/svg+xml,"); + -webkit-mask: var(--eye) center / contain no-repeat; + mask: var(--eye) center / contain no-repeat; +} + +.auth-pw-toggle:hover::before { + background-color: var(--color-accent); +} + +.auth-pw-toggle[aria-pressed="true"]::before { + --eye: url("data:image/svg+xml,"); +} + +/* — Progressive disclosure of the magic-link form — */ +.auth-magic-toggle { + display: block; + width: 100%; + margin-top: var(--space-1); + padding: var(--space-2); + background: none; + border: none; + color: var(--color-accent-text); + font-size: var(--text-sm); + font-weight: var(--weight-medium); + cursor: pointer; + text-align: center; +} + +.auth-magic-toggle:hover { + text-decoration: underline; +} + +.auth-magic-reveal { + margin-top: var(--space-3); +} + +.auth-magic-reveal:not(.hidden) { + animation: langPickerSlideDown 0.2s ease; +} + +/* — Live password-match feedback (sits inside the confirm field group) — */ +.auth-match { + margin-top: var(--space-2); + font-size: var(--text-xs); + font-weight: var(--weight-medium); + display: none; + align-items: center; + gap: var(--space-1-5); +} + +.auth-match.show { + display: flex; +} + +.auth-match::before { + content: ""; + width: 14px; + height: 14px; + flex-shrink: 0; + background-color: currentColor; + -webkit-mask: var(--match-icon) center / contain no-repeat; + mask: var(--match-icon) center / contain no-repeat; +} + +.auth-match--ok { + color: var(--color-success-text); + --match-icon: url("data:image/svg+xml,"); +} + +.auth-match--bad { + color: var(--color-error-text); + --match-icon: url("data:image/svg+xml,"); +} + +/* "Caps Lock is on" hint under a password field (toggled by auth.js). */ +.auth-caps-warning { + display: flex; + align-items: center; + gap: var(--space-1-5); + margin-top: var(--space-2); + font-size: var(--text-xs); + font-weight: var(--weight-medium); + color: var(--color-warning-orange-text); +} diff --git a/frontend/src/lib/styles/legacy/breadcrumb.css b/frontend/src/lib/styles/legacy/breadcrumb.css new file mode 100644 index 00000000..7f6e2237 --- /dev/null +++ b/frontend/src/lib/styles/legacy/breadcrumb.css @@ -0,0 +1,65 @@ +/* Breadcrumb */ +.breadcrumb { + display: flex; + align-items: center; + flex-wrap: wrap; + margin-bottom: 15px; + font-size: var(--text-base); + color: var(--color-text-medium); + gap: var(--space-0-5); +} + +.breadcrumb-item { + padding: var(--space-0-5) var(--space-1); + border-radius: var(--radius-sm); + border: 2px solid transparent; + transition: + background 0.15s, + color 0.15s; +} + +.breadcrumb-link { + cursor: pointer; + color: var(--color-text-muted); +} + +.breadcrumb-link.drop-target { + background-color: var(--color-warning-ring); + border: 2px dashed var(--color-warning-border); +} + +.breadcrumb-link:hover { + text-decoration: underline; + color: var(--color-accent); + background: var(--color-accent-bg); +} + +.breadcrumb-current { + font-weight: var(--weight-semibold); + color: var(--color-text-black); + cursor: default; +} + +.breadcrumb-separator { + margin: 0 var(--space-1); + color: var(--color-text-faint); + font-size: var(--text-xs); + user-select: none; +} + +.breadcrumb-home { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: var(--radius-sm); +} + +.breadcrumb-home i { + font-size: var(--text-xs); +} + +.breadcrumb-home.breadcrumb-link:hover { + background: var(--color-accent-ring); +} diff --git a/frontend/src/lib/styles/legacy/buttons.css b/frontend/src/lib/styles/legacy/buttons.css new file mode 100644 index 00000000..6048b6ab --- /dev/null +++ b/frontend/src/lib/styles/legacy/buttons.css @@ -0,0 +1,259 @@ +.btn { + padding: var(--space-3) var(--space-6); + border-radius: var(--radius-2xl); + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: var(--text-base); + font-weight: var(--weight-medium); + gap: var(--space-2); + transition: all 0.2s ease; +} + +.btn i { + font-size: 15px; +} + +.btn-primary { + background: var(--color-accent-gradient); + color: var(--color-danger-text); + box-shadow: 0 4px 15px var(--color-accent-shadow); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px var(--color-accent-shadow-lg); +} + +.btn-primary:active { + transform: translateY(0); + box-shadow: 0 2px 10px var(--color-accent-shadow); +} + +.btn-secondary { + background-color: var(--color-bg-hover); + color: var(--color-text-secondary); + border: 2px solid var(--color-border); +} + +.btn-secondary:hover { + background-color: var(--color-bg-input-alt); + border-color: var(--color-border-medium); + transform: translateY(-2px); + box-shadow: 0 4px 12px var(--color-shadow-sm); +} + +.btn-secondary:active { + transform: translateY(0); + background-color: var(--color-border); +} + +.btn-danger { + background: var(--color-danger-gradient); + color: var(--color-danger-text); + box-shadow: 0 4px 15px var(--color-danger-ring); +} + +.btn-danger:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px var(--color-danger-ring-lg); +} + +.btn-danger:active { + transform: translateY(0); + box-shadow: 0 2px 10px var(--color-danger-ring); +} + +/* ── State matrix: focus / disabled / loading ───────────────── */ + +/* Keyboard focus ring (explicit so the gradient variants get a crisp ring; + mirrors the global a11y baseline). */ +.btn:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 2px; +} + +/* Disabled — dimmed, no hover lift, non-interactive. */ +.btn:disabled, +.btn[disabled], +.btn.is-disabled { + opacity: 0.5; + cursor: not-allowed; + box-shadow: none; + transform: none; + filter: none; + pointer-events: none; +} + +/* Loading — hide the label, show an inline spinner, block interaction. + Toggle with the `.is-loading` class or `aria-busy="true"`. */ +.btn.is-loading, +.btn[aria-busy="true"] { + position: relative; + color: transparent; + pointer-events: none; +} + +.btn.is-loading::after, +.btn[aria-busy="true"]::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 16px; + height: 16px; + margin: -8px 0 0 -8px; + border: 2px solid var(--color-on-accent); + border-top-color: transparent; + border-radius: var(--radius-full); + animation: spin var(--spin-duration) linear infinite; +} + +/* The secondary button's text isn't white — tint its spinner to the text. */ +.btn-secondary.is-loading::after, +.btn-secondary[aria-busy="true"]::after { + border-color: var(--color-text-secondary); + border-top-color: transparent; +} + +/* View Toggle Buttons */ +.view-toggle { + display: flex; + gap: var(--space-0-5); + padding: 3px; + background-color: var(--color-bg-muted); + border-radius: var(--radius-xl); + border: 1px solid var(--color-border); +} + +.toggle-btn { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 32px; + background-color: transparent; + border: none; + border-radius: var(--radius-lg); + cursor: pointer; + color: var(--color-text-faint); + font-size: var(--text-base); + transition: all 0.2s ease; +} + +.toggle-btn:hover { + background-color: var(--color-border); + color: var(--color-text-subtle); +} + +.toggle-btn.active { + background-color: var(--color-border); + color: var(--color-accent); + box-shadow: 0 1px 3px var(--color-shadow); +} + +.toggle-btn i { + pointer-events: none; +} + +/* ── Group-by selector (inside .view-toggle) ────────────── */ + +.view-toggle-separator { + width: 1px; + height: 20px; + background: var(--color-border-medium); + align-self: center; + margin: 0 var(--space-0-5); +} + +.view-toggle-separator.hidden { + display: none; +} + +.group-by-selector { + display: flex; + align-items: center; + position: relative; +} + +.group-by-selector.hidden { + display: none; +} + +.group-by-btn.active { + color: var(--color-accent); +} + +/* Sort direction button — rotate the SVG icon when order is reversed */ +.sort-dir-btn .oxi-icon { + transition: transform 0.2s ease; +} + +.sort-dir-btn.active .oxi-icon { + transform: rotate(180deg); +} + +/* Active label shown inline next to the icon */ +.group-by-label { + display: none; + font-size: 0.78rem; + font-weight: var(--weight-semibold); + white-space: nowrap; +} + +/* When a group-by is selected the label has text — expand the button to fit */ +.group-by-btn:has(.group-by-label:not(:empty)) { + width: auto; + padding: 0 var(--space-2); + gap: 5px; +} + +.group-by-btn:has(.group-by-label:not(:empty)) .group-by-label { + display: inline; +} + +.group-by-menu { + position: absolute; + top: calc(100% + 6px); + left: 0; + z-index: 200; + min-width: 140px; + background: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + box-shadow: 0 4px 16px var(--color-shadow); + padding: var(--space-1); + display: flex; + flex-direction: column; + gap: var(--space-0-5); +} + +.group-by-menu.hidden { + display: none; +} + +.group-by-option { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-1-5) var(--space-2-5); + border: none; + background: transparent; + border-radius: var(--radius-md); + cursor: pointer; + font-size: 0.85rem; + color: var(--color-text); + text-align: left; + width: 100%; +} + +.group-by-option:hover { + background: var(--color-border); +} + +.group-by-option.active { + color: var(--color-accent); + font-weight: var(--weight-semibold); +} diff --git a/frontend/src/lib/styles/legacy/content.css b/frontend/src/lib/styles/legacy/content.css new file mode 100644 index 00000000..7f212f33 --- /dev/null +++ b/frontend/src/lib/styles/legacy/content.css @@ -0,0 +1,113 @@ +/* ── Scrollbar ── */ + +::-webkit-scrollbar { + width: 6px; +} +::-webkit-scrollbar-track { + background: var(--color-bg-surface); +} +::-webkit-scrollbar-thumb { + background: var(--color-accent); + border-radius: 3px; +} +* { + scrollbar-width: thin; + scrollbar-color: var(--color-accent) var(--color-bg-surface); +} + +/* Content area */ +.content-area { + flex-grow: 1; + padding: var(--space-5) var(--gutter); + overflow-y: scroll; + scrollbar-gutter: stable; +} + +/* Phones: tighten the shared gutter and let the actions bar wrap. */ +@media (max-width: 640px) { + :root { + --gutter: var(--space-4); + } + + /* Mobile uses overlay scrollbars — don't reserve a phantom gutter. */ + .content-area { + scrollbar-gutter: auto; + } + + .actions-bar { + flex-wrap: wrap; + height: auto; + gap: var(--space-2); + } +} + +.page-title { + font-size: var(--text-2xl); + font-weight: var(--weight-bold); + margin-bottom: var(--space-5); + color: var(--color-text); +} + +.page-sticky-header { + position: sticky; + margin: 0px; + padding: var(--space-2-5) 0px; + top: -20px; /* due to padding top of content-area */ + background-color: var(--color-bg-page); + z-index: 100; /* ensure header is above image preview */ +} + +.actions-bar { + display: flex; + justify-content: space-between; + margin: 0 0 var(--space-3); + height: 60px; + padding: var(--space-2-5); +} + +.action-buttons { + display: flex; + flex: auto; + gap: var(--space-3); +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-2); + padding: var(--space-12) var(--space-6); + text-align: center; + color: var(--color-text-muted); + width: 100%; + min-height: 320px; + grid-column: 1 / -1; +} + +.empty-state p { + margin: 0; + max-width: 42ch; + color: var(--color-text-muted); +} + +/* First paragraph acts as the title. */ +.empty-state p:first-of-type { + font-size: var(--text-lg); + font-weight: var(--weight-semibold); + color: var(--color-text-heading); +} + +/* Call-to-action button spacing. */ +.empty-state .btn { + margin-top: var(--space-4); +} + +/* invisible element, permits building of drag element without altering display */ +.drag-preview { + position: absolute; + top: -9999px; + left: -9999px; + pointer-events: none; + width: 360px; +} diff --git a/frontend/src/lib/styles/legacy/fileManager.css b/frontend/src/lib/styles/legacy/fileManager.css new file mode 100644 index 00000000..dec439b4 --- /dev/null +++ b/frontend/src/lib/styles/legacy/fileManager.css @@ -0,0 +1,17 @@ +/* File-manager page shell — layout concerns specific to the file-manager section. + * Item rendering (grid cards, list rows, drag ghost) lives in resourceList.css. */ + +.files-container { + padding-top: 3px; /* cards animate on hover; sticky header has positive z-index */ +} + +/* Rubber band / lasso selection rectangle */ +.selection-rect { + position: fixed; + border: 1.5px solid var(--primary-color, var(--color-card-drop-border)); + background-color: var(--color-card-drop-tint); + pointer-events: none; + z-index: 1000; + border-radius: 3px; + display: none; +} diff --git a/frontend/src/lib/styles/legacy/resourceList.css b/frontend/src/lib/styles/legacy/resourceList.css new file mode 100644 index 00000000..dc374a65 --- /dev/null +++ b/frontend/src/lib/styles/legacy/resourceList.css @@ -0,0 +1,1087 @@ +/* ============================================================ + * ResourceList component styles + * + * All styles for .file-item (both grid cards and list rows), + * the list header, drag ghost, per-item checkboxes, and + * section-specific item modifiers (.favorite-item, .recent-item, + * .trash-item). + * + * Page-level shell (.files-container, .selection-rect) → fileManager.css + * Batch-action toolbar (.batch-selection-bar, .batch-btn …) → multiSelect.css + * Section page headers (.list-header.favorites-header …) → views/*.css + * ============================================================ */ + +/* ── Base icon container ─────────────────────────────────── */ + +.file-icon { + width: 100px; + height: 70px; + border-radius: var(--radius-lg); + position: relative; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +/* Thumbnail image inside file-icon (grid and list views) */ +.file-icon .file-thumb { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + z-index: 1; + /* The thumbnail is purely decorative — it must never be a hit-test target. + In grid view it fills the whole tile UNDER the overlay controls (checkbox, + favorite star, kebab); a loaded full-bleed thumbnail (promoted to its own + stacking context by the hover scale transform) was capturing clicks meant + for those controls, so they "did nothing" (the click fell through to + open-file). Making it transparent to pointer events routes corner clicks + to the controls and body clicks to the .file-icon (which still opens). */ + pointer-events: none; +} + +/* ── Item states ─────────────────────────────────────────── */ + +.file-item { + background-color: var(--color-item); + /* Smooth the hover/selection tint (list rows used to snap). Grid cards + override this with their own transform/shadow transition. */ + transition: + background-color var(--motion-fast) var(--ease-standard), + border-color var(--motion-fast) var(--ease-standard); +} + +/* Brief pulse on rows that were just optimistically inserted (e.g. + newly created folder, upload completion, drag-drop move into the + current folder). Pure CSS so timing is deterministic — the JS adds + the class, the animation auto-clears the background, and a + single `animationend` listener removes the class. + + `scroll-margin-top` reserves space above the row for the sticky + page header (`.page-sticky-header` ≈ 80 px). Without it, + `scrollIntoView({ block: 'nearest' })` aligns the row's top edge + against the viewport's top edge — which the sticky header is + currently covering — so the user only sees the row's bottom edge. + `scroll-margin-bottom` gives a touch of breathing room when the + scroll happens to land the row near the viewport bottom. */ +.file-item.resource-row--just-added { + animation: resource-row-just-added 1.5s ease-out; + scroll-margin-top: 100px; + scroll-margin-bottom: 24px; +} + +@keyframes resource-row-just-added { + 0% { + background-color: var(--color-success-bg); + } + + 100% { + background-color: transparent; + } +} + +/* Client-only "New" swimlane created on the fly by `addItem()` when + the view is grouped. Subtler styling than a natural-group lane: the + user understands the pin is temporary (it dissolves on next full + reload), so we don't want the bar to dominate the list. The pinned + placement at the top of the container is what makes it + discoverable; the header just confirms the intent. */ +.resource-list__swimlane-group--just-added > .resource-list__swimlane-header { + color: var(--color-success-text); +} + +.file-item.selected { + background-color: var(--color-item-selected); +} + +.file-item:hover { + background-color: var(--color-item-hover); + border-color: var(--color-border-medium); +} + +.file-item.selected:hover { + background-color: var(--color-item-hover-accent); +} + +.file-item.dragging { + opacity: 0.8; + background-color: var(--color-bg-input); +} + +.file-item.drop-target { + background-color: var(--color-warning-ring); +} + +.file-badge-shared { + color: var(--color-badge-blue-text); +} + +.file-item .file-icon > i, +.file-item .file-icon > svg { + position: absolute; + display: flex; + align-items: center; + justify-content: center; +} + +/* ── Per-item checkboxes (list + grid) ───────────────────── */ + +.list-header-checkbox, +.file-item .checkbox-cell { + display: flex; + align-items: center; + justify-content: center; +} + +.list-header-checkbox input[type="checkbox"], +.file-item .checkbox-cell input[type="checkbox"] { + width: 17px; + height: 17px; + cursor: pointer; + accent-color: var(--color-accent); + border-radius: var(--radius-sm); +} + +.list-header.selection-mode { + grid-template-columns: 36px 1fr; + background-color: var(--color-multiselect-bg); + color: var(--color-multiselect-text); + border-bottom-color: var(--color-multiselect-border); +} + +.list-header.selection-mode .list-header-checkbox input[type="checkbox"] { + accent-color: var(--color-accent); +} + +/* ── List view ───────────────────────────────────────────── */ + +.list-header { + display: grid; + grid-template-columns: var(--files-list-columns); + column-gap: var(--space-3); + padding: 15px; + font-weight: var(--weight-semibold); + color: var(--color-text); + background-color: var(--color-bg-subtle); + border-bottom: 1px solid var(--color-border-faint); + align-items: center; +} + +/* Trash view: Name → [Path] → Size → Date → Actions + * Override --files-list-columns so the trash header AND trash items align. + * No checkbox (selectable=false), no owner cell visible, no type column. + * + * Mobile-first: the Path column is hidden by default to keep the layout + * legible on narrow screens (path stays accessible via itemTooltip on hover). + * From 1000px upward, Path reappears and claims roughly half the table + * width via a 3fr share against Name's 1fr. */ +.files-list-view.trash-list { + --files-list-columns: minmax(180px, 1fr) 110px 130px 100px; +} + +.files-list-view.trash-list .file-item .path-cell, +.files-list-view.trash-list .list-header.trash-header > div:nth-child(2) { + display: none; +} + +@media (min-width: 1000px) { + .files-list-view.trash-list { + --files-list-columns: minmax(180px, 1fr) 3fr 110px 130px 100px; + } + + .files-list-view.trash-list .file-item .path-cell, + .files-list-view.trash-list .list-header.trash-header > div:nth-child(2) { + display: block; + } +} + +.list-header > div, +.files-list-view .file-item > div { + min-width: 0; +} + +/* Size column: always nth-child(5) because .owner-cell is always in the DOM + (even when hidden via display:none, it still occupies a child slot). */ +.list-header > div:nth-child(5), +.files-list-view .file-item .size-cell { + justify-self: end; + text-align: right; +} + +/* ── Sortable column headers (flat Drive-style sort) ─────────── */ +.list-header-sort { + display: inline-flex; + align-items: center; + gap: var(--space-1-5); + min-width: 0; + padding: 0; + border: none; + background: none; + cursor: pointer; + /* Inherit the header row's weight + colour so it reads as a header. */ + font: inherit; + color: inherit; + transition: color var(--motion-fast) var(--ease-standard); +} + +.list-header-sort:hover, +.list-header-sort.is-active { + color: var(--color-accent); +} + +.list-header-sort__arrow { + font-size: var(--text-2xs); + color: var(--color-accent); + flex-shrink: 0; +} + +/* Match the value-cell alignment so header and data line up. */ +.list-header-sort[data-sort-field="size"] { + justify-self: end; +} + +.list-header-sort[data-sort-field="modified_at"] { + justify-self: center; +} + +/* ── Owner column ────────────────────────────────────────── */ + +/* Styles applied whenever the cell is visible (hidden class absent). + The .hidden utility class (display:none !important) keeps it invisible + by default — it is stamped directly in the HTML templates. + + `display: flex` lets a nested `.user-vignette` (the common case + for an owner cell — an avatar circle plus a name) become a flex + child that can shrink past its intrinsic content width. The + `.user-vignette__name` already declares + `text-overflow: ellipsis`, but that only fires when the cascade + above it ACTUALLY constrains the width. Without flex here, the + vignette sized to its content and the cell clipped it flat with + no ellipsis. The cell's own `text-overflow` still ellipses + plain-text fallback content (cells without a vignette child). */ +.owner-cell { + color: var(--color-text-secondary); + font-size: var(--text-base); + display: flex; + align-items: center; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Expand the grid track as soon as at least one owner cell is visible. */ +.files-list-view:has(.owner-cell:not(.hidden)) { + --files-list-columns: 36px minmax(200px, 2fr) 120px 100px 110px 130px 72px; +} + +.files-list-view { + --files-list-columns: 36px minmax(200px, 2fr) 100px 110px 130px 72px; + display: flex; + flex-direction: column; + width: 100%; + border-radius: var(--radius-xl); + overflow: hidden; + background-color: var(--color-item); + box-shadow: 0 1px 3px var(--color-shadow-xs); +} + +.files-list-view .file-item { + display: grid; + grid-template-columns: var(--files-list-columns); + column-gap: var(--space-3); + padding: var(--space-3) 15px; + border-bottom: 1px solid var(--color-border-xfaint); + align-items: center; + cursor: pointer; + transition: + background-color var(--motion-fast) var(--ease-standard), + box-shadow var(--motion-fast) var(--ease-standard); +} + +/* "This row" emphasis — a left accent bar on hover/selection (inset shadow, + so it adds no width and never shifts the grid columns). */ +.files-list-view .file-item:hover, +.files-list-view .file-item.selected { + box-shadow: inset 3px 0 0 var(--color-accent); +} + +.files-list-view .file-item.drop-target { + border: 1px dashed var(--color-warning-border); +} + +.files-list-view .file-item .name-cell { + color: var(--color-text); + display: flex; + align-items: center; + gap: var(--space-3); + min-width: 0; + overflow: hidden; +} + +.files-list-view .file-item .name-cell span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Content-search snippet — fragment of the file body that matched the + query. Wraps onto a second line inside the flex name-cell. */ +.files-list-view .file-item .name-cell:has(.file-item__snippet) { + flex-wrap: wrap; + row-gap: 2px; +} + +.files-list-view .file-item .file-item__snippet { + flex-basis: 100%; + /* icon width (36px) + cell gap (12px) — aligns under the name */ + padding-left: 48px; + font-size: 12px; + color: var(--color-text-muted); +} + +/* Grid cards are too compact for body fragments. */ +.files-grid-view .file-item .file-item__snippet { + display: none; +} + +.files-list-view .file-item .file-icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-lg); + /* Hairline inner ring — consistent with the grid thumbnails so light + previews don't bleed into the row. */ + box-shadow: inset 0 0 0 1px var(--color-border); + font-size: var(--text-md); + margin-bottom: 0; + flex-shrink: 0; +} + +.list-header > div:nth-child(5), +.files-list-view .file-item .date-cell { + justify-self: center; + text-align: center; +} + +.files-list-view .file-item .date-cell { + color: var(--color-text-muted); + font-size: var(--text-base); +} + +.files-list-view .file-item .size-cell { + color: var(--color-text-muted); + font-size: var(--text-base); + text-align: right; + font-variant-numeric: tabular-nums; +} + +.files-list-view .file-item .type-cell { + color: var(--color-text-secondary); + font-weight: var(--weight-medium); + font-size: var(--text-base); +} + +.files-list-view .file-item .action-cell { + align-items: center; + text-align: right; +} + +/* Styles for the built-in action-cell buttons (favorite-star, kebab). + * `.btn-action` is excluded — it owns its own colors via the generic + * `.btn-action` rule + variant modifiers (e.g. `.btn-action--delete`). */ +.files-list-view .file-item .action-cell button:not(.btn-action), +.files-list-view .file-item .action-cell div { + display: inline; + width: 28px; + height: 28px; + border-radius: var(--radius-lg); + border: none; + background: transparent; + align-items: center; + justify-content: center; + cursor: pointer; + color: var(--color-text-subtle); + font-size: var(--text-md); +} + +.files-list-view .file-item .action-cell button:not(.btn-action):hover { + background: var(--color-border-subtle); + color: var(--color-text-dark); +} + +/* could be visible if we want */ +.files-list-view .file-item .action-cell button.favorite-star { + display: none; + border: none; +} + +.files-list-view .file-item:hover .action-cell button.favorite-star { + display: inline; +} + +/* Reveal the kebab on hover for cleaner rows — but only on hover-capable + devices, so touch users (no hover) keep it always tappable. Stays visible + on keyboard focus within the row. */ +@media (hover: hover) { + .files-list-view .file-item .action-cell button.file-actions { + opacity: 0; + transition: opacity var(--motion-fast) var(--ease-standard); + } + + .files-list-view .file-item:hover .action-cell button.file-actions, + .files-list-view .file-item:focus-within .action-cell button.file-actions { + opacity: 1; + } +} + +/* ── Grid view ───────────────────────────────────────────── */ + +.files-grid-view { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(var(--grid-card-min), 1fr)); + gap: var(--space-5); + position: relative; +} + +/* header not visible in grid mode */ +.files-grid-view .list-header { + display: none; +} + +/* ── Mobile reflow (≤ --bp-sm 640px) ────────────────────────── + The 6-column grid can't fit a phone, so each list row collapses to a + compact flex line (icon + name … size + actions) and the column header + is hidden (sort controls live in the actions bar). One block covers + files / trash / favorites / recent uniformly; flex ignores the grid + tracks, so there's no header-vs-row misalignment to maintain. */ +@media (max-width: 640px) { + /* Keep the selection-mode header (holds select-all); hide the plain one. */ + .list-header:not(.selection-mode) { + display: none; + } + + .files-list-view .file-item { + display: flex; + align-items: center; + gap: var(--space-3); + } + + .files-list-view .file-item .type-cell, + .files-list-view .file-item .date-cell, + .files-list-view .file-item .owner-cell, + .files-list-view .file-item .path-cell { + display: none; + } + + .files-list-view .file-item .name-cell { + flex: 1; + min-width: 0; + } + + .files-list-view .file-item .checkbox-cell, + .files-list-view .file-item .size-cell, + .files-list-view .file-item .action-cell { + flex-shrink: 0; + } + + /* Tighter grid on phones (2 columns instead of 1). */ + .files-grid-view { + --grid-card-min: 140px; + gap: var(--space-2); + } +} + +.files-grid-view .file-item { + border-radius: var(--radius-2xl); + /* Hairline at rest → accent on hover (gallery, not form). */ + border: 1px solid var(--color-border); + padding: var(--space-3); + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 0 1px 2px var(--color-shadow-xs); + cursor: pointer; + width: 100%; + min-height: 160px; + position: relative; + transition: + transform var(--motion-base) var(--ease-standard), + box-shadow var(--motion-base) var(--ease-standard), + border-color var(--motion-base) var(--ease-standard); +} + +.files-grid-view .file-item.dragging { + opacity: 0.5; + transform: scale(0.95); + box-shadow: none; +} + +.files-grid-view .file-item:hover { + transform: translateY(-3px); + box-shadow: 0 12px 28px -8px var(--color-shadow-md); + border-color: var(--color-accent); +} + +/* The thumbnail subtly zooms inside its clipped tile on hover — the "alive" + feel of a premium gallery. */ +.files-grid-view .file-item .file-icon .file-thumb { + transition: transform var(--motion-moderate) var(--ease-standard); +} + +.files-grid-view .file-item:hover .file-icon .file-thumb { + transform: scale(1.04); +} + +.files-grid-view .file-item.selected { + border-color: var(--color-accent); + box-shadow: + 0 0 0 1px var(--color-accent-ring-dark), + 0 4px 12px var(--color-accent-ring); +} + +.files-grid-view .file-item.selected:hover { + box-shadow: + 0 0 0 1px var(--color-accent-ring-strong), + 0 6px 18px var(--color-accent-ring-dark); +} + +/* elements hidden in grid view — the type label ("Imagen") is redundant under + a thumbnail, so we drop it and surface the file size instead. */ +.files-grid-view .file-item .date-cell, +.files-grid-view .file-item .type-cell, +.files-grid-view .file-item .owner-cell { + display: none; +} + +/* Selection checkbox */ +/* Overlay controls sit ON the thumbnail (offset by the card padding) over a + frosted scrim, so they read as part of the media instead of floating on the + card corner. */ +.files-grid-view .file-item .checkbox-cell { + position: absolute; + top: calc(var(--space-3) + 8px); + left: calc(var(--space-3) + 8px); + width: 26px; + height: 26px; + border-radius: var(--radius-md); + background: var(--color-scrim-control); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + box-shadow: 0 1px 3px var(--color-shadow-sm); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + z-index: 10; + cursor: pointer; + transition: opacity var(--motion-fast) var(--ease-standard); +} + +/* Visible whenever the card is hovered OR already selected, so the checked + state stays on screen after the pointer leaves. */ +.files-grid-view .file-item:hover .checkbox-cell, +.files-grid-view .file-item.selected .checkbox-cell { + opacity: 1; +} + +/* Custom-drawn checkbox: an empty white box that fills with accent and shows a + crisp white tick when checked. The native 17px control was invisible once the + cell turned accent on selection (accent-on-accent), and the intended tick + targeted a `.checkbox-cell i` the markup never renders — so a click read as + "nothing happened". This makes the checked state unmistakable. */ +.files-grid-view .file-item .checkbox-cell input[type="checkbox"] { + appearance: none; + -webkit-appearance: none; + width: 18px; + height: 18px; + margin: 0; + border: 2px solid var(--color-border-medium); + border-radius: var(--radius-sm); + background: var(--color-bg-surface); + display: grid; + place-content: center; + cursor: pointer; + transition: + background-color var(--motion-fast) var(--ease-standard), + border-color var(--motion-fast) var(--ease-standard); +} + +.files-grid-view .file-item .checkbox-cell input[type="checkbox"]::after { + content: ""; + width: 5px; + height: 9px; + margin-top: -1px; + border: solid var(--color-danger-text); + border-width: 0 2px 2px 0; + transform: rotate(45deg) scale(0); + transform-origin: center; + transition: transform var(--motion-fast) var(--ease-standard); +} + +.files-grid-view .file-item .checkbox-cell input[type="checkbox"]:checked { + background: var(--color-accent); + border-color: var(--color-accent); +} + +.files-grid-view .file-item .checkbox-cell input[type="checkbox"]:checked::after { + transform: rotate(45deg) scale(1); +} + +.files-grid-view .file-item .checkbox-cell input[type="checkbox"]:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + +/* More actions button (three dots) — top-right of the thumbnail on a scrim. */ +.files-grid-view .file-item .file-actions { + position: absolute; + top: calc(var(--space-3) + 8px); + right: calc(var(--space-3) + 8px); + width: 30px; + height: 30px; + border-radius: var(--radius-full); + border: none; + background: var(--color-scrim-control); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + box-shadow: 0 1px 3px var(--color-shadow-sm); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + z-index: 10; + cursor: pointer; + color: var(--color-text); + font-size: var(--text-md); + transition: opacity var(--motion-fast) var(--ease-standard); +} + +.files-grid-view .file-item:hover .file-actions { + opacity: 1; +} + +.files-grid-view .file-item .file-actions:hover { + color: var(--color-accent); +} + +.files-grid-view .file-item.drop-target { + background-color: var(--color-warning-ring); + border: 2px dashed var(--color-warning-border); +} + +/* "Shared" indicator — top-left of the thumbnail. Sits below the checkbox + (which only appears on hover), so the two never both compete for the eye. */ +.files-grid-view .file-item .file-badge-shared { + position: absolute; + top: calc(var(--space-3) + 8px); + left: calc(var(--space-3) + 8px); + width: 24px; + height: 24px; + border-radius: var(--radius-full); + border: none; + background: var(--color-scrim-control); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + box-shadow: 0 1px 3px var(--color-shadow-sm); + display: flex; + align-items: center; + justify-content: center; + z-index: 9; + font-size: var(--text-2xs); + padding: 0; + line-height: var(--leading-none); +} + +/* Favorite star — top-right of the thumbnail, left of the kebab, on a scrim. */ +.files-grid-view .file-item button.favorite-star { + position: absolute; + top: calc(var(--space-3) + 8px); + right: calc(var(--space-3) + 8px + 34px); + width: 30px; + height: 30px; + border-radius: var(--radius-full); + border: none; + background: var(--color-scrim-control); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + box-shadow: 0 1px 3px var(--color-shadow-sm); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + z-index: 12; + cursor: pointer; + color: var(--color-text-subtle); + font-size: 15px; + padding: 0; + line-height: var(--leading-none); + transition: opacity var(--motion-fast) var(--ease-standard); +} + +.files-grid-view .file-item:hover button.favorite-star { + opacity: 1; +} + +.files-grid-view .file-item button.favorite-star:hover { + color: var(--color-star-text); +} + +.files-grid-view .file-item button.favorite-star.active { + opacity: 1; + color: var(--color-star-text-hover); +} + +.files-grid-view .file-item button.favorite-star.active:hover { + color: var(--color-star-active); +} + +.files-grid-view .file-item .name-cell { + font-size: var(--text-sm); + font-weight: var(--weight-medium); + text-align: center; + margin-bottom: 2px; + color: var(--color-text); + /* Span the full card width. The card is a centered flex column + (`align-items: center`), so without an explicit width the name-cell — + which wraps BOTH the thumbnail tile and the filename — shrink-wraps to + the filename's length. That made `.file-icon { width: 100% }` track the + NAME width, so a long name ("CURSO TERRAFORM") rendered a big tile and a + short one ("Idiomas") a small one. Forcing 100% makes every thumbnail + tile identical regardless of name length. */ + width: 100%; + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: var(--space-1); +} + +.files-grid-view .file-item .name-cell span { + display: block; + max-width: 100%; + /* Ellipsis must live on the span (the text node), not the flex parent, + or long names clip with no "…". */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-top: var(--space-2); + padding-bottom: var(--space-2); +} + +.files-grid-view .file-item .name-cell svg.favorite-star-inline { + display: none; +} + +/* The grid card's secondary line is now the combined .grid-meta block + ("hace 2 días · 4,31 MB"), so the standalone size-cell is hidden here. */ +.files-grid-view .file-item .size-cell { + display: none; +} + +/* ── Grid metadata line — recency + size (+ owner avatar when shared) ────── + Replaces the lone "4,31 MB": a top file app captions a thumbnail with WHEN + it was touched (the dominant retrieval cue), not just bytes. Hidden + everywhere except the grid — list view keeps its own date/size/owner + columns, so a display:none here drops it cleanly out of the row grid. */ +.grid-meta { + display: none; +} + +.files-grid-view .file-item .grid-meta { + display: flex; + align-items: center; + justify-content: center; + gap: var(--space-1); + max-width: 100%; + margin-top: 2px; + font-size: var(--text-2xs); + color: var(--color-text-muted); + line-height: var(--leading-snug); +} + +/* On a narrow card the relative date truncates first; the size never clips. */ +.files-grid-view .file-item .grid-meta__date { + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.files-grid-view .file-item .grid-meta__size { + flex: 0 0 auto; + color: var(--color-text-faint); +} + +.files-grid-view .file-item .grid-meta__size::before { + content: "·"; + margin-right: var(--space-1); + color: var(--color-text-faint); +} + +/* Owner avatar — only rendered when the item is shared; sits at the line end. */ +.files-grid-view .file-item .grid-meta .user-vignette { + flex: 0 0 auto; + margin-left: var(--space-1); +} + +/* Full-width thumbnail tile with a fixed aspect ratio — the image + (`.file-thumb`, object-fit:cover) fills it edge-to-edge for a uniform, + rich grid instead of a small letterboxed preview. Non-image files show + their type icon centred on the placeholder fill. */ +.files-grid-view .file-item .file-icon { + width: 100%; + height: auto; + aspect-ratio: 4 / 3; + border-radius: var(--radius-lg); + background: var(--color-bg-input); + /* Hairline inner ring so light thumbnails don't bleed into the card. */ + box-shadow: inset 0 0 0 1px var(--color-border); + margin: 0 0 var(--space-3); + font-size: 30px; +} + +/* Type icon for non-thumbnail files (documents, etc.) — perfectly centered in + the tile. `inset: 0` + `margin: auto` + a fixed size is the absolute-centering + trick; the previous `top: auto` broke the vertical half, bottom-anchoring the + icon so it sat low in the tile. */ +.files-grid-view .file-item .file-icon > i, +.files-grid-view .file-item .file-icon > svg { + inset: 0; + margin: auto; + width: 56px; + height: 56px; +} + +/* ── Drag ghost ──────────────────────────────────────────── */ + +.dragged-items { + --dragged-files-list-columns: 36px minmax(200px, 1fr); + flex-direction: column; + background-color: transparent; + overflow: hidden; + border-radius: var(--radius-xl); + width: 100%; +} + +.dragged-items .file-item { + grid-template-columns: var(--dragged-files-list-columns); + column-gap: var(--space-3); + align-items: center; + background-color: var(--color-item); + display: flex; + padding: var(--space-1); + width: 360px; + height: 46px; + color: var(--color-text); +} + +/* file name text is truncated */ +.dragged-items .file-item div:nth-child(2) { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.dragged-items .file-item .file-icon { + width: 36px; + height: 36px; +} + +.dragged-items div.fading { + -webkit-mask-image: linear-gradient(to bottom, var(--color-item) 10%, transparent); + mask-image: linear-gradient(to bottom, var(--color-item) 10%, transparent); +} + +.dragged-items-badge { + position: absolute; + top: 0; + right: 0; + transform: translate(50%, -50%); + background: var(--color-notification-badge); + color: var(--color-danger-text); + border-radius: 50%; + min-width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + font-size: var(--text-xs); + font-weight: var(--weight-bold); + padding: var(--space-0-5); +} + +/* ── Swimlane group header ───────────────────────────────── */ + +/* Spans the full grid width in list view; no-op in grid view since + grid wraps it naturally. */ +.resource-list__swimlane-header { + grid-column: 1 / -1; + padding: var(--space-1-5) var(--space-3) var(--space-1); + font-size: 0.72rem; + font-weight: var(--weight-semibold); + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--color-text-faint); + border-bottom: 1px solid var(--color-border); + margin-top: var(--space-2); + cursor: default; + user-select: none; +} + +.resource-list__swimlane-header:first-child, +.list-header + .resource-list__swimlane-header { + margin-top: 0; +} + +/* When the header contains a rich DOM node (e.g. a user vignette for the + "owner" group-by), reset the typographic overrides that only make sense + for plain-text labels, and lay the node out inline. */ +.resource-list__swimlane-header--node { + display: flex; + align-items: center; + padding: var(--space-1) var(--space-3); + text-transform: none; + letter-spacing: normal; + font-size: inherit; + font-weight: var(--weight-normal); + color: inherit; +} + +/* ── Swimlane group card (list view only) ────────────────── */ + +/* When swimlane groups are present, dissolve the outer container into the + page background so each group card reads as its own panel. */ +.files-list-view:has(.resource-list__swimlane-group) { + background-color: transparent; + box-shadow: none; + border-radius: 0; + overflow: visible; + gap: var(--space-2-5); +} + +/* Each group is a self-contained card */ +.files-list-view .resource-list__swimlane-group { + background-color: var(--color-item); + border-radius: var(--radius-xl); + box-shadow: 0 1px 3px var(--color-shadow-xs); + overflow: hidden; +} + +/* The header inside a group card is always first — no extra top margin */ +.files-list-view .resource-list__swimlane-group .resource-list__swimlane-header { + margin-top: 0; +} + +/* ── Swimlane group wrapper (grid view) ──────────────────── */ + +/* The group wrapper must be transparent to the grid so .file-item children + continue to flow in the parent's columns (subgrid mirrors the column tracks). + The wrapper spans the full row width; items inside fill the columns naturally. */ +.files-grid-view .resource-list__swimlane-group { + grid-column: 1 / -1; + display: grid; + grid-template-columns: subgrid; + gap: var(--space-5); +} + +/* ── Section item modifiers ──────────────────────────────── */ + +/* — Favorites — */ + +.file-item.favorite-item { + border-left: 3px solid var(--color-warning-border); + + [dir="rtl"] & { + border-right: 3px solid var(--color-warning-border); + border-left: unset; + } +} + +/* list-view column override */ +.file-item.favorite-item { + position: relative; + grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px; +} + +.file-item.favorite-item .favorite-indicator { + position: relative; + top: 0; + right: 0; + width: 30px; + height: 30px; +} + +/* — Recent — */ + +.files-grid-view .file-item.recent-item { + border-left: 3px solid var(--color-recent-border); + + [dir="rtl"] & { + border-right: 3px solid var(--color-recent-border); + border-left: unset; + } +} + +/* list-view column override */ +.file-item.recent-item { + position: relative; + grid-template-columns: 30px minmax(200px, 2fr) 1fr 1fr 120px; +} + +.file-item.recent-item .recent-indicator { + position: relative; + top: 0; + right: 0; + width: 30px; + height: 30px; +} + +/* ── Path column (opt-in via ResourceListConfig.showPath) ────────── + * Visible in list view only — grid cards hide it because they have no + * dedicated column slot. itemTooltip still surfaces the path on hover. + */ +.files-list-view .file-item .path-cell { + color: var(--color-text-secondary); + font-size: var(--text-sm); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.files-grid-view .file-item .path-cell { + display: none; +} + +/* ── Custom inline actions (ResourceListConfig.customActions) ────── + * Always visible in both list and grid view — used by trash for the + * restore / permanent-delete buttons that must be one click away. + */ +.btn-action { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: var(--radius-lg); + border: none; + background: transparent; + cursor: pointer; + color: var(--color-text-subtle); + font-size: var(--text-md); + padding: 0; +} + +.btn-action:hover { + background: var(--color-border-subtle); + color: var(--color-text-dark); +} + +.files-grid-view .file-item .btn-action { + margin-top: var(--space-1); +} diff --git a/frontend/src/lib/styles/legacy/sidebar.css b/frontend/src/lib/styles/legacy/sidebar.css new file mode 100644 index 00000000..d909369f --- /dev/null +++ b/frontend/src/lib/styles/legacy/sidebar.css @@ -0,0 +1,258 @@ +/* Sidebar */ +.sidebar { + width: var(--sidebar-width); + background: linear-gradient(180deg, var(--color-sidebar-bg-from) 0%, var(--color-sidebar-bg-to) 100%); + color: var(--color-sidebar-text-active); + display: flex; + flex-direction: column; + height: 100%; + flex-shrink: 0; + box-shadow: 2px 0 12px var(--color-shadow-md); + transition: transform var(--motion-slow) var(--ease-emphasized); +} + +/* Sidebar overlay for mobile */ +.sidebar-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--color-sidebar-overlay); + z-index: 998; + opacity: 0; + transition: opacity 0.3s ease; +} + +.sidebar-overlay.active { + display: block; + opacity: 1; +} + +/* Mobile responsive styles */ +@media (max-width: 768px) { + .sidebar { + position: fixed; + left: 0; + top: 0; + z-index: 999; + transform: translateX(-100%); + } + + .sidebar.open { + transform: translateX(0); + } + + [dir="rtl"] .sidebar { + left: auto; + right: 0; + transform: translateX(100%); + } + + [dir="rtl"] .sidebar.open { + transform: translateX(0); + } +} + +.logo-container { + padding: 22px var(--space-5); + display: flex; + align-items: center; + border-bottom: 1px solid var(--color-sidebar-separator); + margin-bottom: var(--space-2); + text-decoration: none; + color: inherit; +} + +.logo { + width: 40px; + height: 40px; + background: var(--color-sidebar-logo-gradient); + border-radius: var(--radius-2xl); + display: flex; + align-items: center; + justify-content: center; + margin-right: var(--space-3); + box-shadow: 0 3px 10px var(--color-sidebar-shadow); + transition: + transform 0.2s, + box-shadow 0.2s; + + [dir="rtl"] & { + margin-left: var(--space-3); + margin-right: unset; + } +} + +.logo:hover { + transform: scale(1.05); + box-shadow: 0 4px 14px var(--color-sidebar-shadow-lg); +} + +.logo svg { + width: 22px; + height: 22px; + fill: var(--color-sidebar-text-active); +} + +.app-name { + font-size: 19px; + font-weight: var(--weight-bold); + color: var(--color-sidebar-text-active); + letter-spacing: 0.3px; +} + +.nav-menu { + display: flex; + flex-direction: column; + flex-grow: 1; + padding: var(--space-2) var(--space-3); + gap: var(--space-0-5); +} + +.nav-item { + display: flex; + align-items: center; + width: 100%; + padding: 11px var(--space-3-5); + border-radius: var(--radius-xl); + cursor: pointer; + color: var(--color-sidebar-text); + font-size: 14.5px; + font-weight: var(--weight-medium); + /* Button resets — .nav-item is a + + + + {#if tab === 'users'} +
    + +
    + {#if usersError} +

    {usersError}

    + {:else} + + + + + + + + + + + + {#each users as u (u.id)} + + + + + + + + {/each} + +
    {t('admin.user', 'User')}{t('admin.role', 'Role')}{t('admin.status', 'Status')}{t('admin.quota', 'Quota')}
    +
    + {u.username || u.email} + {u.email} +
    +
    {u.role}{u.active ? t('admin.active', 'Active') : t('admin.inactive', 'Inactive')} + {u.storage_quota_bytes > 0 ? formatBytes(u.storage_quota_bytes) : '∞'} + + + + + + +
    +
    + + {pageIndex + 1} / {Math.max(1, Math.ceil(total / PAGE_SIZE))} + +
    + {/if} + {:else if !pluginsAvailable} +

    {t('admin.plugins_disabled', 'The plugin subsystem is disabled.')}

    + {:else if pluginsError} +

    {pluginsError}

    + {:else if plugins.length === 0} +

    {t('admin.no_plugins', 'No plugins installed.')}

    + {:else} + + + + + + + + + + + {#each plugins as p (p.id)} + + + + + + + {/each} + +
    {t('admin.plugin', 'Plugin')}{t('admin.version', 'Version')}{t('admin.status', 'Status')}
    +
    + {p.name} + {#if p.description}{p.description}{/if} +
    +
    {p.version ?? '—'}{p.enabled ? t('admin.enabled', 'Enabled') : t('admin.disabled', 'Disabled')} + + +
    + {/if} +
    + + +
    + + + + + +
    + {#snippet footer()} + + + {/snippet} +
    + + diff --git a/frontend/src/routes/device/+page.svelte b/frontend/src/routes/device/+page.svelte new file mode 100644 index 00000000..34c07d41 --- /dev/null +++ b/frontend/src/routes/device/+page.svelte @@ -0,0 +1,177 @@ + + +{t('device.title', 'Device verification')} · OxiCloud + +
    +
    +

    {t('device.title', 'Device verification')}

    + + {#if step === 'code'} +
    + + +
    + {:else if step === 'loading'} +

    {t('common.loading', 'Loading…')}

    + {:else if step === 'review'} +
    +
    {t('device.client', 'Application')}
    +
    {info?.client_name || t('device.unknown', 'Unknown')}
    +
    {t('device.scopes', 'Access')}
    +
    {info?.scopes || 'all'}
    +
    +
    + + +
    + {:else if step === 'approved'} +

    + {t('device.approved', 'Device approved. You can return to your device.')} +

    + {:else if step === 'denied'} +

    {t('device.denied', 'Device access denied.')}

    + {:else if step === 'error'} + + + {/if} +
    +
    + + diff --git a/frontend/src/routes/favorites/+page.svelte b/frontend/src/routes/favorites/+page.svelte new file mode 100644 index 00000000..79bb078d --- /dev/null +++ b/frontend/src/routes/favorites/+page.svelte @@ -0,0 +1,88 @@ + + +{t('nav.favorites', 'Favorites')} · OxiCloud + +

    {t('nav.favorites', 'Favorites')}

    + + load(false)} +> + {#each items as item (item.resource.id)} + + {#snippet actions()} + + {/snippet} + + {/each} + + + diff --git a/frontend/src/routes/files/[...path]/+page.svelte b/frontend/src/routes/files/[...path]/+page.svelte new file mode 100644 index 00000000..37798b71 --- /dev/null +++ b/frontend/src/routes/files/[...path]/+page.svelte @@ -0,0 +1,398 @@ + + +{t('nav.files', 'Files')} · OxiCloud + +
    { + e.preventDefault(); + dragOver = true; + }} + ondragleave={() => (dragOver = false)} + ondrop={onDrop} +> +
    + + +
    +
    + + + +
    + +
    + + +
    +
    +
    + + {#if error} +

    {error}

    + {:else if loading && isEmpty} +

    {t('common.loading', 'Loading…')}

    + {:else if isEmpty} +
    +

    {t('files.empty_title', 'This folder is empty')}

    +

    {t('files.empty_hint', 'Drop files here or use the Upload button to add files.')}

    +
    + {:else} +
    +
    +
    +
    {t('files.col_name', 'Name')}
    +
    {t('files.col_type', 'Type')}
    +
    {t('files.col_size', 'Size')}
    +
    {t('files.col_modified', 'Modified')}
    +
    +
    + + {#each listing.folders as folder (folder.id)} +
    openFolder(folder)} + onclick={() => openFolder(folder)} + onkeydown={(e) => e.key === 'Enter' && openFolder(folder)} + > +
    + + {folder.name} +
    +
    {t('files.folder', 'Folder')}
    +
    —
    +
    {formatDate(folder.modified_at)}
    +
    +
    + + +
    +
    + {/each} + + {#each listing.files as file (file.id)} +
    openFile(file)} + onclick={() => openFile(file)} + onkeydown={(e) => e.key === 'Enter' && openFile(file)} + > +
    + + {file.name} +
    +
    {file.category || t('files.file', 'File')}
    +
    {file.size != null ? formatBytes(file.size) : ''}
    +
    {formatDate(file.modified_at)}
    +
    + {#if file.size != null}{formatBytes(file.size)}{/if} +
    +
    + e.stopPropagation()}> + + +
    +
    + {/each} +
    +
    + {/if} +
    + + diff --git a/frontend/src/routes/groups/+page.svelte b/frontend/src/routes/groups/+page.svelte new file mode 100644 index 00000000..f4280cd9 --- /dev/null +++ b/frontend/src/routes/groups/+page.svelte @@ -0,0 +1,306 @@ + + +{t('nav.groups', 'Groups')} · OxiCloud + +
    +
    +

    {t('nav.groups', 'Groups')}

    + +
    + + {#if error} +

    {error}

    + {:else if loading} +

    {t('common.loading', 'Loading…')}

    + {:else if groups.length === 0} +

    {t('groups.empty', 'No groups yet.')}

    + {:else} +
      + {#each groups as g (g.id)} +
    • +
      + +
      + + +
      +
      + + {#if expandedId === g.id} +
      +
      +

      {t('groups.members', 'Members')}

      + +
      + {#if members.length === 0} +

      {t('groups.no_members', 'No members.')}

      + {:else} +
        + {#each members as m (m.user_id ?? m.group_id)} +
      • + {m.email ?? m.name ?? m.user_id ?? m.group_id} + +
      • + {/each} +
      + {/if} +
      + {/if} +
    • + {/each} +
    + {/if} +
    + + diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte new file mode 100644 index 00000000..17c36aa5 --- /dev/null +++ b/frontend/src/routes/login/+page.svelte @@ -0,0 +1,107 @@ + + + + {t('app.title', 'OxiCloud')} + + +
    +
    + + +

    {t('auth.sign_in', 'Sign in')}

    + + {#if page.url.searchParams.get('source') === 'session_expired'} +
    + {t('auth.session_expired', 'Your session expired. Please sign in again.')} +
    + {/if} + + {#if error} + + {/if} + +
    +
    + + +
    + +
    + + +
    + + +
    +
    +
    diff --git a/frontend/src/routes/music/+page.svelte b/frontend/src/routes/music/+page.svelte new file mode 100644 index 00000000..d2d3eb7c --- /dev/null +++ b/frontend/src/routes/music/+page.svelte @@ -0,0 +1,360 @@ + + +{t('nav.music', 'Music')} · OxiCloud + +
    + + +
    + {#if current} +
    +

    {current.name}

    +
    + {#if tracks.length === 0} +

    {t('music.empty_playlist', 'This playlist has no tracks yet.')}

    + {:else} +
      + {#each tracks as track, i (track.id)} +
    • onDragStart(i)} + ondragover={(e) => onDragOver(e, i)} + ondrop={onDrop} + ondragend={() => (dragIndex = null)} + > + + + {trackLabel(track)} + {#if track.artist}{track.artist}{/if} + +
    • + {/each} +
    + {/if} + + {#if nowPlaying} + + {/if} + {:else} +

    {t('music.select_playlist', 'Select a playlist.')}

    + {/if} +
    +
    + + diff --git a/frontend/src/routes/nextcloud/error/+page.svelte b/frontend/src/routes/nextcloud/error/+page.svelte new file mode 100644 index 00000000..60c40da8 --- /dev/null +++ b/frontend/src/routes/nextcloud/error/+page.svelte @@ -0,0 +1,49 @@ + + +{t('nextcloud.error_title', 'Something went wrong')} · OxiCloud + +
    + +

    {t('nextcloud.error_title', 'Something went wrong')}

    +

    + {t( + 'nextcloud.error_body', + 'The connection could not be completed. Please try again from your application.' + )} +

    + {#if reason}

    {reason}

    {/if} +
    + + diff --git a/frontend/src/routes/nextcloud/login/+page.svelte b/frontend/src/routes/nextcloud/login/+page.svelte new file mode 100644 index 00000000..53457a93 --- /dev/null +++ b/frontend/src/routes/nextcloud/login/+page.svelte @@ -0,0 +1,129 @@ + + +{t('app.title', 'OxiCloud')} + +
    +
    +

    {t('nextcloud.grant_title', 'Grant access')}

    + + {#if !validToken} +

    {t('nextcloud.invalid_token', 'Invalid session token.')}

    + {:else} + {#if passwordLoginEnabled} +
    + + + +
    + {/if} + + {#if oidcEnabled} + + {/if} + {/if} +
    +
    + + diff --git a/frontend/src/routes/nextcloud/success/+page.svelte b/frontend/src/routes/nextcloud/success/+page.svelte new file mode 100644 index 00000000..d6437217 --- /dev/null +++ b/frontend/src/routes/nextcloud/success/+page.svelte @@ -0,0 +1,34 @@ + + +{t('nextcloud.success_title', 'Access granted')} · OxiCloud + +
    + +

    {t('nextcloud.success_title', 'Access granted')}

    +

    {t('nextcloud.success_body', 'You can now return to your application — it is connected.')}

    +
    + + diff --git a/frontend/src/routes/photos/+page.svelte b/frontend/src/routes/photos/+page.svelte new file mode 100644 index 00000000..67e8ad87 --- /dev/null +++ b/frontend/src/routes/photos/+page.svelte @@ -0,0 +1,113 @@ + + +{t('nav.photos', 'Photos')} · OxiCloud + +

    {t('nav.photos', 'Photos')}

    + +{#if error} + +{:else if items.length === 0 && exhausted} +

    {t('photos.empty', 'No photos yet.')}

    +{:else} +
      + {#each items as photo (photo.id)} +
    • + + {photo.name} + +
    • + {/each} +
    +{/if} + + +{#if loading}

    {t('common.loading', 'Loading…')}

    {/if} + + diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte new file mode 100644 index 00000000..64d77e65 --- /dev/null +++ b/frontend/src/routes/profile/+page.svelte @@ -0,0 +1,212 @@ + + +{t('nav.profile', 'Profile')} · OxiCloud + +
    +

    {t('nav.profile', 'Profile')}

    + + {#if session.user} + + +
    +

    {t('profile.details', 'Profile details')}

    + + + + + + + + +
    + + {#if session.user.can_edit_image !== false && session.user.auth_provider === 'local'} +
    +

    {t('profile.change_password', 'Change password')}

    + + + + +
    + {/if} + {:else} +

    {t('common.loading', 'Loading…')}

    + {/if} +
    + + diff --git a/frontend/src/routes/recent/+page.svelte b/frontend/src/routes/recent/+page.svelte new file mode 100644 index 00000000..2e442a56 --- /dev/null +++ b/frontend/src/routes/recent/+page.svelte @@ -0,0 +1,85 @@ + + +{t('nav.recent', 'Recent')} · OxiCloud + +

    {t('nav.recent', 'Recent')}

    + + load(false)} +> + {#snippet toolbar()} + {#if items.length > 0} + + {/if} + {/snippet} + + {#each items as item (item.resource.id + item.accessed_at)} + + {/each} + + + diff --git a/frontend/src/routes/s/[token]/+page.svelte b/frontend/src/routes/s/[token]/+page.svelte new file mode 100644 index 00000000..60820f7e --- /dev/null +++ b/frontend/src/routes/s/[token]/+page.svelte @@ -0,0 +1,294 @@ + + +{meta?.item_name ?? t('share.title', 'Shared')} · OxiCloud + +
    + {#if view === 'loading'} + + {:else if view === 'expired'} + + {:else if view === 'password'} + + {:else if view === 'file'} + + {:else if view === 'folder' && listing} + + + {#if listing.folders.length === 0 && listing.files.length === 0} + + {/if} + + {#if listing.folders.length > 0} + + + {/if} + + {#if listing.files.length > 0} + + + {/if} + {/if} +
    + + diff --git a/frontend/src/routes/shared-with-me/+page.svelte b/frontend/src/routes/shared-with-me/+page.svelte new file mode 100644 index 00000000..c8900797 --- /dev/null +++ b/frontend/src/routes/shared-with-me/+page.svelte @@ -0,0 +1,62 @@ + + +{t('nav.shared_with_me', 'Shared with me')} · OxiCloud + +

    {t('nav.shared_with_me', 'Shared with me')}

    + + load(false)} +> + {#each items as item (item.resource.id)} + + {/each} + + + diff --git a/frontend/src/routes/shared/+page.svelte b/frontend/src/routes/shared/+page.svelte new file mode 100644 index 00000000..d0507a80 --- /dev/null +++ b/frontend/src/routes/shared/+page.svelte @@ -0,0 +1,62 @@ + + +{t('nav.shared', 'Shared')} · OxiCloud + +

    {t('nav.shared', 'Shared')}

    + + load(false)} +> + {#each items as item (item.resource.id)} + + {/each} + + + diff --git a/frontend/src/routes/trash/+page.svelte b/frontend/src/routes/trash/+page.svelte new file mode 100644 index 00000000..cb59a99a --- /dev/null +++ b/frontend/src/routes/trash/+page.svelte @@ -0,0 +1,127 @@ + + +{t('nav.trash', 'Trash')} · OxiCloud + +

    {t('nav.trash', 'Trash')}

    + + load(false)} +> + {#snippet toolbar()} + {#if items.length > 0} + + {/if} + {/snippet} + + {#each items as item (item.resource.id)} + + {#snippet actions()} + + + {/snippet} + + {/each} + + + diff --git a/frontend/static/.gitkeep b/frontend/static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/frontend/static/locales/ar.json b/frontend/static/locales/ar.json new file mode 100644 index 00000000..a1884660 --- /dev/null +++ b/frontend/static/locales/ar.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "لم يعد رابط تسجيل الدخول صالحًا", + "expired_body": "ربما انتهت صلاحية الرابط أو تم استخدامه بالفعل. يمكننا إرسال رابط جديد لك — سيصل إلى صندوق الوارد خلال ثوانٍ.", + "resend_to": "أرسل رابطًا جديدًا إلى {{email}}", + "generic_unavailable": "لم يعد رابط تسجيل الدخول صالحًا. ربما تم استخدامه بالفعل أو انتهت صلاحيته. اطلب رابطًا جديدًا من صفحة تسجيل الدخول.", + "service_unavailable": "تسجيل الدخول عبر الرابط السحري غير مفعّل على هذا الخادم.", + "internal_error": "حدث خطأ أثناء تسجيل الدخول. يُرجى المحاولة مرة أخرى.", + "resend_failure": "حدث خطأ أثناء إرسال الرابط. يُرجى المحاولة مرة أخرى.", + "cross_browser_title": "هل تريد متابعة تسجيل الدخول على هذا الجهاز؟", + "cross_browser_body": "لقد فتحت رابط تسجيل الدخول في متصفح أو جهاز مختلف عن الجهاز الذي طلبته منه.", + "cross_browser_warning": "إذا كنت قد طلبت هذا الرابط، فمن الآمن المتابعة. إذا لم تطلبه، أغلق هذه الصفحة — النقر على «متابعة» سيُسجّل دخول شخص آخر إلى حسابك.", + "cross_browser_continue": "متابعة وتسجيل الدخول", + "resend_confirmation_title": "تحقق من صندوق الوارد", + "resend_confirmation_body": "إذا كان رابط تسجيل الدخول ينتمي إلى حساب نشط، فقد تم للتو إرسال رابط جديد. يُرجى التحقق من صندوق الوارد.", + "return_link": "العودة إلى OxiCloud" + }, + "email": { + "invitation": { + "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", + "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتحه بالنقر على الرابط أدناه:\n{{link}}\n\nيعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_hours}} ساعة.\nإذا لم تكن تتوقع هذه الدعوة، يمكنك تجاهل هذه الرسالة.\n\n— OxiCloud" + }, + "login": { + "subject": "تسجيل الدخول إلى OxiCloud", + "body": "مرحبًا،\n\nاستخدم الرابط أدناه لتسجيل الدخول إلى OxiCloud. يعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_minutes}} دقيقة. افتحه على الجهاز نفسه الذي طلبت منه الرابط.\n\n{{link}}\n\nإذا لم تطلب رابط تسجيل الدخول هذا، يمكنك تجاهل هذه الرسالة — لا حاجة لأي إجراء إضافي.\n\n— OxiCloud" + }, + "kind_file": "ملف", + "kind_folder": "مجلد", + "english_fallback_divider": "--- النسخة الإنجليزية أدناه ---" + } + }, + "notification": { + "share": { + "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", + "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتح OxiCloud لعرض مشاركتك الجديدة:\n{{login_link}}\n\nقد تكون لديك مشاركات جديدة أخرى من {{inviter}} — سجّل الدخول لرؤية جميع العناصر المشاركة معك.\n\n— OxiCloud\n\nأنت تتلقى هذه الرسالة لأن لديك حسابًا في OxiCloud وتفضيل إشعارات المشاركة مُفعّل. يمكنك تعطيله من ملفك الشخصي (راسلني عندما يشاركني شخص ما)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "نظام تخزين سحابي بسيط" + }, + "nav": { + "files": "الملفات", + "shared": "مشاركاتي", + "recent": "الأخيرة", + "favorites": "المفضلة", + "photos": "الصور", + "music": "الموسيقى", + "trash": "سلة المهملات", + "sharedwithme": "مشتركة معي" + }, + "photos": { + "empty_state": "لا توجد صور بعد", + "empty_hint": "ارفع صوراً أو مقاطع فيديو لعرضها هنا", + "items_selected": "محدد", + "view_daily": "يوم", + "view_monthly": "شهر", + "view_yearly": "سنة" + }, + "music": { + "create_playlist": "إنشاء قائمة تشغيل", + "playlists": "قوائم التشغيل", + "no_playlists": "لا توجد قوائم تشغيل بعد", + "select_playlist": "اختر قائمة تشغيل", + "select_hint": "اختر قائمة تشغيل من الشريط الجانبي أو أنشئ واحدة جديدة", + "add_tracks": "إضافة مقاطع", + "no_tracks": "لا توجد مقاطع في هذه القائمة", + "unknown_artist": "فنان غير معروف", + "unknown_title": "غير معروف", + "confirm_delete": "هل تريد حذف هذه القائمة؟", + "playlist_name": "اسم القائمة", + "create": "إنشاء", + "delete": "حذف", + "share": "مشاركة", + "edit": "تعديل", + "play_all": "تشغيل الكل", + "shuffle": "عشوائي", + "repeat": "تكرار", + "repeat_one": "تكرار واحد", + "queue": "قائمة الانتظار", + "queue_empty": "قائمة الانتظار فارغة", + "not_playing": "لا يتم التشغيل", + "play": "تشغيل", + "pause": "إيقاف مؤقت", + "previous": "السابق", + "next": "التالي", + "volume": "مستوى الصوت", + "mute": "كتم", + "unmute": "إلغاء الكتم", + "title": "العنوان", + "artist": "الفنان", + "album": "الألبوم", + "tracks": "مقاطع", + "add": "إضافة", + "added": "تمت الإضافة!", + "added_to_playlist": "تمت إضافته إلى القائمة", + "add_to_playlist": "إضافة إلى القائمة", + "load_error": "خطأ في تحميل القوائم", + "add_error": "تعذر إضافة المقاطع", + "no_playlists_yet": "لا توجد قوائم بعد. أنشئ واحدة أولاً!", + "selected_files": "محدد:", + "error": "خطأ", + "search_audio": "البحث عن ملفات صوتية…", + "no_audio_files": "لم يتم العثور على ملفات صوتية", + "selected": "محدد", + "loading": "جارٍ التحميل…", + "search_error": "تعذر تحميل الملفات الصوتية", + "adding": "جارٍ الإضافة…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "البحث في الملفات...", + "new_folder": "مجلد جديد", + "upload": "رفع", + "upload_files": "رفع ملفات", + "upload_folder": "رفع مجلد", + "upload.uploading": "جارٍ الرفع...", + "upload.complete": "{count} / {total} تم رفعها", + "upload.files": "ملفات", + "rename": "إعادة التسمية", + "move": "نقل إلى...", + "move_to": "نقل إلى", + "delete": "حذف", + "download": "تحميل", + "view": "عرض", + "cancel": "إلغاء", + "confirm": "تأكيد", + "share": "مشاركة", + "favorite": "إضافة للمفضلة", + "unfavorite": "إزالة من المفضلة", + "copy": "نسخ", + "notify": "إشعار", + "send": "إرسال", + "clear_recent": "مسح الأخيرة", + "logout": "تسجيل الخروج", + "create": "إنشاء", + "search_btn": "بحث", + "close": "إغلاق", + "delete_permanently": "حذف نهائياً", + "empty_trash": "تفريغ سلة المهملات", + "open_parent_folder": "الانتقال إلى المجلد الأصلي", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "المظهر", + "about": "حول OxiCloud", + "about_description": "منصة تخزين سحابي مبنية بـ Rust و Clean Architecture. سريعة وآمنة وخاصة.", + "admin_panel": "لوحة الإدارة", + "profile": "ملفي الشخصي", + "role_user": "مستخدم", + "theme": { + "light": "فاتح", + "dark": "داكن", + "auto": "مثل النظام" + }, + "manage_groups": "إدارة المجموعات" + }, + "share": { + "dialogTitle": "رابط المشاركة", + "linkLabel": "رابط المشاركة:", + "copyLink": "نسخ", + "permissions": "الصلاحيات:", + "permissionRead": "قراءة", + "permissionWrite": "كتابة", + "permissionReshare": "إعادة مشاركة", + "password": "حماية بكلمة مرور:", + "generatePassword": "توليد", + "expiration": "تاريخ انتهاء الصلاحية:", + "update": "تحديث المشاركة", + "remove": "إزالة المشاركة", + "notifyTitle": "إرسال إشعار", + "notifyEmailLabel": "عنوان البريد الإلكتروني:", + "notifyMessageLabel": "رسالة (اختياري):", + "notifySend": "إرسال الإشعار", + "shareWithOthers": "مشاركة مع آخرين", + "sharePublicly": "مشاركة عامة", + "shareSettings": "إعدادات المشاركة", + "shareCopied": "تم نسخ الرابط إلى الحافظة", + "shareCreated": "تم إنشاء رابط المشاركة بنجاح", + "shareUpdated": "تم تحديث إعدادات المشاركة بنجاح", + "shareRemoved": "تمت إزالة المشاركة بنجاح", + "inviteByEmail": "دعوة عبر البريد الإلكتروني — ستُرسل الدعوة", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "رابط المشاركة", + "share_linkLabel": "رابط المشاركة:", + "share_copyLink": "نسخ", + "share_permissions": "الصلاحيات:", + "share_permissionRead": "قراءة", + "share_permissionWrite": "كتابة", + "share_permissionReshare": "إعادة مشاركة", + "share_password": "حماية بكلمة مرور:", + "share_generatePassword": "توليد", + "share_expiration": "تاريخ انتهاء الصلاحية:", + "share_update": "تحديث المشاركة", + "share_remove": "إزالة المشاركة", + "share_notifyTitle": "إرسال إشعار", + "share_notifyEmailLabel": "عنوان البريد الإلكتروني:", + "share_notifyMessageLabel": "رسالة (اختياري):", + "share_notifySend": "إرسال الإشعار", + "shared": { + "backToFiles": "العودة إلى الملفات", + "pageTitle": "الموارد المشتركة", + "pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", + "filterType": "النوع:", + "filterAll": "الكل", + "filterFiles": "ملفات", + "filterFolders": "مجلدات", + "sortBy": "ترتيب حسب:", + "sortByName": "الاسم", + "sortByDate": "تاريخ المشاركة", + "sortByExpiration": "انتهاء الصلاحية", + "search": "بحث", + "colName": "الاسم", + "colType": "النوع", + "colDateShared": "تاريخ المشاركة", + "colExpiration": "انتهاء الصلاحية", + "colPermissions": "الصلاحيات", + "colPassword": "كلمة المرور", + "colActions": "الإجراءات", + "emptyStateTitle": "لا توجد موارد مشتركة بعد", + "emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", + "goToFiles": "الذهاب إلى الملفات", + "typeFile": "ملف", + "typeFolder": "مجلد", + "noExpiration": "بدون انتهاء صلاحية", + "hasPassword": "نعم", + "noPassword": "لا", + "editShare": "تعديل المشاركة", + "notifyShare": "إشعار شخص ما", + "copyLink": "نسخ الرابط", + "removeShare": "إزالة المشاركة", + "linkCopied": "تم نسخ الرابط إلى الحافظة!", + "linkCopyFailed": "فشل نسخ الرابط", + "itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", + "itemRemoved": "تمت إزالة المشاركة بنجاح", + "invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", + "notificationSent": "تم إرسال الإشعار بنجاح", + "notificationFailed": "فشل إرسال الإشعار", + "shared_backToFiles": "العودة إلى الملفات", + "shared_pageTitle": "الموارد المشتركة", + "shared_pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", + "shared_filterType": "النوع:", + "shared_filterAll": "الكل", + "shared_filterFiles": "ملفات", + "shared_filterFolders": "مجلدات", + "shared_sortBy": "ترتيب حسب:", + "shared_sortByName": "الاسم", + "shared_sortByDate": "تاريخ المشاركة", + "shared_sortByExpiration": "انتهاء الصلاحية", + "shared_search": "بحث", + "shared_colName": "الاسم", + "shared_colType": "النوع", + "shared_colDateShared": "تاريخ المشاركة", + "shared_colExpiration": "انتهاء الصلاحية", + "shared_colPermissions": "الصلاحيات", + "shared_colPassword": "كلمة المرور", + "shared_colActions": "الإجراءات", + "shared_emptyStateTitle": "لا توجد موارد مشتركة بعد", + "shared_emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", + "shared_goToFiles": "الذهاب إلى الملفات", + "shared_typeFile": "ملف", + "shared_typeFolder": "مجلد", + "shared_noExpiration": "بدون انتهاء صلاحية", + "shared_hasPassword": "نعم", + "shared_noPassword": "لا", + "shared_editShare": "تعديل المشاركة", + "shared_notifyShare": "إشعار شخص ما", + "shared_copyLink": "نسخ الرابط", + "shared_removeShare": "إزالة المشاركة", + "shared_linkCopied": "تم نسخ الرابط إلى الحافظة!", + "shared_linkCopyFailed": "فشل نسخ الرابط", + "shared_itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", + "shared_itemRemoved": "تمت إزالة المشاركة بنجاح", + "shared_invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", + "shared_notificationSent": "تم إرسال الإشعار بنجاح", + "shared_notificationFailed": "فشل إرسال الإشعار" + }, + "files": { + "name": "الاسم", + "type": "النوع", + "size": "الحجم", + "modified": "تاريخ التعديل", + "no_files": "لا توجد ملفات في هذا المجلد", + "empty_hint": "ارفع ملفات أو أنشئ مجلدات للبدء", + "loading": "جارٍ تحميل الملفات…", + "view_grid": "عرض شبكي", + "view_list": "عرض قائمة", + "file_types": { + "document": "مستند", + "image": "صورة", + "video": "فيديو", + "audio": "صوت", + "pdf": "PDF", + "text": "نص", + "folder": "مجلد", + "spreadsheet": "جدول بيانات", + "presentation": "عرض تقديمي", + "archive": "أرشيف", + "installer": "مثبّت", + "code": "كود" + }, + "owner": "المالك" + }, + "dialogs": { + "rename_folder": "إعادة تسمية المجلد", + "rename_file": "إعادة تسمية الملف", + "new_name": "الاسم الجديد", + "new_folder_title": "مجلد جديد", + "folder_name": "اسم المجلد", + "folder_placeholder": "مجلدي", + "rename_title": "إعادة التسمية", + "move_file": "نقل الملف", + "move_folder": "نقل المجلد", + "select_destination": "اختر المجلد الوجهة:", + "select_this_folder": "اختيار هذا المجلد", + "go_to_parent": ".. (المجلد الأعلى)", + "no_subfolders": "لا توجد مجلدات فرعية", + "root": "الجذر", + "delete_confirmation": "هل أنت متأكد أنك تريد حذف", + "and_contents": "وجميع محتوياته", + "no_undo": "لا يمكن التراجع عن هذا الإجراء", + "confirm_title": "تأكيد الإجراء", + "confirm_delete": "نقل إلى سلة المهملات", + "confirm_delete_file": "هل أنت متأكد أنك تريد نقل الملف \"{{name}}\" إلى سلة المهملات؟", + "confirm_delete_folder": "هل أنت متأكد أنك تريد نقل المجلد \"{{name}}\" وجميع محتوياته إلى سلة المهملات؟", + "confirm_permanent_delete": "حذف نهائي", + "confirm_permanent_delete_msg": "هل أنت متأكد أنك تريد حذف هذا العنصر نهائياً؟ لا يمكن التراجع عن هذا الإجراء.", + "confirm_empty_trash": "تفريغ سلة المهملات", + "confirm_delete_share": "حذف رابط المشاركة", + "confirm_delete_share_msg": "هل أنت متأكد أنك تريد حذف رابط المشاركة هذا؟", + "share_file": "مشاركة الملف", + "share_folder": "مشاركة المجلد", + "existing_shares": "المشاركات الحالية", + "share_options": "خيارات المشاركة", + "password": "كلمة المرور", + "expiration": "انتهاء الصلاحية", + "permissions": "الصلاحيات", + "generated_link": "الرابط المُنشأ", + "notify": "إرسال إشعار", + "recipient": "المستلم", + "message": "الرسالة", + "move_to_home": "نقل إلى المجلد الرئيسي" + }, + "dropzone": { + "drag_files": "اسحب الملفات هنا أو انقر للاختيار", + "drop_files": "أسقط الملفات للرفع" + }, + "permissions": { + "read": "قراءة", + "write": "كتابة", + "reshare": "إعادة مشاركة" + }, + "errors": { + "file_not_found": "الملف غير موجود", + "folder_not_found": "المجلد غير موجود", + "delete_error": "خطأ في الحذف", + "upload_error": "خطأ في رفع الملف", + "rename_error": "خطأ في إعادة التسمية", + "move_error": "خطأ في النقل", + "empty_name": "لا يمكن أن يكون الاسم فارغاً", + "name_exists": "ملف أو مجلد بهذا الاسم موجود بالفعل", + "generic_error": "حدث خطأ", + "group_name_invalid": "يجب أن يتطابق اسم المجموعة مع صيغة بادئة البريد الإلكتروني (حروف، أرقام، نقطة، شرطة، شرطة سفلية؛ 1–64 حرفًا).", + "group_cycle": "سينشئ هذا العضو مرجعًا دائريًا بين المجموعات.", + "group_depth_exceeded": "تتجاوز عمق التعشيش الحد الأقصى المسموح (8).", + "group_virtual_immutable": "مجموعة «Internal» تدار من قبل النظام ولا يمكن تعديلها.", + "group_not_found": "المجموعة غير موجودة.", + "group_name_taken": "توجد بالفعل مجموعة بهذا الاسم." + }, + "breadcrumb": { + "home": "الرئيسية" + }, + "trash": { + "empty_trash": "تفريغ سلة المهملات", + "empty_state": "سلة المهملات فارغة", + "original_location": "الموقع الأصلي", + "deleted_date": "تاريخ الحذف", + "remaining": "المتبقي", + "actions": "الإجراءات", + "restore": "استعادة", + "delete_permanently": "حذف نهائياً", + "empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً.", + "groupby": { + "remaining_days": "الأيام المتبقية", + "trashed_time": "وقت الحذف" + } + }, + "daysRemaining": { + "expired": "منتهية الصلاحية", + "today": "اليوم", + "tomorrow": "غدًا", + "inDays": "{{count}} يوم" + }, + "expiryChip": { + "never": "لا تنتهي الصلاحية", + "expired": "منتهية الصلاحية", + "today": "تنتهي الصلاحية اليوم", + "tomorrow": "تنتهي الصلاحية غدًا", + "inDays": "تنتهي الصلاحية خلال {{count}} يوم", + "onDate": "تنتهي الصلاحية في {{date}}" + }, + "auth": { + "login_title": "تسجيل الدخول", + "username": "اسم المستخدم", + "username_placeholder": "أدخل اسم المستخدم", + "login_identifier": "اسم المستخدم أو البريد الإلكتروني", + "login_identifier_placeholder": "أدخل اسم المستخدم أو البريد الإلكتروني", + "password": "كلمة المرور", + "password_placeholder": "أدخل كلمة المرور", + "login_button": "تسجيل الدخول", + "no_account": "ليس لديك حساب؟", + "register": "إنشاء حساب", + "admin_setup": "أول مرة؟", + "setup": "إعداد المسؤول", + "register_title": "إنشاء حساب", + "email": "البريد الإلكتروني", + "email_placeholder": "أدخل بريدك الإلكتروني", + "confirm_password": "تأكيد كلمة المرور", + "confirm_password_placeholder": "أكد كلمة المرور", + "register_button": "إنشاء حساب", + "have_account": "لديك حساب بالفعل؟", + "login": "تسجيل الدخول", + "setup_title": "الإعداد الأولي", + "setup_step1": "المسؤول", + "setup_step2": "النظام", + "setup_step3": "مكتمل", + "admin_username": "اسم مستخدم المسؤول", + "admin_email": "بريد المسؤول الإلكتروني", + "admin_password": "كلمة مرور المسؤول", + "create_admin": "إنشاء حساب المسؤول", + "back_to_login": "تم الإعداد مسبقاً؟", + "admin_success": "تم إنشاء حساب المسؤول بنجاح! يمكنك الآن تسجيل الدخول.", + "account_success": "تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول.", + "passwords_mismatch": "كلمات المرور غير متطابقة", + "admin_create_error": "خطأ في إنشاء حساب المسؤول", + "or": "أو", + "sso_login": "تسجيل الدخول عبر SSO", + "sso_login_provider": "تسجيل الدخول عبر {{provider}}", + "magicLinkHint": "ليس لديك كلمة مرور؟ أدخل بريدك الإلكتروني وسنرسل لك رابط تسجيل دخول لمرة واحدة.", + "magicLinkEmailLabel": "عنوان البريد الإلكتروني", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "إرسال رابط تسجيل الدخول", + "magicLinkSent": "إذا كان هناك حساب لهذا البريد الإلكتروني، فسيتم إرسال رابط تسجيل الدخول. تحقق من صندوق الوارد.", + "magicLinkUnavailable": "تسجيل الدخول عبر البريد الإلكتروني غير متاح على هذا الخادم.", + "magicLinkNetworkError": "تعذر الوصول إلى الخادم: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "التخزين", + "calculating": "جارٍ الحساب...", + "used": "{{percentage}}% مستخدم ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "لا يمكن معاينة هذا النوع من الملفات.", + "download_file": "تحميل الملف", + "zoom_in": "تكبير", + "zoom_out": "تصغير", + "zoom_reset": "إعادة تعيين التكبير" + }, + "language_selector": { + "title": "!مرحباً", + "subtitle": "اختر لغتك للمتابعة", + "continue": "متابعة", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "لا توجد مفضلات بعد", + "empty_hint": "ضع نجمة على الملفات أو المجلدات لإضافتها إلى المفضلة", + "add": "إضافة للمفضلة", + "remove": "إزالة من المفضلة", + "added_title": "أُضيف للمفضلة", + "added_msg": "أُضيف للمفضلة", + "removed_title": "أُزيل من المفضلة", + "removed_msg": "أُزيل من المفضلة" + }, + "recent": { + "title": "الأخيرة", + "clear": "مسح الأخيرة", + "accessed": "تم الوصول", + "empty_state": "لا توجد ملفات حديثة", + "empty_hint": "الملفات التي تفتحها ستظهر هنا", + "loadMore": "تحميل المزيد" + }, + "notifications": { + "file_renamed": "تمت إعادة تسمية الملف", + "file_renamed_to": "تمت إعادة تسمية الملف إلى \"{{name}}\"", + "folder_renamed": "تمت إعادة تسمية المجلد", + "folder_renamed_to": "تمت إعادة تسمية المجلد إلى \"{{name}}\"", + "file_uploaded": "تم رفع الملف", + "file_deleted": "تم نقل الملف إلى سلة المهملات", + "folder_deleted": "تم نقل المجلد إلى سلة المهملات", + "item_deleted_permanently": "تم حذف العنصر نهائياً", + "trash_emptied": "تم تفريغ سلة المهملات بنجاح", + "title": "الإشعارات", + "empty": "لا توجد إشعارات", + "link_created": "تم إنشاء الرابط", + "share_success": "تم إنشاء رابط المشاركة بنجاح", + "upload_files_section_title": "التحميل غير متاح هنا", + "upload_files_section_body": "انتقل إلى قسم الملفات لتحميل الملفات" + }, + "batch": { + "one_selected": "عنصر واحد محدد", + "n_selected": "{{count}} عناصر محددة", + "confirm_delete": "هل أنت متأكد أنك تريد نقل {{count}} عنصر إلى سلة المهملات؟", + "move_title": "نقل {{count}} عنصر", + "add_favorites": "إضافة للمفضلة", + "move_copy": "نقل أو نسخ" + }, + "admin": { + "page_title": "لوحة الإدارة", + "back_to_app": "العودة إلى OxiCloud", + "loading": "جارٍ التحميل…", + "access_denied": "الوصول مرفوض", + "access_denied_desc": "صلاحيات المسؤول مطلوبة.", + "sign_in": "تسجيل الدخول", + "tab_dashboard": "لوحة المعلومات", + "tab_users": "المستخدمون", + "tab_oidc": "SSO / OIDC", + "total_users": "إجمالي المستخدمين", + "active_users": "المستخدمون النشطون", + "admins": "المسؤولون", + "version": "الإصدار", + "storage_overview": "نظرة عامة على التخزين", + "used": "مستخدم", + "total_quota": "الحصة الإجمالية", + "usage_pct": "نسبة الاستخدام", + "users_over_80": "مستخدمون >80%", + "users_over_quota": "مستخدمون تجاوزوا الحصة", + "system": "النظام", + "auth_label": "المصادقة", + "oidc_label": "OIDC", + "quotas_label": "الحصص", + "enabled": "مفعّل", + "disabled": "معطّل", + "active": "نشط", + "off": "متوقف", + "allow_registration": "السماح بالتسجيل العام", + "registration_warning": "التسجيل العام معطّل. فقط المسؤولون يمكنهم إنشاء مستخدمين.", + "user_management": "إدارة المستخدمين", + "create_user": "إنشاء مستخدم", + "col_user": "المستخدم", + "col_role": "الدور", + "col_auth": "المصادقة", + "col_status": "الحالة", + "col_storage": "التخزين", + "col_last_login": "آخر دخول", + "col_actions": "الإجراءات", + "loading_users": "جارٍ تحميل المستخدمين…", + "failed_load_users": "فشل التحميل", + "no_users_found": "لم يتم العثور على مستخدمين", + "showing_users": "عرض {{from}}-{{to}} من {{total}}", + "prev": "السابق", + "next": "التالي", + "inactive": "غير نشط", + "you_badge": "(أنت)", + "local": "محلي", + "never": "أبداً", + "just_now": "الآن", + "minutes_ago": "منذ {{n}} دقيقة", + "hours_ago": "منذ {{n}} ساعة", + "days_ago": "منذ {{n}} يوم", + "edit_quota_title": "تعديل الحصة", + "reset_password_title": "إعادة تعيين كلمة المرور", + "toggle_role_title": "تبديل الدور", + "deactivate_title": "تعطيل", + "activate_title": "تفعيل", + "delete_title": "حذف", + "sso_title": "تسجيل الدخول الموحد (OIDC / SSO)", + "enable_sso": "تفعيل مصادقة SSO", + "provider_name": "اسم الموفر", + "issuer_url": "عنوان المُصدر", + "issuer_url_hint": "عنوان مُصدر OpenID Connect", + "auto_discover": "اكتشاف تلقائي", + "discovering": "جارٍ الاكتشاف…", + "client_id": "معرّف العميل", + "client_secret": "سر العميل", + "client_secret_placeholder": "اتركه فارغاً للاحتفاظ بالقيمة", + "secret_configured": "سر العميل مُهيأ بالفعل", + "callback_url": "عنوان الاستدعاء", + "callback_url_hint": "(سجّل في IdP)", + "advanced_settings": "إعدادات متقدمة", + "scopes": "النطاقات", + "auto_provision": "إنشاء تلقائي عند أول دخول", + "admin_groups": "مجموعات المسؤولين", + "admin_groups_hint": "أسماء مجموعات OIDC مفصولة بفواصل", + "disable_password": "تعطيل الدخول بكلمة المرور (OIDC فقط)", + "password_warning": "سيمنع جميع عمليات الدخول بالمرور!", + "test_btn": "اختبار", + "save_btn": "حفظ", + "saving": "جارٍ الحفظ…", + "settings_saved": "تم الحفظ — OIDC الآن {{status}}", + "quota_modal_title": "تحديث حصة التخزين", + "quota_user_label": "المستخدم:", + "new_quota": "حصة جديدة", + "quota_unlimited_hint": "0 لغير محدود", + "cancel": "إلغاء", + "create_user_title": "إنشاء مستخدم جديد", + "username_label": "اسم المستخدم", + "username_placeholder": "اسم_المستخدم", + "username_hint": "3–32 حرفاً", + "password_label": "كلمة المرور", + "password_placeholder": "8 أحرف على الأقل", + "email_label": "البريد", + "email_optional": "(اختياري)", + "email_placeholder": "user@example.com (يُنشأ تلقائياً)", + "role_label": "الدور", + "role_user": "مستخدم", + "role_admin": "مسؤول", + "quota_label": "الحصة", + "creating": "جارٍ الإنشاء…", + "reset_pw_title": "إعادة تعيين كلمة المرور", + "new_password_label": "كلمة مرور جديدة", + "resetting": "جارٍ إعادة التعيين…", + "reset_btn": "إعادة تعيين", + "confirm_role_change": "تغيير الدور إلى {{role}}؟", + "confirm_deactivate": "هل أنت متأكد من التعطيل؟", + "confirm_activate": "هل أنت متأكد من التفعيل؟", + "confirm_delete_user": "حذف المستخدم \"{{name}}\"؟ لا يمكن التراجع!", + "confirm_action": "تأكيد الإجراء", + "confirm_yes": "تأكيد", + "confirm_no": "إلغاء", + "error_username_short": "الاسم 3 أحرف على الأقل", + "error_password_short": "كلمة المرور 8 أحرف على الأقل", + "error_generic": "فشل", + "error_network": "خطأ في الشبكة: {{message}}", + "error_create_user": "فشل إنشاء المستخدم", + "tab_storage": "التخزين", + "storage_title": "إعداد التخزين", + "storage_current_backend": "الواجهة الخلفية الحالية", + "storage_total_blobs": "إجمالي الكتل", + "storage_total_size": "الحجم الإجمالي", + "storage_dedup_ratio": "نسبة إزالة التكرار", + "storage_backend": "الواجهة الخلفية", + "storage_local": "محلي", + "storage_s3": "متوافق مع S3", + "storage_provider_preset": "إعداد مسبق للمزود", + "storage_preset_custom": "مخصص", + "storage_endpoint_url": "رابط نقطة النهاية", + "storage_endpoint_hint": "اتركه فارغاً لـ AWS S3", + "storage_bucket": "الحاوية", + "storage_region": "المنطقة", + "storage_access_key": "مفتاح الوصول", + "storage_secret_key": "المفتاح السري", + "storage_secret_configured": "تم إعداد المفتاح", + "storage_key_placeholder": "أدخل مفتاحاً جديداً", + "storage_path_style": "فرض أسلوب المسار", + "storage_path_style_hint": "مطلوب لـ MinIO وبعض الخدمات المتوافقة مع S3", + "storage_test_connection": "اختبار الاتصال", + "storage_test_success": "نجح الاتصال", + "storage_test_failure": "فشل الاتصال", + "storage_save": "حفظ الإعداد", + "storage_saved": "تم حفظ الإعداد", + "storage_migration": "ترحيل البيانات", + "storage_migration_coming_soon": "أدوات الترحيل قريباً", + "migration_status_label": "حالة الترحيل", + "migration_start": "بدء الترحيل", + "migration_pause": "إيقاف مؤقت", + "migration_resume": "استئناف", + "migration_verify": "التحقق", + "migration_complete": "إكمال", + "migration_started": "بدأ الترحيل", + "migration_paused_msg": "الترحيل متوقف مؤقتاً", + "migration_resumed_msg": "استُؤنف الترحيل", + "migration_completed_msg": "اكتمل الترحيل بنجاح", + "migration_verifying": "جارٍ التحقق...", + "migration_verify_passed": "اجتاز التحقق", + "migration_verify_failed": "فشل التحقق", + "migration_failed_blobs": "كتل فاشلة", + "testing": "جارٍ الاختبار...", + "smtp_disabled": "معطّل (المضيف غير مضبوط)", + "smtp_enabled": "مفعّل", + "smtp_enabled_label": "الحالة", + "smtp_intro": "يتم تكوين SMTP حصريًا عبر متغيرات البيئة (OXICLOUD_SMTP_*). تُقرأ القيم أدناه من الخادم قيد التشغيل — لتغييرها، عدّل البيئة وأعد تشغيل OxiCloud.", + "smtp_not_configured": "SMTP غير مكوَّن على هذا الخادم.", + "smtp_send_failed": "فشل الإرسال.", + "smtp_send_test": "إرسال بريد اختباري", + "smtp_sending": "جارٍ الإرسال…", + "smtp_sent": "تم إرسال البريد الاختباري.", + "smtp_server_code": "رد الخادم", + "smtp_test_intro": "يرسل رسالة تشخيصية محددة مسبقًا إلى المستلم أدناه ويُبلِّغ عن استجابة خادم SMTP لتتمكن من مطابقتها مع سجلات المرحّل الخاص بك.", + "smtp_test_missing_to": "أدخل عنوان المستلم.", + "smtp_test_title": "إرسال بريد اختباري", + "smtp_test_to": "عنوان المستلم", + "smtp_title": "البريد الصادر (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "الملف الشخصي", + "back_to_app": "العودة إلى OxiCloud", + "loading": "جارٍ التحميل…", + "not_authenticated": "غير مُصادق", + "not_authenticated_desc": "سجّل الدخول لعرض ملفك الشخصي.", + "sign_in": "تسجيل الدخول", + "role_admin": "مسؤول", + "role_user": "مستخدم", + "account_details": "تفاصيل الحساب", + "username": "اسم المستخدم", + "email": "البريد الإلكتروني", + "role": "الدور", + "last_login": "آخر دخول", + "storage": "التخزين", + "used": "مستخدم", + "quota": "الحصة", + "usage": "الاستخدام", + "unlimited": "غير محدود", + "app_passwords": "كلمات مرور التطبيقات", + "app_pw_desc": "أنشئ كلمات مرور لعملاء WebDAV و CalDAV و CardDAV. تُعرض كل كلمة مرور مرة واحدة فقط.", + "app_pw_label_placeholder": "التسمية (مثلاً Thunderbird، macOS)", + "generate": "إنشاء", + "generating": "جارٍ الإنشاء…", + "new_password_for": "كلمة مرور جديدة لـ", + "copy_warning": "انسخ كلمة المرور الآن. لن تتمكن من رؤيتها مرة أخرى.", + "copy_to_clipboard": "نسخ إلى الحافظة", + "col_label": "التسمية", + "col_created": "تاريخ الإنشاء", + "col_last_used": "آخر استخدام", + "col_status": "الحالة", + "active": "نشط", + "revoked": "ملغى", + "revoke_title": "إلغاء", + "no_app_passwords": "لا توجد كلمات مرور تطبيقات بعد.", + "client_sessions": "جلسات العميل", + "client_sessions_desc": "تُنشأ تلقائيًا عند اتصال عميل متوافق مع Nextcloud.", + "col_client": "العميل", + "never": "أبداً", + "just_now": "الآن", + "minutes_ago": "منذ {{n}} دقيقة", + "hours_ago": "منذ {{n}} ساعة", + "days_ago": "منذ {{n}} يوم", + "edit_profile": "تعديل الملف الشخصي", + "edit_oidc_managed": "لتغيير معلوماتك (الاسم، الاسم الأول، صورة الملف الشخصي، …)، يرجى تحديثها لدى مزود الهوية. ستظهر تغييراتك عند تسجيل الدخول التالي.", + "username_claim_hint": "2-64 حرفًا، أحرف / أرقام / نقطة / شرطة / شرطة سفلية. بمجرد الاختيار، لا يمكن تغيير اسم المستخدم (عملاء DAV/NextCloud يعتمدون عليه).", + "username_already_claimed": "اسم المستخدم محدد ولا يمكن تغييره (عملاء DAV/NextCloud يعتمدون عليه).", + "given_name": "الاسم الأول", + "family_name": "اسم العائلة", + "notify_on_share": "أرسل لي بريدًا إلكترونيًا عندما يشاركني شخص ما", + "notify_on_share_hint": "عند إلغاء التحديد، ستظل المشاركات تظهر في حسابك — لن تتلقى فقط بريدًا إلكترونيًا بشأنها.", + "save_profile": "حفظ التغييرات", + "profile_saved": "تم تحديث الملف الشخصي", + "profile_no_changes": "لا توجد تغييرات لحفظها.", + "profile_save_failed": "فشل الحفظ", + "username_taken_error": "اسم المستخدم هذا مستخدم بالفعل.", + "username_immutable_error": "اسم المستخدم الخاص بك محدد بالفعل ولا يمكن تغييره هنا. اتصل بالمسؤول إذا كنت بحاجة إلى إعادة التسمية.", + "change_password": "تغيير كلمة المرور", + "current_password": "كلمة المرور الحالية", + "new_password": "كلمة المرور الجديدة", + "min_8_chars": "8 أحرف على الأقل", + "confirm_password": "تأكيد كلمة المرور الجديدة", + "update_password": "تحديث كلمة المرور", + "updating": "جارٍ التحديث…", + "password_updated": "تم تحديث كلمة المرور بنجاح", + "passwords_no_match": "كلمتا المرور غير متطابقتين", + "password_too_short": "يجب أن تكون كلمة المرور 8 أحرف على الأقل", + "password_change_failed": "فشل تغيير كلمة المرور", + "error_network": "خطأ في الشبكة: {{message}}", + "error_label_required": "أدخل تسمية", + "error_create_pw": "فشل إنشاء كلمة المرور", + "confirm_revoke": "إلغاء كلمة المرور \"{{label}}\"؟ ستتوقف العملاء عن العمل.", + "error_revoke": "فشل الإلغاء", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "جارٍ الرفع...", + "files": "ملفات", + "complete": "{{count}} / {{total}} تم الرفع" + }, + "storage_quota_exceeded": "تجاوز حصة التخزين", + "sharedwithme": { + "pageTitle": "مشترك معي", + "pageDescription": "الملفات والمجلدات التي شاركها معك مستخدمون آخرون", + "emptyStateTitle": "لم يُشارك معك أي شيء بعد", + "emptyStateDesc": "ستظهر هنا العناصر التي يشاركها معك مستخدمون آخرون", + "loadMore": "تحميل المزيد", + "sharedBy": "مشترك من قِبل", + "colName": "الاسم", + "colType": "النوع", + "colSharedBy": "مشترك من قِبل", + "colDate": "تاريخ المشاركة", + "colPermissions": "الصلاحيات" + }, + "groupby": { + "none": "لا شيء", + "title": "التجميع حسب", + "owner": "المالك", + "shareDate": "تاريخ المشاركة", + "type": "النوع", + "type.folders": "المجلدات", + "accessedAt": "تاريخ الوصول", + "modifiedAt": "تاريخ التعديل", + "createdAt": "تاريخ الإنشاء", + "size": "الحجم", + "favoriteDate": "تاريخ المفضلة", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "جديد" + }, + "dateBucket": { + "today": "اليوم", + "last7days": "آخر 7 أيام", + "last30days": "آخر 30 يومًا" + }, + "groups": { + "title": "إدارة المجموعات", + "create_button": "إنشاء مجموعة", + "create_dialog_title": "مجموعة جديدة", + "edit_dialog_title": "إعادة تسمية المجموعة", + "name_label": "الاسم", + "name_placeholder": "engineering", + "description_label": "الوصف (اختياري)", + "members_section": "الأعضاء", + "add_member_placeholder": "إضافة مستخدم أو مجموعة…", + "no_members": "لا يوجد أعضاء بعد.", + "remove_member": "إزالة", + "delete_group": "حذف المجموعة", + "delete_confirm": "حذف المجموعة \"{name}\"؟ سيتم إلغاء الصلاحيات المرتبطة بهذه المجموعة.", + "empty_state": "لا توجد مجموعات بعد.", + "load_more": "تحميل المزيد", + "back_to_list": "رجوع", + "loading": "جارٍ التحميل…", + "virtual_badge": "النظام", + "member_count_zero": "لا يوجد أعضاء", + "member_count_one": "عضو واحد", + "member_count_other": "{count} أعضاء", + "delete_confirm_label": "اكتب اسم المجموعة للتأكيد:", + "delete_confirm_mismatch": "اكتب اسم المجموعة كما هو للتأكيد.", + "virtual_internal_name": "داخلي", + "members_loading": "جارٍ تحميل الأعضاء…", + "members_empty": "لا يوجد أعضاء", + "virtual_internal_explanation": "كل مستخدم داخلي على هذا الخادم" + }, + "myshares": { + "copyLink": "نسخ الرابط", + "deleteLink": "حذف الرابط", + "notifyByEmail": "إشعار عبر البريد الإلكتروني", + "notifyFailed": "تعذّر إرسال الإشعار.", + "notifyGroupMembers": "إشعار أعضاء المجموعة", + "notifyRateLimited": "عدد كبير من الإشعارات لهذا المستلم — حاول لاحقًا.", + "removeAccess": "إزالة الوصول", + "resendInvitation": "إعادة إرسال بريد الدعوة" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/de.json b/frontend/static/locales/de.json new file mode 100644 index 00000000..a4771f23 --- /dev/null +++ b/frontend/static/locales/de.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Dieser Anmeldelink ist nicht mehr gültig", + "expired_body": "Der Link ist möglicherweise abgelaufen oder wurde bereits verwendet. Wir können Ihnen einen neuen senden — er wird in wenigen Sekunden in Ihrem Posteingang sein.", + "resend_to": "Neuen Link an {{email}} senden", + "generic_unavailable": "Dieser Anmeldelink ist nicht mehr gültig. Er wurde möglicherweise bereits verwendet oder ist abgelaufen. Fordern Sie auf der Anmeldeseite einen neuen Link an.", + "service_unavailable": "Die Magic-Link-Anmeldung ist auf diesem Server nicht aktiviert.", + "internal_error": "Bei der Anmeldung ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", + "resend_failure": "Beim Senden des Links ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", + "cross_browser_title": "Anmeldung auf diesem Gerät fortsetzen?", + "cross_browser_body": "Sie haben diesen Anmeldelink in einem anderen Browser oder Gerät geöffnet als dem, von dem aus Sie ihn angefordert haben.", + "cross_browser_warning": "Wenn Sie diesen Link angefordert haben, können Sie sicher fortfahren. Falls nicht, schließen Sie diese Seite — ein Klick auf Weiter würde jemand anderen in Ihrem Konto anmelden.", + "cross_browser_continue": "Fortfahren und anmelden", + "resend_confirmation_title": "Prüfen Sie Ihren Posteingang", + "resend_confirmation_body": "Falls der Anmeldelink zu einem aktiven Konto gehörte, wurde gerade ein neuer Link gesendet. Bitte prüfen Sie Ihren Posteingang.", + "return_link": "Zurück zu OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", + "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie ihn, indem Sie auf den folgenden Link klicken:\n{{link}}\n\nDer Link kann nur einmal verwendet werden und läuft in {{ttl_hours}} Stunden ab.\nFalls Sie diese Einladung nicht erwartet haben, können Sie diese Nachricht ignorieren.\n\n— OxiCloud" + }, + "login": { + "subject": "Anmeldung bei OxiCloud", + "body": "Hallo,\n\nVerwenden Sie den Link unten, um sich bei OxiCloud anzumelden. Der Link kann nur einmal verwendet werden und läuft in {{ttl_minutes}} Minuten ab. Öffnen Sie ihn auf demselben Gerät, von dem aus Sie ihn angefordert haben.\n\n{{link}}\n\nFalls Sie diesen Anmeldelink nicht angefordert haben, können Sie diese Nachricht ignorieren — es ist keine weitere Aktion erforderlich.\n\n— OxiCloud" + }, + "kind_file": "Datei", + "kind_folder": "Ordner", + "english_fallback_divider": "--- Englische Version unten ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", + "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie OxiCloud, um Ihre neue Freigabe zu sehen:\n{{login_link}}\n\nMöglicherweise gibt es weitere neue Freigaben von {{inviter}} — melden Sie sich an, um alle Ihre freigegebenen Elemente zu sehen.\n\n— OxiCloud\n\nSie erhalten diese Nachricht, weil Sie ein OxiCloud-Konto haben und die Benachrichtigung über Freigaben aktiviert ist. Sie können sie in Ihrem Profil deaktivieren (Per E-Mail benachrichtigen, wenn jemand mit mir teilt)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalistisches Cloud-Speichersystem" + }, + "nav": { + "files": "Dateien", + "shared": "Freigaben", + "recent": "Zuletzt verwendet", + "favorites": "Favoriten", + "photos": "Fotos", + "music": "Musik", + "trash": "Papierkorb", + "sharedwithme": "Mit mir geteilt" + }, + "photos": { + "empty_state": "Noch keine Fotos", + "empty_hint": "Laden Sie Bilder oder Videos hoch, um sie hier zu sehen", + "items_selected": "ausgewählt", + "view_daily": "Tag", + "view_monthly": "Monat", + "view_yearly": "Jahr" + }, + "music": { + "create_playlist": "Playlist erstellen", + "playlists": "Playlists", + "no_playlists": "Noch keine Playlists", + "select_playlist": "Playlist auswählen", + "select_hint": "Wählen Sie eine Playlist aus der Seitenleiste oder erstellen Sie eine neue", + "add_tracks": "Titel hinzufügen", + "no_tracks": "Keine Titel in dieser Playlist", + "unknown_artist": "Unbekannter Künstler", + "unknown_title": "Unbekannt", + "confirm_delete": "Diese Playlist löschen?", + "playlist_name": "Playlist-Name", + "create": "Erstellen", + "delete": "Löschen", + "share": "Teilen", + "edit": "Bearbeiten", + "play_all": "Alle abspielen", + "shuffle": "Zufällig", + "repeat": "Wiederholen", + "repeat_one": "Einen wiederholen", + "queue": "Warteschlange", + "queue_empty": "Warteschlange ist leer", + "not_playing": "Nicht abspielend", + "play": "Abspielen", + "pause": "Pause", + "previous": "Zurück", + "next": "Weiter", + "volume": "Lautstärke", + "mute": "Stumm", + "unmute": "Ton ein", + "title": "Titel", + "artist": "Künstler", + "album": "Album", + "tracks": "Titel", + "add": "Hinzufügen", + "added": "Hinzugefügt!", + "added_to_playlist": "zur Playlist hinzugefügt", + "add_to_playlist": "Zur Playlist hinzufügen", + "load_error": "Fehler beim Laden der Playlists", + "add_error": "Tracks konnten nicht hinzugefügt werden", + "no_playlists_yet": "Noch keine Playlists. Erstellen Sie zuerst eine!", + "selected_files": "Ausgewählt:", + "error": "Fehler", + "search_audio": "Audiodateien suchen…", + "no_audio_files": "Keine Audiodateien gefunden", + "selected": "ausgewählt", + "loading": "Wird geladen…", + "search_error": "Audiodateien konnten nicht geladen werden", + "adding": "Wird hinzugefügt…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "Dateien suchen...", + "new_folder": "Neuer Ordner", + "upload": "Hochladen", + "upload_files": "Dateien hochladen", + "upload_folder": "Ordner hochladen", + "upload.uploading": "Wird hochgeladen...", + "upload.complete": "{count} / {total} hochgeladen", + "upload.files": "Dateien", + "rename": "Umbenennen", + "move": "Verschieben nach...", + "move_to": "Verschieben nach", + "delete": "Löschen", + "download": "Herunterladen", + "view": "Anzeigen", + "cancel": "Abbrechen", + "confirm": "Bestätigen", + "share": "Teilen", + "favorite": "Zu Favoriten hinzufügen", + "unfavorite": "Aus Favoriten entfernen", + "copy": "Kopieren", + "notify": "Benachrichtigen", + "send": "Senden", + "clear_recent": "Zuletzt verwendete löschen", + "logout": "Abmelden", + "create": "Erstellen", + "search_btn": "Suchen", + "close": "Schließen", + "delete_permanently": "Endgültig löschen", + "empty_trash": "Papierkorb leeren", + "open_parent_folder": "Zum übergeordneten Ordner", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Erscheinungsbild", + "about": "Über OxiCloud", + "about_description": "Cloud-Speicherplattform mit Rust und Clean Architecture. Schnell, sicher und privat.", + "admin_panel": "Admin-Panel", + "profile": "Mein Profil", + "role_user": "Benutzer", + "theme": { + "light": "Hell", + "dark": "Dunkel", + "auto": "Wie System" + }, + "manage_groups": "Gruppen verwalten" + }, + "share": { + "dialogTitle": "Link teilen", + "linkLabel": "Geteilter Link:", + "copyLink": "Kopieren", + "permissions": "Berechtigungen:", + "permissionRead": "Lesen", + "permissionWrite": "Schreiben", + "permissionReshare": "Weiterteilen", + "password": "Passwortschutz:", + "generatePassword": "Generieren", + "expiration": "Ablaufdatum:", + "update": "Freigabe aktualisieren", + "remove": "Freigabe entfernen", + "notifyTitle": "Benachrichtigung senden", + "notifyEmailLabel": "E-Mail-Adresse:", + "notifyMessageLabel": "Nachricht (optional):", + "notifySend": "Benachrichtigung senden", + "shareWithOthers": "Mit anderen teilen", + "sharePublicly": "Öffentlich teilen", + "shareSettings": "Freigabeeinstellungen", + "shareCopied": "Link in Zwischenablage kopiert", + "shareCreated": "Freigabelink erfolgreich erstellt", + "shareUpdated": "Freigabeeinstellungen aktualisiert", + "shareRemoved": "Freigabe erfolgreich entfernt", + "inviteByEmail": "Per E-Mail einladen — Einladung wird gesendet", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Link teilen", + "share_linkLabel": "Geteilter Link:", + "share_copyLink": "Kopieren", + "share_permissions": "Berechtigungen:", + "share_permissionRead": "Lesen", + "share_permissionWrite": "Schreiben", + "share_permissionReshare": "Weiterteilen", + "share_password": "Passwortschutz:", + "share_generatePassword": "Generieren", + "share_expiration": "Ablaufdatum:", + "share_update": "Freigabe aktualisieren", + "share_remove": "Freigabe entfernen", + "share_notifyTitle": "Benachrichtigung senden", + "share_notifyEmailLabel": "E-Mail-Adresse:", + "share_notifyMessageLabel": "Nachricht (optional):", + "share_notifySend": "Benachrichtigung senden", + "shared": { + "backToFiles": "Zurück zu Dateien", + "pageTitle": "Geteilte Ressourcen", + "pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", + "filterType": "Typ:", + "filterAll": "Alle", + "filterFiles": "Dateien", + "filterFolders": "Ordner", + "sortBy": "Sortieren nach:", + "sortByName": "Name", + "sortByDate": "Freigabedatum", + "sortByExpiration": "Ablaufdatum", + "search": "Suchen", + "colName": "Name", + "colType": "Typ", + "colDateShared": "Freigabedatum", + "colExpiration": "Ablaufdatum", + "colPermissions": "Berechtigungen", + "colPassword": "Passwort", + "colActions": "Aktionen", + "emptyStateTitle": "Noch keine geteilten Ressourcen", + "emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", + "goToFiles": "Zu Dateien gehen", + "typeFile": "Datei", + "typeFolder": "Ordner", + "noExpiration": "Kein Ablaufdatum", + "hasPassword": "Ja", + "noPassword": "Nein", + "editShare": "Freigabe bearbeiten", + "notifyShare": "Jemanden benachrichtigen", + "copyLink": "Link kopieren", + "removeShare": "Freigabe entfernen", + "linkCopied": "Link in Zwischenablage kopiert!", + "linkCopyFailed": "Link konnte nicht kopiert werden", + "itemUpdated": "Freigabeeinstellungen aktualisiert", + "itemRemoved": "Freigabe erfolgreich entfernt", + "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", + "notificationSent": "Benachrichtigung erfolgreich gesendet", + "notificationFailed": "Benachrichtigung konnte nicht gesendet werden", + "shared_backToFiles": "Zurück zu Dateien", + "shared_pageTitle": "Geteilte Ressourcen", + "shared_pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", + "shared_filterType": "Typ:", + "shared_filterAll": "Alle", + "shared_filterFiles": "Dateien", + "shared_filterFolders": "Ordner", + "shared_sortBy": "Sortieren nach:", + "shared_sortByName": "Name", + "shared_sortByDate": "Freigabedatum", + "shared_sortByExpiration": "Ablaufdatum", + "shared_search": "Suchen", + "shared_colName": "Name", + "shared_colType": "Typ", + "shared_colDateShared": "Freigabedatum", + "shared_colExpiration": "Ablaufdatum", + "shared_colPermissions": "Berechtigungen", + "shared_colPassword": "Passwort", + "shared_colActions": "Aktionen", + "shared_emptyStateTitle": "Noch keine geteilten Ressourcen", + "shared_emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", + "shared_goToFiles": "Zu Dateien gehen", + "shared_typeFile": "Datei", + "shared_typeFolder": "Ordner", + "shared_noExpiration": "Kein Ablaufdatum", + "shared_hasPassword": "Ja", + "shared_noPassword": "Nein", + "shared_editShare": "Freigabe bearbeiten", + "shared_notifyShare": "Jemanden benachrichtigen", + "shared_copyLink": "Link kopieren", + "shared_removeShare": "Freigabe entfernen", + "shared_linkCopied": "Link in Zwischenablage kopiert!", + "shared_linkCopyFailed": "Link konnte nicht kopiert werden", + "shared_itemUpdated": "Freigabeeinstellungen aktualisiert", + "shared_itemRemoved": "Freigabe erfolgreich entfernt", + "shared_invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", + "shared_notificationSent": "Benachrichtigung erfolgreich gesendet", + "shared_notificationFailed": "Benachrichtigung konnte nicht gesendet werden" + }, + "files": { + "name": "Name", + "type": "Typ", + "size": "Größe", + "modified": "Geändert", + "no_files": "Keine Dateien in diesem Ordner", + "empty_hint": "Laden Sie Dateien hoch oder erstellen Sie Ordner, um loszulegen", + "loading": "Dateien werden geladen…", + "view_grid": "Rasteransicht", + "view_list": "Listenansicht", + "file_types": { + "document": "Dokument", + "image": "Bild", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Text", + "folder": "Ordner", + "spreadsheet": "Tabelle", + "presentation": "Präsentation", + "archive": "Archiv", + "installer": "Installationsdatei", + "code": "Code" + }, + "owner": "Eigentümer" + }, + "dialogs": { + "rename_folder": "Ordner umbenennen", + "rename_file": "Datei umbenennen", + "new_name": "Neuer Name", + "new_folder_title": "Neuer Ordner", + "folder_name": "Ordnername", + "folder_placeholder": "Mein Ordner", + "rename_title": "Umbenennen", + "move_file": "Datei verschieben", + "move_folder": "Ordner verschieben", + "select_destination": "Zielordner auswählen:", + "root": "Stammverzeichnis", + "delete_confirmation": "Sind Sie sicher, dass Sie löschen möchten", + "and_contents": "und den gesamten Inhalt", + "no_undo": "Diese Aktion kann nicht rückgängig gemacht werden", + "confirm_title": "Aktion bestätigen", + "confirm_delete": "In Papierkorb verschieben", + "confirm_delete_file": "Sind Sie sicher, dass Sie die Datei \"{{name}}\" in den Papierkorb verschieben möchten?", + "confirm_delete_folder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und seinen gesamten Inhalt in den Papierkorb verschieben möchten?", + "confirm_permanent_delete": "Endgültig löschen", + "confirm_permanent_delete_msg": "Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm_empty_trash": "Papierkorb leeren", + "confirm_delete_share": "Freigabelink löschen", + "confirm_delete_share_msg": "Sind Sie sicher, dass Sie diesen Freigabelink löschen möchten?", + "share_file": "Datei teilen", + "share_folder": "Ordner teilen", + "existing_shares": "Bestehende Freigaben", + "share_options": "Freigabeoptionen", + "password": "Passwort", + "expiration": "Ablaufdatum", + "permissions": "Berechtigungen", + "generated_link": "Generierter Link", + "notify": "Benachrichtigung senden", + "recipient": "Empfänger", + "message": "Nachricht", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "In den Home-Ordner verschieben" + }, + "dropzone": { + "drag_files": "Dateien hierher ziehen oder klicken zum Auswählen", + "drop_files": "Dateien zum Hochladen ablegen" + }, + "permissions": { + "read": "Lesen", + "write": "Schreiben", + "reshare": "Weiterteilen" + }, + "errors": { + "file_not_found": "Datei nicht gefunden", + "folder_not_found": "Ordner nicht gefunden", + "delete_error": "Fehler beim Löschen", + "upload_error": "Fehler beim Hochladen", + "rename_error": "Fehler beim Umbenennen", + "move_error": "Fehler beim Verschieben", + "empty_name": "Der Name darf nicht leer sein", + "name_exists": "Eine Datei oder ein Ordner mit diesem Namen existiert bereits", + "generic_error": "Ein Fehler ist aufgetreten", + "group_name_invalid": "Der Gruppenname muss dem E-Mail-Präfix-Format entsprechen (Buchstaben, Ziffern, Punkt, Bindestrich, Unterstrich; 1–64 Zeichen).", + "group_cycle": "Dieses Mitglied würde einen Gruppen-Zirkelbezug erzeugen.", + "group_depth_exceeded": "Die Verschachtelungstiefe überschreitet das zulässige Maximum (8).", + "group_virtual_immutable": "Die Gruppe „Internal“ wird vom System verwaltet und kann nicht geändert werden.", + "group_not_found": "Gruppe nicht gefunden.", + "group_name_taken": "Eine Gruppe mit diesem Namen existiert bereits." + }, + "breadcrumb": { + "home": "Startseite" + }, + "trash": { + "empty_trash": "Papierkorb leeren", + "empty_state": "Der Papierkorb ist leer", + "original_location": "Ursprünglicher Speicherort", + "deleted_date": "Löschdatum", + "remaining": "Verbleibend", + "actions": "Aktionen", + "restore": "Wiederherstellen", + "delete_permanently": "Endgültig löschen", + "empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht.", + "groupby": { + "remaining_days": "Verbleibende Tage", + "trashed_time": "Löschzeit" + } + }, + "daysRemaining": { + "expired": "Abgelaufen", + "today": "Heute", + "tomorrow": "Morgen", + "inDays": "{{count}} Tage" + }, + "expiryChip": { + "never": "Läuft nie ab", + "expired": "Abgelaufen", + "today": "Läuft heute ab", + "tomorrow": "Läuft morgen ab", + "inDays": "Läuft in {{count}} Tagen ab", + "onDate": "Läuft am {{date}} ab" + }, + "auth": { + "login_title": "Anmelden", + "username": "Benutzername", + "username_placeholder": "Geben Sie Ihren Benutzernamen ein", + "login_identifier": "Benutzername oder E-Mail", + "login_identifier_placeholder": "Geben Sie Ihren Benutzernamen oder Ihre E-Mail-Adresse ein", + "password": "Passwort", + "password_placeholder": "Geben Sie Ihr Passwort ein", + "login_button": "Anmelden", + "no_account": "Kein Konto?", + "register": "Registrieren", + "admin_setup": "Erstmalig?", + "setup": "Administrator einrichten", + "register_title": "Konto erstellen", + "email": "E-Mail", + "email_placeholder": "Geben Sie Ihre E-Mail ein", + "confirm_password": "Passwort bestätigen", + "confirm_password_placeholder": "Bestätigen Sie Ihr Passwort", + "register_button": "Konto erstellen", + "have_account": "Bereits ein Konto?", + "login": "Anmelden", + "setup_title": "Ersteinrichtung", + "setup_step1": "Admin", + "setup_step2": "System", + "setup_step3": "Abgeschlossen", + "admin_username": "Admin-Benutzername", + "admin_email": "Admin-E-Mail", + "admin_password": "Admin-Passwort", + "create_admin": "Administrator erstellen", + "back_to_login": "Bereits eingerichtet?", + "admin_success": "Administratorkonto erfolgreich erstellt! Sie können sich jetzt anmelden.", + "account_success": "Konto erfolgreich erstellt! Sie können sich jetzt anmelden.", + "passwords_mismatch": "Die Passwörter stimmen nicht überein", + "admin_create_error": "Fehler beim Erstellen des Administratorkontos", + "or": "oder", + "sso_login": "Mit SSO anmelden", + "sso_login_provider": "Mit {{provider}} anmelden", + "magicLinkHint": "Kein Passwort? Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen einmaligen Anmeldelink.", + "magicLinkEmailLabel": "E-Mail-Adresse", + "magicLinkEmailPlaceholder": "sie@beispiel.de", + "magicLinkSubmit": "Anmeldelink senden", + "magicLinkSent": "Wenn für diese E-Mail-Adresse ein Konto besteht, wurde ein Anmeldelink gesendet. Überprüfen Sie Ihren Posteingang.", + "magicLinkUnavailable": "Die Anmeldung per E-Mail ist auf diesem Server nicht verfügbar.", + "magicLinkNetworkError": "Server nicht erreichbar: {{message}}", + "magicLinkToggle": "Kein Passwort? Anmeldelink per E-Mail", + "passwordsMatch": "Passwörter stimmen überein", + "capsLock": "Feststelltaste aktiv" + }, + "storage": { + "title": "Speicher", + "calculating": "Berechnung...", + "used": "{{percentage}}% verwendet ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Dieser Dateityp kann nicht in der Vorschau angezeigt werden.", + "download_file": "Datei herunterladen", + "zoom_in": "Vergrößern", + "zoom_out": "Verkleinern", + "zoom_reset": "Zoom zurücksetzen" + }, + "language_selector": { + "title": "Willkommen!", + "subtitle": "Wählen Sie Ihre Sprache, um fortzufahren", + "continue": "Weiter", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Noch keine Favoriten", + "empty_hint": "Markieren Sie Dateien oder Ordner mit einem Stern, um sie zu Ihren Favoriten hinzuzufügen", + "add": "Zu Favoriten hinzufügen", + "remove": "Aus Favoriten entfernen", + "added_title": "Zu Favoriten hinzugefügt", + "added_msg": "zu Favoriten hinzugefügt", + "removed_title": "Aus Favoriten entfernt", + "removed_msg": "aus Favoriten entfernt" + }, + "recent": { + "title": "Zuletzt verwendet", + "clear": "Zuletzt verwendete löschen", + "accessed": "Zugegriffen", + "empty_state": "Keine zuletzt verwendeten Dateien", + "empty_hint": "Dateien, die Sie öffnen, werden hier angezeigt", + "loadMore": "Mehr laden" + }, + "notifications": { + "file_renamed": "Datei umbenannt", + "file_renamed_to": "Datei umbenannt in \"{{name}}\"", + "folder_renamed": "Ordner umbenannt", + "folder_renamed_to": "Ordner umbenannt in \"{{name}}\"", + "file_uploaded": "Datei hochgeladen", + "file_deleted": "Datei in Papierkorb verschoben", + "folder_deleted": "Ordner in Papierkorb verschoben", + "item_deleted_permanently": "Element endgültig gelöscht", + "trash_emptied": "Papierkorb erfolgreich geleert", + "title": "Benachrichtigungen", + "empty": "Keine Benachrichtigungen", + "link_created": "Link erstellt", + "share_success": "Freigabelink erfolgreich erstellt", + "upload_files_section_title": "Upload hier nicht verfügbar", + "upload_files_section_body": "Wechseln Sie zum Abschnitt Dateien, um Dateien hochzuladen" + }, + "batch": { + "one_selected": "1 Element ausgewählt", + "n_selected": "{{count}} Elemente ausgewählt", + "confirm_delete": "Möchten Sie wirklich {{count}} Elemente in den Papierkorb verschieben?", + "move_title": "{{count}} Element(e) verschieben", + "add_favorites": "Zu Favoriten hinzufügen", + "move_copy": "Verschieben oder kopieren" + }, + "admin": { + "page_title": "Admin-Panel", + "back_to_app": "Zurück zu OxiCloud", + "loading": "Laden…", + "access_denied": "Zugriff verweigert", + "access_denied_desc": "Administratorrechte erforderlich.", + "sign_in": "Anmelden", + "tab_dashboard": "Dashboard", + "tab_users": "Benutzer", + "tab_oidc": "SSO / OIDC", + "total_users": "Benutzer gesamt", + "active_users": "Aktive Benutzer", + "admins": "Admins", + "version": "Version", + "storage_overview": "Speicherübersicht", + "used": "Verwendet", + "total_quota": "Gesamtkontingent", + "usage_pct": "Nutzung %", + "users_over_80": "Benutzer >80% Kontingent", + "users_over_quota": "Benutzer über Kontingent", + "system": "System", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Kontingente", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "active": "Aktiv", + "off": "Aus", + "allow_registration": "Öffentliche Selbstregistrierung erlauben", + "registration_warning": "Öffentliche Registrierung ist deaktiviert. Nur Admins können neue Benutzer erstellen.", + "user_management": "Benutzerverwaltung", + "create_user": "Benutzer erstellen", + "col_user": "Benutzer", + "col_role": "Rolle", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Speicher", + "col_last_login": "Letzter Login", + "col_actions": "Aktionen", + "loading_users": "Benutzer werden geladen…", + "failed_load_users": "Laden fehlgeschlagen", + "no_users_found": "Keine Benutzer gefunden", + "showing_users": "Zeige {{from}}-{{to}} von {{total}}", + "prev": "Zurück", + "next": "Weiter", + "inactive": "Inaktiv", + "you_badge": "(du)", + "local": "Lokal", + "never": "Nie", + "just_now": "Gerade eben", + "minutes_ago": "vor {{n}}Min", + "hours_ago": "vor {{n}}Std", + "days_ago": "vor {{n}}T", + "edit_quota_title": "Kontingent bearbeiten", + "reset_password_title": "Passwort zurücksetzen", + "toggle_role_title": "Rolle wechseln", + "deactivate_title": "Deaktivieren", + "activate_title": "Aktivieren", + "delete_title": "Löschen", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "SSO-Authentifizierung aktivieren", + "provider_name": "Anbietername", + "issuer_url": "Aussteller-URL", + "issuer_url_hint": "OpenID Connect Aussteller-URL Ihres Identitätsanbieters", + "auto_discover": "Auto-Erkennung", + "discovering": "Erkennung…", + "client_id": "Client-ID", + "client_secret": "Client-Secret", + "client_secret_placeholder": "Leer lassen für aktuellen Wert", + "secret_configured": "Ein Client-Secret ist bereits konfiguriert", + "callback_url": "Callback-URL", + "callback_url_hint": "(bei IdP registrieren)", + "advanced_settings": "Erweiterte Einstellungen", + "scopes": "Scopes", + "auto_provision": "Benutzer bei erstem Login automatisch anlegen", + "admin_groups": "Admin-Gruppen", + "admin_groups_hint": "Kommagetrennte OIDC-Gruppennamen für Admin-Rolle", + "disable_password": "Passwort-Login deaktivieren (nur OIDC)", + "password_warning": "Dies verhindert ALLE passwortbasierten Anmeldungen!", + "test_btn": "Testen", + "save_btn": "Speichern", + "saving": "Speichern…", + "settings_saved": "Einstellungen gespeichert — OIDC ist jetzt {{status}}", + "quota_modal_title": "Speicherkontingent aktualisieren", + "quota_user_label": "Benutzer:", + "new_quota": "Neues Kontingent", + "quota_unlimited_hint": "0 für unbegrenzt", + "cancel": "Abbrechen", + "create_user_title": "Neuen Benutzer erstellen", + "username_label": "Benutzername", + "username_placeholder": "maxmuster", + "username_hint": "3–32 Zeichen", + "password_label": "Passwort", + "password_placeholder": "Min. 8 Zeichen", + "email_label": "E-Mail", + "email_optional": "(optional)", + "email_placeholder": "benutzer@beispiel.de (automatisch wenn leer)", + "role_label": "Rolle", + "role_user": "Benutzer", + "role_admin": "Admin", + "quota_label": "Kontingent", + "creating": "Erstellen…", + "reset_pw_title": "Passwort zurücksetzen", + "new_password_label": "Neues Passwort", + "resetting": "Zurücksetzen…", + "reset_btn": "Zurücksetzen", + "confirm_role_change": "Rolle zu {{role}} ändern?", + "confirm_deactivate": "Diesen Benutzer wirklich deaktivieren?", + "confirm_activate": "Diesen Benutzer wirklich aktivieren?", + "confirm_delete_user": "Benutzer \"{{name}}\" LÖSCHEN? Kann nicht rückgängig gemacht werden!", + "confirm_action": "Aktion bestätigen", + "confirm_yes": "Bestätigen", + "confirm_no": "Abbrechen", + "error_username_short": "Benutzername muss mindestens 3 Zeichen haben", + "error_password_short": "Passwort muss mindestens 8 Zeichen haben", + "error_generic": "Fehlgeschlagen", + "error_network": "Netzwerkfehler: {{message}}", + "error_create_user": "Benutzer erstellen fehlgeschlagen", + "tab_storage": "Speicher", + "storage_title": "Speicherkonfiguration", + "storage_current_backend": "Aktuelles Backend", + "storage_total_blobs": "Gesamt-Blobs", + "storage_total_size": "Gesamtgröße", + "storage_dedup_ratio": "Deduplizierungsrate", + "storage_backend": "Backend", + "storage_local": "Lokal", + "storage_s3": "S3-kompatibel", + "storage_provider_preset": "Anbieter-Voreinstellung", + "storage_preset_custom": "Benutzerdefiniert", + "storage_endpoint_url": "Endpunkt-URL", + "storage_endpoint_hint": "Leer lassen für AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Region", + "storage_access_key": "Zugriffsschlüssel", + "storage_secret_key": "Geheimschlüssel", + "storage_secret_configured": "Schlüssel konfiguriert", + "storage_key_placeholder": "Neuen Schlüssel eingeben", + "storage_path_style": "Pfadstil erzwingen", + "storage_path_style_hint": "Erforderlich für MinIO und einige S3-kompatible Dienste", + "storage_test_connection": "Verbindung testen", + "storage_test_success": "Verbindung erfolgreich", + "storage_test_failure": "Verbindung fehlgeschlagen", + "storage_save": "Konfiguration speichern", + "storage_saved": "Konfiguration gespeichert", + "storage_migration": "Datenmigration", + "storage_migration_coming_soon": "Migrationstools demnächst verfügbar", + "migration_status_label": "Migrationsstatus", + "migration_start": "Migration starten", + "migration_pause": "Pausieren", + "migration_resume": "Fortsetzen", + "migration_verify": "Verifizieren", + "migration_complete": "Abschließen", + "migration_started": "Migration gestartet", + "migration_paused_msg": "Migration pausiert", + "migration_resumed_msg": "Migration fortgesetzt", + "migration_completed_msg": "Migration erfolgreich abgeschlossen", + "migration_verifying": "Wird verifiziert...", + "migration_verify_passed": "Verifizierung erfolgreich", + "migration_verify_failed": "Verifizierung fehlgeschlagen", + "migration_failed_blobs": "Fehlgeschlagene Blobs", + "testing": "Wird getestet...", + "smtp_disabled": "Deaktiviert (Host nicht gesetzt)", + "smtp_enabled": "Aktiviert", + "smtp_enabled_label": "Status", + "smtp_intro": "SMTP wird ausschließlich über Umgebungsvariablen (OXICLOUD_SMTP_*) konfiguriert. Die folgenden Werte werden aus dem laufenden Server gelesen — zum Ändern bearbeiten Sie die Umgebung und starten OxiCloud neu.", + "smtp_not_configured": "SMTP ist auf diesem Server nicht konfiguriert.", + "smtp_send_failed": "Senden fehlgeschlagen.", + "smtp_send_test": "Test-E-Mail senden", + "smtp_sending": "Senden …", + "smtp_sent": "Test-E-Mail gesendet.", + "smtp_server_code": "Server antwortete", + "smtp_test_intro": "Sendet eine fest einprogrammierte Diagnosenachricht an den unten angegebenen Empfänger und meldet die Antwort des SMTP-Servers, sodass Sie sie mit Ihren Relay-Protokollen abgleichen können.", + "smtp_test_missing_to": "Geben Sie eine Empfängeradresse ein.", + "smtp_test_title": "Test-E-Mail senden", + "smtp_test_to": "Empfängeradresse", + "smtp_title": "Ausgehende E-Mail (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Profil", + "back_to_app": "Zurück zu OxiCloud", + "loading": "Laden…", + "not_authenticated": "Nicht authentifiziert", + "not_authenticated_desc": "Bitte melden Sie sich an, um Ihr Profil anzuzeigen.", + "sign_in": "Anmelden", + "role_admin": "Administrator", + "role_user": "Benutzer", + "account_details": "Kontodetails", + "username": "Benutzername", + "email": "E-Mail", + "role": "Rolle", + "last_login": "Letzter Login", + "storage": "Speicher", + "used": "Verwendet", + "quota": "Kontingent", + "usage": "Nutzung", + "unlimited": "Unbegrenzt", + "app_passwords": "App-Passwörter", + "app_pw_desc": "Passwörter für WebDAV-, CalDAV- und CardDAV-Clients generieren. Jedes Passwort wird nur einmal angezeigt.", + "app_pw_label_placeholder": "Bezeichnung (z.B. Thunderbird, macOS)", + "generate": "Generieren", + "generating": "Generieren…", + "new_password_for": "Neues Passwort für", + "copy_warning": "Kopieren Sie dieses Passwort jetzt. Sie können es nicht erneut anzeigen.", + "copy_to_clipboard": "In Zwischenablage kopieren", + "col_label": "Bezeichnung", + "col_created": "Erstellt", + "col_last_used": "Zuletzt verwendet", + "col_status": "Status", + "active": "Aktiv", + "revoked": "Widerrufen", + "revoke_title": "Widerrufen", + "no_app_passwords": "Noch keine App-Passwörter.", + "client_sessions": "Client-Sitzungen", + "client_sessions_desc": "Automatisch generiert beim Verbinden eines Nextcloud-kompatiblen Clients.", + "col_client": "Client", + "never": "Nie", + "just_now": "Gerade eben", + "minutes_ago": "vor {{n}} Min", + "hours_ago": "vor {{n}} Std", + "days_ago": "vor {{n}} Tagen", + "edit_profile": "Profil bearbeiten", + "edit_oidc_managed": "Um Ihre Informationen (Name, Vorname, Profilbild, …) zu ändern, aktualisieren Sie sie bitte bei Ihrem Identity-Provider. Ihre Änderungen erscheinen bei der nächsten Anmeldung.", + "username_claim_hint": "2–64 Zeichen, Buchstaben / Ziffern / Punkt / Bindestrich / Unterstrich. Nach der Wahl kann der Benutzername nicht mehr geändert werden (DAV/NextCloud-Clients hängen davon ab).", + "username_already_claimed": "Benutzername ist gesetzt und kann nicht geändert werden (DAV/NextCloud-Clients hängen davon ab).", + "given_name": "Vorname", + "family_name": "Nachname", + "notify_on_share": "Mich per E-Mail benachrichtigen, wenn jemand mit mir teilt", + "notify_on_share_hint": "Wenn deaktiviert, werden Freigaben weiterhin in deinem Konto angezeigt — du erhältst nur keine E-Mail dazu.", + "save_profile": "Änderungen speichern", + "profile_saved": "Profil aktualisiert", + "profile_no_changes": "Keine Änderungen zu speichern.", + "profile_save_failed": "Speichern fehlgeschlagen", + "username_taken_error": "Dieser Benutzername ist bereits vergeben.", + "username_immutable_error": "Ihr Benutzername ist bereits gesetzt und kann hier nicht geändert werden. Wenden Sie sich an einen Administrator, wenn Sie umbenennen möchten.", + "change_password": "Passwort ändern", + "current_password": "Aktuelles Passwort", + "new_password": "Neues Passwort", + "min_8_chars": "Mindestens 8 Zeichen", + "confirm_password": "Neues Passwort bestätigen", + "update_password": "Passwort aktualisieren", + "updating": "Aktualisierung…", + "password_updated": "Passwort erfolgreich aktualisiert", + "passwords_no_match": "Passwörter stimmen nicht überein", + "password_too_short": "Passwort muss mindestens 8 Zeichen haben", + "password_change_failed": "Passwort ändern fehlgeschlagen", + "error_network": "Netzwerkfehler: {{message}}", + "error_label_required": "Bitte Bezeichnung eingeben", + "error_create_pw": "App-Passwort erstellen fehlgeschlagen", + "confirm_revoke": "App-Passwort \"{{label}}\" widerrufen? Clients werden nicht mehr funktionieren.", + "error_revoke": "Widerrufen fehlgeschlagen", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Wird hochgeladen...", + "files": "Dateien", + "complete": "{{count}} / {{total}} hochgeladen" + }, + "storage_quota_exceeded": "Speicherplatz erschöpft", + "sharedwithme": { + "pageTitle": "Mit mir geteilt", + "pageDescription": "Dateien und Ordner, die andere Benutzer mit Ihnen geteilt haben", + "emptyStateTitle": "Noch nichts mit Ihnen geteilt", + "emptyStateDesc": "Elemente, die andere Benutzer mit Ihnen teilen, erscheinen hier", + "loadMore": "Mehr laden", + "sharedBy": "Geteilt von", + "colName": "Name", + "colType": "Typ", + "colSharedBy": "Geteilt von", + "colDate": "Datum der Freigabe", + "colPermissions": "Berechtigungen" + }, + "groupby": { + "none": "Keine", + "title": "Gruppieren nach", + "owner": "Eigentümer", + "shareDate": "Freigabedatum", + "type": "Typ", + "type.folders": "Ordner", + "accessedAt": "Zugriffsdatum", + "modifiedAt": "Änderungsdatum", + "createdAt": "Erstellungsdatum", + "size": "Größe", + "favoriteDate": "Datum der Markierung", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Neu" + }, + "dateBucket": { + "today": "Heute", + "last7days": "Letzte 7 Tage", + "last30days": "Letzte 30 Tage" + }, + "groups": { + "title": "Gruppen verwalten", + "create_button": "Gruppe erstellen", + "create_dialog_title": "Neue Gruppe", + "edit_dialog_title": "Gruppe umbenennen", + "name_label": "Name", + "name_placeholder": "engineering", + "description_label": "Beschreibung (optional)", + "members_section": "Mitglieder", + "add_member_placeholder": "Benutzer oder Gruppe hinzufügen…", + "no_members": "Noch keine Mitglieder.", + "remove_member": "Entfernen", + "delete_group": "Gruppe löschen", + "delete_confirm": "Die Gruppe „{name}\" löschen? Auf diese Gruppe verweisende Berechtigungen werden widerrufen.", + "empty_state": "Noch keine Gruppen.", + "load_more": "Mehr laden", + "back_to_list": "Zurück", + "loading": "Wird geladen…", + "virtual_badge": "System", + "member_count_zero": "Keine Mitglieder", + "member_count_one": "1 Mitglied", + "member_count_other": "{count} Mitglieder", + "delete_confirm_label": "Tippe den Gruppennamen zur Bestätigung ein:", + "delete_confirm_mismatch": "Tippe den Gruppennamen exakt zur Bestätigung ein.", + "virtual_internal_name": "Intern", + "members_loading": "Mitglieder werden geladen…", + "members_empty": "Keine Mitglieder", + "virtual_internal_explanation": "Jeder interne Benutzer auf diesem Server" + }, + "myshares": { + "copyLink": "Link kopieren", + "deleteLink": "Link löschen", + "notifyByEmail": "Per E-Mail benachrichtigen", + "notifyFailed": "Benachrichtigung konnte nicht gesendet werden.", + "notifyGroupMembers": "Gruppenmitglieder benachrichtigen", + "notifyRateLimited": "Zu viele Benachrichtigungen für diesen Empfänger — versuchen Sie es später erneut.", + "removeAccess": "Zugriff entfernen", + "resendInvitation": "Einladungs-E-Mail erneut senden" + }, + "sort": { + "asc": "aufsteigend", + "desc": "absteigend" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/en.json b/frontend/static/locales/en.json new file mode 100644 index 00000000..c829bf57 --- /dev/null +++ b/frontend/static/locales/en.json @@ -0,0 +1,1027 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "This sign-in link is no longer valid", + "expired_body": "The link may have expired or already been used. We can send you a fresh one — it'll arrive in your inbox in a few seconds.", + "resend_to": "Send a fresh link to {{email}}", + "generic_unavailable": "This sign-in link is no longer valid. It may have already been used or expired. Request a fresh link from the login page.", + "service_unavailable": "Magic-link sign-in is not enabled on this server.", + "internal_error": "Something went wrong while signing you in. Please try again.", + "resend_failure": "Something went wrong while sending the link. Please try again.", + "cross_browser_title": "Continue signing in on this device?", + "cross_browser_body": "You opened this sign-in link in a different browser or device than the one where you requested it.", + "cross_browser_warning": "If you requested this link, it's safe to continue. If you didn't, close this page — clicking Continue would sign someone else into your account.", + "cross_browser_continue": "Continue and sign in", + "resend_confirmation_title": "Check your inbox", + "resend_confirmation_body": "If the sign-in link belonged to an active account, a fresh link has just been sent. Please check your inbox.", + "return_link": "Return to OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", + "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen it by clicking the link below:\n{{link}}\n\nThe link works once and expires in {{ttl_hours}} hours.\nIf you didn't expect this invitation, you can safely ignore this message.\n\n— OxiCloud" + }, + "login": { + "subject": "Sign in to OxiCloud", + "body": "Hello,\n\nUse the link below to sign in to OxiCloud. The link works once and expires in {{ttl_minutes}} minutes. Open it on the same device where you requested it.\n\n{{link}}\n\nIf you didn't request this sign-in link, you can safely ignore this message — no further action is needed.\n\n— OxiCloud" + }, + "kind_file": "file", + "kind_folder": "folder", + "english_fallback_divider": "--- English version below ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", + "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen OxiCloud to see your new share:\n{{login_link}}\n\nYou may have additional new shares from {{inviter}} — sign in to see all your shared items.\n\n— OxiCloud\n\nYou're receiving this message because you have an OxiCloud account and your share-notification preference is on. You can turn it off in your profile (Email me when someone shares with me)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalist cloud storage system" + }, + "myshares": { + "resendInvitation": "Resend invitation email", + "notifyByEmail": "Notify by email", + "notifyGroupMembers": "Notify group members", + "notifyRateLimited": "Too many notifications for this recipient — try again later.", + "notifyFailed": "Could not send notification.", + "removeAccess": "Remove access", + "copyLink": "Copy link", + "deleteLink": "Delete link" + }, + "nav": { + "files": "Files", + "shared": "My shares", + "sharedwithme": "Shared with me", + "recent": "Recent", + "favorites": "Favorites", + "photos": "Photos", + "music": "Music", + "trash": "Trash" + }, + "photos": { + "empty_state": "No photos yet", + "empty_hint": "Upload images or videos to see them here", + "items_selected": "selected", + "view_daily": "Day", + "view_monthly": "Month", + "view_yearly": "Year" + }, + "music": { + "create_playlist": "Create Playlist", + "playlists": "Playlists", + "no_playlists": "No playlists yet", + "empty_hint": "Create your first playlist to start organizing your music", + "select_playlist": "Select a playlist", + "select_hint": "Choose a playlist from the sidebar or create a new one", + "add_tracks": "Add Tracks", + "add_to_playlist": "Add to Playlist", + "add": "Add", + "added": "Added!", + "added_to_playlist": "added to playlist", + "load_error": "Error loading playlists", + "add_error": "Could not add tracks to playlist", + "no_playlists_yet": "No playlists yet. Create one first!", + "selected_files": "Selected:", + "no_tracks": "No tracks in this playlist", + "unknown_artist": "Unknown Artist", + "unknown_title": "Unknown", + "confirm_delete": "Delete this playlist?", + "playlist_name": "Playlist name", + "create": "Create", + "delete": "Delete", + "share": "Share", + "edit": "Edit", + "play_all": "Play All", + "shuffle": "Shuffle", + "repeat": "Repeat", + "repeat_one": "Repeat One", + "queue": "Queue", + "queue_empty": "Queue is empty", + "not_playing": "Not playing", + "play": "Play", + "pause": "Pause", + "previous": "Previous", + "next": "Next", + "volume": "Volume", + "mute": "Mute", + "unmute": "Unmute", + "title": "Title", + "artist": "Artist", + "album": "Album", + "tracks": "tracks", + "share_with_user": "User ID or email", + "playback_error": "Playback failed", + "error": "Error", + "remove": "Remove", + "track_removed": "Track removed", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "remove_share": "Remove share", + "can_write": "Can edit", + "read_only": "Read only", + "public": "Public", + "private": "Private", + "toggle_public": "Visibility", + "make_public": "Make public", + "make_private": "Make private", + "set_cover": "Set cover", + "cover_updated": "Cover updated", + "search_audio": "Search audio files…", + "no_audio_files": "No audio files found", + "selected": "selected", + "loading": "Loading…", + "search_error": "Could not load audio files", + "adding": "Adding…" + }, + "actions": { + "search": "Search files...", + "new_folder": "New folder", + "upload": "Upload", + "upload_files": "Upload files", + "upload_folder": "Upload folder", + "upload.uploading": "Uploading...", + "upload.complete": "{count} / {total} uploaded", + "upload.files": "files", + "rename": "Rename", + "move": "Move to...", + "move_to": "Move to", + "delete": "Delete", + "download": "Download", + "view": "View", + "cancel": "Cancel", + "confirm": "Confirm", + "share": "Share", + "favorite": "Add to favorites", + "unfavorite": "Remove from favorites", + "copy": "Copy", + "notify": "Notify", + "send": "Send", + "clear_recent": "Clear recent", + "logout": "Log out", + "create": "Create", + "search_btn": "Search", + "close": "Close", + "delete_permanently": "Delete permanently", + "empty_trash": "Empty trash", + "open_parent_folder": "Go to parent folder", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Appearance", + "about": "About OxiCloud", + "about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private.", + "admin_panel": "Admin Panel", + "profile": "My Profile", + "role_user": "User", + "theme": { + "light": "Light", + "dark": "Dark", + "auto": "Like OS" + }, + "manage_groups": "Manage groups" + }, + "share": { + "dialogTitle": "Share Link", + "linkLabel": "Share Link:", + "copyLink": "Copy", + "permissions": "Permissions:", + "permissionRead": "Read", + "permissionWrite": "Write", + "permissionReshare": "Reshare", + "password": "Password Protection:", + "generatePassword": "Generate", + "expiration": "Expiration Date:", + "update": "Update Share", + "remove": "Remove Share", + "notifyTitle": "Send Notification", + "notifyEmailLabel": "Email Address:", + "notifyMessageLabel": "Message (optional):", + "notifySend": "Send Notification", + "shareWithOthers": "Share with others", + "sharePublicly": "Share publicly", + "shareSettings": "Sharing settings", + "shareCopied": "Link copied to clipboard", + "shareCreated": "Share link created successfully", + "shareUpdated": "Share settings updated successfully", + "shareRemoved": "Share removed successfully", + "inviteByEmail": "Invite by email — invitation will be sent", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Share Link", + "share_linkLabel": "Share Link:", + "share_copyLink": "Copy", + "share_permissions": "Permissions:", + "share_permissionRead": "Read", + "share_permissionWrite": "Write", + "share_permissionReshare": "Reshare", + "share_password": "Password Protection:", + "share_generatePassword": "Generate", + "share_expiration": "Expiration Date:", + "share_update": "Update Share", + "share_remove": "Remove Share", + "share_notifyTitle": "Send Notification", + "share_notifyEmailLabel": "Email Address:", + "share_notifyMessageLabel": "Message (optional):", + "share_notifySend": "Send Notification", + "shared": { + "backToFiles": "Back to Files", + "pageTitle": "Shared Resources", + "pageDescription": "Manage your shared files and folders", + "filterType": "Type:", + "filterAll": "All", + "filterFiles": "Files", + "filterFolders": "Folders", + "sortBy": "Sort by:", + "sortByName": "Name", + "sortByDate": "Date shared", + "sortByExpiration": "Expiration", + "search": "Search", + "colName": "Name", + "colType": "Type", + "colDateShared": "Date Shared", + "colExpiration": "Expiration", + "colPermissions": "Permissions", + "colPassword": "Password", + "colActions": "Actions", + "emptyStateTitle": "No shared resources yet", + "emptyStateDesc": "When you share files or folders, they will appear here", + "goToFiles": "Go to Files", + "typeFile": "File", + "typeFolder": "Folder", + "noExpiration": "No expiration", + "hasPassword": "Yes", + "noPassword": "No", + "editShare": "Edit Share", + "notifyShare": "Notify Someone", + "copyLink": "Copy Link", + "removeShare": "Remove Share", + "linkCopied": "Link copied to clipboard!", + "linkCopyFailed": "Failed to copy link", + "itemUpdated": "Share settings updated successfully", + "itemRemoved": "Share removed successfully", + "invalidEmail": "Please enter a valid email address", + "notificationSent": "Notification sent successfully", + "notificationFailed": "Failed to send notification", + "shared_backToFiles": "Back to Files", + "shared_pageTitle": "Shared Resources", + "shared_pageDescription": "Manage your shared files and folders", + "shared_filterType": "Type:", + "shared_filterAll": "All", + "shared_filterFiles": "Files", + "shared_filterFolders": "Folders", + "shared_sortBy": "Sort by:", + "shared_sortByName": "Name", + "shared_sortByDate": "Date shared", + "shared_sortByExpiration": "Expiration", + "shared_search": "Search", + "shared_colName": "Name", + "shared_colType": "Type", + "shared_colDateShared": "Date Shared", + "shared_colExpiration": "Expiration", + "shared_colPermissions": "Permissions", + "shared_colPassword": "Password", + "shared_colActions": "Actions", + "shared_emptyStateTitle": "No shared resources yet", + "shared_emptyStateDesc": "When you share files or folders, they will appear here", + "shared_goToFiles": "Go to Files", + "shared_typeFile": "File", + "shared_typeFolder": "Folder", + "shared_noExpiration": "No expiration", + "shared_hasPassword": "Yes", + "shared_noPassword": "No", + "shared_editShare": "Edit Share", + "shared_notifyShare": "Notify Someone", + "shared_copyLink": "Copy Link", + "shared_removeShare": "Remove Share", + "shared_linkCopied": "Link copied to clipboard!", + "shared_linkCopyFailed": "Failed to copy link", + "shared_itemUpdated": "Share settings updated successfully", + "shared_itemRemoved": "Share removed successfully", + "shared_invalidEmail": "Please enter a valid email address", + "shared_notificationSent": "Notification sent successfully", + "shared_notificationFailed": "Failed to send notification" + }, + "files": { + "name": "Name", + "type": "Type", + "size": "Size", + "modified": "Modified", + "no_files": "No files in this folder", + "empty_hint": "Upload files or create folders to get started", + "loading": "Loading files…", + "view_grid": "Grid view", + "view_list": "List view", + "file_types": { + "document": "Document", + "image": "Image", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Text", + "folder": "Folder", + "spreadsheet": "Spreadsheet", + "presentation": "Presentation", + "archive": "Archive", + "installer": "Installer", + "code": "Code" + }, + "owner": "Owner" + }, + "dialogs": { + "rename_folder": "Rename folder", + "rename_file": "Rename file", + "new_name": "New name", + "new_folder_title": "New folder", + "folder_name": "Folder name", + "folder_placeholder": "My folder", + "rename_title": "Rename", + "move_file": "Move file", + "move_folder": "Move folder", + "select_destination": "Select destination folder:", + "select_this_folder": "Select this folder", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "root": "Root", + "delete_confirmation": "Are you sure you want to delete", + "and_contents": "and all its contents", + "no_undo": "This action cannot be undone", + "confirm_title": "Confirm action", + "confirm_delete": "Move to trash", + "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", + "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", + "confirm_permanent_delete": "Delete permanently", + "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", + "confirm_empty_trash": "Empty trash", + "confirm_delete_share": "Delete share link", + "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", + "share_file": "Share File", + "share_folder": "Share Folder", + "existing_shares": "Existing Shares", + "share_options": "Share Options", + "password": "Password", + "expiration": "Expiration", + "permissions": "Permissions", + "generated_link": "Generated Link", + "notify": "Send Notification", + "recipient": "Recipient", + "message": "Message", + "move_to_home": "Move to Home folder" + }, + "dropzone": { + "drag_files": "Drag files here or click to select", + "drop_files": "Drop files to upload" + }, + "permissions": { + "read": "Read", + "write": "Write", + "reshare": "Reshare" + }, + "errors": { + "file_not_found": "File not found", + "folder_not_found": "Folder not found", + "delete_error": "Error deleting", + "upload_error": "Error uploading file", + "rename_error": "Error renaming", + "move_error": "Error moving", + "empty_name": "Name cannot be empty", + "name_exists": "A file or folder with that name already exists", + "generic_error": "An error has occurred", + "group_name_invalid": "Group name must match the email-prefix format (letters, digits, dot, dash, underscore; 1–64 chars).", + "group_cycle": "This member would create a circular group reference.", + "group_depth_exceeded": "This nesting depth exceeds the maximum allowed (8).", + "group_virtual_immutable": "The 'Internal' group is system-managed and cannot be modified.", + "group_not_found": "Group not found.", + "group_name_taken": "A group with this name already exists." + }, + "breadcrumb": { + "home": "Home" + }, + "trash": { + "empty_trash": "Empty Trash", + "empty_state": "Trash is empty", + "original_location": "Original location", + "deleted_date": "Deletion date", + "remaining": "Remaining", + "actions": "Actions", + "restore": "Restore", + "delete_permanently": "Delete permanently", + "empty_confirm": "Are you sure you want to empty the trash? This will permanently delete all items.", + "groupby": { + "remaining_days": "Remaining days", + "trashed_time": "Trashed time" + } + }, + "daysRemaining": { + "expired": "Expired", + "today": "Today", + "tomorrow": "Tomorrow", + "inDays": "{{count}} days" + }, + "expiryChip": { + "never": "Never expires", + "expired": "Expired", + "today": "Expires today", + "tomorrow": "Expires tomorrow", + "inDays": "Expires in {{count}} days", + "onDate": "Expires {{date}}" + }, + "auth": { + "login_title": "Sign in", + "username": "Username", + "username_placeholder": "Enter your username", + "login_identifier": "Username or email", + "login_identifier_placeholder": "Enter your username or email", + "password": "Password", + "password_placeholder": "Enter your password", + "login_button": "Sign in", + "no_account": "Don't have an account?", + "register": "Sign up", + "admin_setup": "First time?", + "setup": "Setup administrator", + "register_title": "Create account", + "email": "Email", + "email_placeholder": "Enter your email", + "confirm_password": "Confirm password", + "confirm_password_placeholder": "Confirm your password", + "register_button": "Create account", + "have_account": "Already have an account?", + "login": "Sign in", + "setup_title": "Initial setup", + "setup_step1": "Admin", + "setup_step2": "System", + "setup_step3": "Complete", + "admin_username": "Admin username", + "admin_email": "Admin email", + "admin_password": "Admin password", + "create_admin": "Create administrator", + "back_to_login": "Already set up?", + "admin_success": "Administrator account created successfully! You can now sign in.", + "account_success": "Account created successfully! You can now sign in.", + "passwords_mismatch": "Passwords do not match", + "admin_create_error": "Error creating administrator account", + "or": "or", + "sso_login": "Sign in with SSO", + "sso_login_provider": "Sign in with {{provider}}", + "magicLinkHint": "No password? Enter your email and we'll send you a one-time sign-in link.", + "magicLinkEmailLabel": "Email address", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "Send sign-in link", + "magicLinkSent": "If an account exists for that email, a sign-in link has been sent. Check your inbox.", + "magicLinkUnavailable": "Sign-in by email is not available on this server.", + "magicLinkNetworkError": "Could not reach the server: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "Storage", + "calculating": "Calculating...", + "used": "{{percentage}}% used ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "This file type cannot be previewed.", + "download_file": "Download file", + "zoom_in": "Zoom in", + "zoom_out": "Zoom out", + "zoom_reset": "Reset zoom" + }, + "language_selector": { + "title": "Welcome!", + "subtitle": "Select your language to continue", + "continue": "Continue", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "No favorites yet", + "empty_hint": "Star files or folders to add them to your favorites", + "add": "Add to favorites", + "remove": "Remove from favorites", + "added_title": "Added to favorites", + "added_msg": "added to favorites", + "removed_title": "Removed from favorites", + "removed_msg": "removed from favorites" + }, + "recent": { + "title": "Recent", + "clear": "Clear recent", + "accessed": "Accessed", + "empty_state": "No recent files", + "empty_hint": "Files you open will appear here", + "loadMore": "Load more" + }, + "notifications": { + "file_renamed": "File renamed", + "file_renamed_to": "File renamed to \"{{name}}\"", + "folder_renamed": "Folder renamed", + "folder_renamed_to": "Folder renamed to \"{{name}}\"", + "file_uploaded": "File uploaded", + "file_deleted": "File moved to trash", + "folder_deleted": "Folder moved to trash", + "item_deleted_permanently": "Item permanently deleted", + "trash_emptied": "Trash emptied successfully", + "title": "Notifications", + "empty": "No notifications", + "link_created": "Link created", + "share_success": "Shared link created successfully", + "upload_files_section_title": "Upload not available here", + "upload_files_section_body": "Go to the Files section to upload files" + }, + "batch": { + "one_selected": "1 item selected", + "n_selected": "{{count}} items selected", + "confirm_delete": "Are you sure you want to move {{count}} items to trash?", + "move_title": "Move {{count}} item(s)", + "add_favorites": "Add to favorites", + "move_copy": "Move or copy" + }, + "admin": { + "page_title": "Admin Panel", + "back_to_app": "Back to OxiCloud", + "loading": "Loading…", + "access_denied": "Access Denied", + "access_denied_desc": "Administrator privileges required to access this panel.", + "sign_in": "Sign in", + "tab_dashboard": "Dashboard", + "tab_users": "Users", + "tab_oidc": "SSO / OIDC", + "total_users": "Total Users", + "active_users": "Active Users", + "admins": "Admins", + "version": "Version", + "storage_overview": "Storage Overview", + "used": "Used", + "total_quota": "Total Quota", + "usage_pct": "Usage %", + "users_over_80": "Users >80% quota", + "users_over_quota": "Users over quota", + "system": "System", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quotas", + "enabled": "Enabled", + "disabled": "Disabled", + "active": "Active", + "off": "Off", + "allow_registration": "Allow public self-registration", + "registration_warning": "Public registration is disabled. Only admins can create new users.", + "user_management": "User Management", + "create_user": "Create User", + "col_user": "User", + "col_role": "Role", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Storage", + "col_last_login": "Last Login", + "col_actions": "Actions", + "loading_users": "Loading users…", + "failed_load_users": "Failed to load users", + "no_users_found": "No users found", + "showing_users": "Showing {{from}}-{{to}} of {{total}}", + "prev": "Prev", + "next": "Next", + "inactive": "Inactive", + "you_badge": "(you)", + "local": "Local", + "never": "Never", + "just_now": "Just now", + "minutes_ago": "{{n}}m ago", + "hours_ago": "{{n}}h ago", + "days_ago": "{{n}}d ago", + "edit_quota_title": "Edit quota", + "reset_password_title": "Reset password", + "toggle_role_title": "Toggle role", + "deactivate_title": "Deactivate", + "activate_title": "Activate", + "delete_title": "Delete", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "Enable SSO Authentication", + "provider_name": "Provider Name", + "issuer_url": "Issuer URL", + "issuer_url_hint": "OpenID Connect issuer URL of your identity provider", + "auto_discover": "Auto-discover", + "discovering": "Discovering…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Leave empty to keep current value", + "secret_configured": "A client secret is already configured", + "callback_url": "Callback URL", + "callback_url_hint": "(register in your IdP)", + "advanced_settings": "Advanced Settings", + "scopes": "Scopes", + "auto_provision": "Auto-provision users on first login", + "admin_groups": "Admin Groups", + "admin_groups_hint": "Comma-separated OIDC group names that map to admin role", + "disable_password": "Disable password login (OIDC only)", + "password_warning": "This will prevent ALL password-based logins!", + "test_btn": "Test", + "save_btn": "Save", + "saving": "Saving…", + "settings_saved": "Settings saved — OIDC is now {{status}}", + "quota_modal_title": "Update Storage Quota", + "quota_user_label": "User:", + "new_quota": "New Quota", + "quota_unlimited_hint": "Set to 0 for unlimited", + "cancel": "Cancel", + "create_user_title": "Create New User", + "username_label": "Username", + "username_placeholder": "johndoe", + "username_hint": "3–32 characters", + "password_label": "Password", + "password_placeholder": "Min 8 characters", + "email_label": "Email", + "email_optional": "(optional)", + "email_placeholder": "user@example.com (auto-generated if empty)", + "role_label": "Role", + "role_user": "User", + "role_admin": "Admin", + "quota_label": "Quota", + "creating": "Creating…", + "reset_pw_title": "Reset Password", + "new_password_label": "New Password", + "resetting": "Resetting…", + "reset_btn": "Reset", + "confirm_role_change": "Change role to {{role}}?", + "confirm_deactivate": "Are you sure you want to deactivate this user?", + "confirm_activate": "Are you sure you want to activate this user?", + "confirm_delete_user": "DELETE user \"{{name}}\"? This cannot be undone!", + "confirm_action": "Confirm Action", + "confirm_yes": "Confirm", + "confirm_no": "Cancel", + "error_username_short": "Username must be at least 3 characters", + "error_password_short": "Password must be at least 8 characters", + "error_generic": "Failed", + "error_network": "Network error: {{message}}", + "error_create_user": "Failed to create user", + "tab_storage": "Storage", + "storage_title": "Storage Backend", + "storage_current_backend": "Active Backend", + "storage_total_blobs": "Total Blobs", + "storage_total_size": "Total Size", + "storage_dedup_ratio": "Dedup Ratio", + "storage_backend": "Backend Type", + "storage_local": "Local Filesystem", + "storage_s3": "S3-Compatible", + "storage_provider_preset": "Provider Preset", + "storage_preset_custom": "Custom", + "storage_endpoint_url": "Endpoint URL", + "storage_endpoint_hint": "Leave empty for Amazon S3 default", + "storage_bucket": "Bucket", + "storage_region": "Region", + "storage_access_key": "Access Key ID", + "storage_secret_key": "Secret Access Key", + "storage_secret_configured": "A secret key is already configured", + "storage_key_placeholder": "Leave empty to keep current value", + "storage_path_style": "Force Path Style", + "storage_path_style_hint": "Required for MinIO and some S3-compatible providers", + "storage_test_connection": "Test Connection", + "storage_test_success": "Connection successful", + "storage_test_failure": "Connection failed", + "storage_save": "Save", + "storage_saved": "Storage settings saved successfully", + "storage_migration": "Backend Migration", + "storage_migration_coming_soon": "Backend migration will be available in a future update.", + "migration_status_label": "Status:", + "migration_start": "Start Migration", + "migration_pause": "Pause", + "migration_resume": "Resume", + "migration_verify": "Verify Integrity", + "migration_complete": "Finalize", + "migration_started": "Migration started", + "migration_paused_msg": "Migration paused", + "migration_resumed_msg": "Migration resumed", + "migration_completed_msg": "Migration finalized. Restart the server to use the new backend.", + "migration_verifying": "Verifying…", + "migration_verify_passed": "Verification passed", + "migration_verify_failed": "Verification failed", + "migration_failed_blobs": "failed blobs", + "testing": "Testing…", + "tab_plugins": "Plugins", + "plugins_title": "Plugins", + "plugins_disabled": "Plugins are disabled on this server. Set OXICLOUD_ENABLE_PLUGINS=true (and build with the \"plugins\" feature) to manage WASM plugins here.", + "plugins_install_title": "Install a plugin", + "plugins_install_intro": "Upload a plugin bundle (.zip) containing plugin.toml and its compiled WebAssembly module (.wasm). The manifest is validated and the module is probed before installation.", + "plugins_bundle_label": "Plugin bundle (.zip)", + "plugins_install": "Install plugin", + "plugins_installed_title": "Installed plugins", + "plugins_col_name": "Name", + "plugins_col_id": "ID", + "plugins_col_version": "Version", + "plugins_col_events": "Events", + "plugins_col_status": "Status", + "plugins_col_actions": "Actions", + "plugins_loading": "Loading plugins…", + "plugins_none": "No plugins installed.", + "plugins_enabled": "Enabled", + "plugins_disabled_badge": "Disabled", + "plugins_enable": "Enable", + "plugins_disable": "Disable", + "plugins_delete": "Delete", + "plugins_confirm_delete": "Delete plugin \"{{name}}\"? Its files will be removed from the server.", + "plugins_installing": "Installing…", + "plugins_installed": "Installed {{name}}.", + "plugins_install_missing_bundle": "Select a plugin bundle (.zip).", + "plugins_details": "Logs & details", + "plugins_back": "Back to plugins", + "plugins_retention_title": "Log retention", + "plugins_retention_intro": "Rotated log segments older than the retention window, or beyond the size cap, are pruned on a schedule.", + "plugins_retention_days": "Retention (days)", + "plugins_retention_max_mb": "Max log size (MB)", + "plugins_retention_save": "Save retention", + "plugins_retention_saved": "Retention saved.", + "plugins_retention_invalid": "Enter non-negative numbers.", + "plugins_logs_title": "Logs", + "plugins_logs_level_all": "All levels", + "plugins_logs_search": "Search messages…", + "plugins_logs_live": "Live", + "plugins_logs_clear": "Clear", + "plugins_logs_confirm_clear": "Clear all logs for this plugin?", + "plugins_logs_none": "No log entries.", + "plugins_logs_col_time": "Time", + "plugins_logs_col_level": "Level", + "plugins_logs_col_kind": "Kind", + "plugins_logs_col_invocation": "Invocation", + "plugins_logs_col_message": "Message", + "plugins_logs_showing": "Showing {{from}}–{{to}} of {{total}}", + "tab_smtp": "SMTP", + "smtp_title": "Outbound Email (SMTP)", + "smtp_intro": "SMTP is configured exclusively via environment variables (OXICLOUD_SMTP_*). The values below are read from the running server — to change them, edit the environment and restart OxiCloud.", + "smtp_enabled_label": "Status", + "smtp_enabled": "Enabled", + "smtp_disabled": "Disabled (host unset)", + "smtp_test_title": "Send a test email", + "smtp_test_intro": "Sends a hardcoded diagnostic message to the recipient below and reports the SMTP server's response so you can correlate it with your relay logs.", + "smtp_test_to": "Recipient address", + "smtp_send_test": "Send test email", + "smtp_sending": "Sending…", + "smtp_sent": "Test email sent.", + "smtp_send_failed": "Send failed.", + "smtp_server_code": "Server replied", + "smtp_test_missing_to": "Enter a recipient address.", + "smtp_not_configured": "SMTP is not configured on this server." + }, + "profile": { + "page_title": "Profile", + "back_to_app": "Back to OxiCloud", + "loading": "Loading…", + "not_authenticated": "Not Authenticated", + "not_authenticated_desc": "Please sign in to view your profile.", + "sign_in": "Sign in", + "role_admin": "Administrator", + "role_user": "User", + "account_details": "Account Details", + "username": "Username", + "email": "Email", + "role": "Role", + "last_login": "Last Login", + "storage": "Storage", + "used": "Used", + "quota": "Quota", + "usage": "Usage", + "unlimited": "Unlimited", + "app_passwords": "App Passwords", + "app_pw_desc": "Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.", + "app_pw_label_placeholder": "Label (e.g. Thunderbird, macOS)", + "generate": "Generate", + "generating": "Generating…", + "new_password_for": "New password for", + "copy_warning": "Copy this password now. You won't be able to see it again.", + "copy_to_clipboard": "Copy to clipboard", + "col_label": "Label", + "col_created": "Created", + "col_last_used": "Last Used", + "col_status": "Status", + "active": "Active", + "revoked": "Revoked", + "revoke_title": "Revoke", + "no_app_passwords": "No app passwords yet.", + "client_sessions": "Client sessions", + "client_sessions_desc": "Auto-generated when you connect a Nextcloud-compatible client.", + "col_client": "Client", + "never": "Never", + "just_now": "Just now", + "minutes_ago": "{{n}} min ago", + "hours_ago": "{{n}}h ago", + "days_ago": "{{n}} days ago", + "edit_profile": "Edit Profile", + "edit_oidc_managed": "To change your information (name, first name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.", + "username_claim_hint": "2–64 characters, letters/digits/dot/dash/underscore. Once chosen, the username can't be changed (DAV/NextCloud clients depend on it).", + "username_already_claimed": "Username is set and can't be changed (DAV/NextCloud clients depend on it).", + "given_name": "First name", + "family_name": "Last name", + "notify_on_share": "Email me when someone shares with me", + "notify_on_share_hint": "When unchecked, shares still appear in your account — you just won't get an email about them.", + "save_profile": "Save changes", + "profile_saved": "Profile updated", + "profile_no_changes": "No changes to save.", + "profile_save_failed": "Save failed", + "username_taken_error": "That username is already taken.", + "username_immutable_error": "Your username is already set and can't be changed here. Contact an administrator if you need a rename.", + "change_password": "Change Password", + "current_password": "Current Password", + "new_password": "New Password", + "min_8_chars": "At least 8 characters", + "confirm_password": "Confirm New Password", + "update_password": "Update Password", + "updating": "Updating…", + "password_updated": "Password updated successfully", + "passwords_no_match": "Passwords do not match", + "password_too_short": "Password must be at least 8 characters", + "password_change_failed": "Failed to change password", + "error_network": "Network error: {{message}}", + "error_label_required": "Please enter a label", + "error_create_pw": "Failed to create app password", + "confirm_revoke": "Revoke app password \"{{label}}\"? Clients using this password will stop working.", + "error_revoke": "Failed to revoke app password", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Uploading...", + "files": "files", + "complete": "{{count}} / {{total}} uploaded" + }, + "storage_quota_exceeded": "Storage quota exceeded", + "sharedwithme": { + "pageTitle": "Shared with me", + "pageDescription": "Files and folders others have shared with you", + "emptyStateTitle": "Nothing shared with you yet", + "emptyStateDesc": "Items shared with you by other users will appear here", + "loadMore": "Load more", + "sharedBy": "Shared by", + "colName": "Name", + "colType": "Type", + "colSharedBy": "Shared by", + "colDate": "Date shared", + "colPermissions": "Permissions" + }, + "groupby": { + "none": "None", + "byFiles": "By files", + "sharedWith": "Shared with", + "title": "Group by", + "type": "Type", + "type.folders": "Folders", + "owner": "Owner", + "shareDate": "Share date", + "favoriteDate": "Favorite date", + "accessedAt": "Accessed date", + "modifiedAt": "Modified date", + "createdAt": "Created date", + "size": "Size", + "justAdded": "New" + }, + "dateBucket": { + "today": "Today", + "last7days": "Last 7 days", + "last30days": "Last 30 days" + }, + "groups": { + "title": "Manage groups", + "create_button": "Create group", + "create_dialog_title": "New group", + "edit_dialog_title": "Rename group", + "name_label": "Name", + "name_placeholder": "engineering", + "description_label": "Description (optional)", + "members_section": "Members", + "members_loading": "Loading members…", + "members_empty": "No members", + "add_member_placeholder": "Add a user or group…", + "no_members": "No members yet.", + "remove_member": "Remove", + "delete_group": "Delete group", + "delete_confirm": "Delete the group \"{name}\"? Grants referencing this group will be revoked.", + "empty_state": "No groups yet.", + "load_more": "Load more", + "back_to_list": "Back", + "loading": "Loading…", + "virtual_badge": "System", + "member_count_zero": "no members", + "member_count_one": "1 member", + "member_count_other": "{count} members", + "delete_confirm_label": "Type the group name to confirm:", + "delete_confirm_mismatch": "Type the group name exactly to confirm.", + "virtual_internal_name": "Internal", + "virtual_internal_explanation": "Every internal user on this server" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/es.json b/frontend/static/locales/es.json new file mode 100644 index 00000000..3a1027ea --- /dev/null +++ b/frontend/static/locales/es.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Este enlace de inicio de sesión ya no es válido", + "expired_body": "Es posible que el enlace haya expirado o ya se haya utilizado. Podemos enviarte uno nuevo — llegará a tu bandeja de entrada en unos segundos.", + "resend_to": "Enviar un nuevo enlace a {{email}}", + "generic_unavailable": "Este enlace de inicio de sesión ya no es válido. Es posible que ya se haya usado o que haya expirado. Solicita uno nuevo desde la página de inicio de sesión.", + "service_unavailable": "El inicio de sesión por enlace mágico no está habilitado en este servidor.", + "internal_error": "Algo salió mal al iniciar sesión. Por favor, inténtalo de nuevo.", + "resend_failure": "Algo salió mal al enviar el enlace. Por favor, inténtalo de nuevo.", + "cross_browser_title": "¿Continuar el inicio de sesión en este dispositivo?", + "cross_browser_body": "Has abierto este enlace de inicio de sesión en un navegador o dispositivo diferente del que lo solicitó.", + "cross_browser_warning": "Si solicitaste este enlace, es seguro continuar. Si no, cierra esta página — hacer clic en Continuar iniciaría sesión a otra persona en tu cuenta.", + "cross_browser_continue": "Continuar e iniciar sesión", + "resend_confirmation_title": "Revisa tu bandeja de entrada", + "resend_confirmation_body": "Si el enlace de inicio de sesión pertenecía a una cuenta activa, se acaba de enviar uno nuevo. Por favor, revisa tu bandeja de entrada.", + "return_link": "Volver a OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", + "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nÁbrelo haciendo clic en el enlace de abajo:\n{{link}}\n\nEl enlace es de un solo uso y expira en {{ttl_hours}} horas.\nSi no esperabas esta invitación, puedes ignorar este mensaje.\n\n— OxiCloud" + }, + "login": { + "subject": "Inicia sesión en OxiCloud", + "body": "Hola,\n\nUsa el enlace de abajo para iniciar sesión en OxiCloud. El enlace es de un solo uso y expira en {{ttl_minutes}} minutos. Ábrelo en el mismo dispositivo donde lo solicitaste.\n\n{{link}}\n\nSi no solicitaste este enlace de inicio de sesión, puedes ignorar este mensaje — no se necesita ninguna acción adicional.\n\n— OxiCloud" + }, + "kind_file": "archivo", + "kind_folder": "carpeta", + "english_fallback_divider": "--- Versión en inglés a continuación ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", + "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nAbre OxiCloud para ver tu nuevo recurso compartido:\n{{login_link}}\n\nPuede que tengas más recursos compartidos nuevos de {{inviter}} — inicia sesión para ver todos tus elementos compartidos.\n\n— OxiCloud\n\nRecibes este mensaje porque tienes una cuenta de OxiCloud y la preferencia de notificación de recursos compartidos está activada. Puedes desactivarla en tu perfil (Enviarme un correo cuando alguien comparta conmigo)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Sistema de almacenamiento en la nube minimalista" + }, + "nav": { + "files": "Archivos", + "shared": "Compartidos", + "recent": "Recientes", + "favorites": "Favoritos", + "photos": "Fotos", + "music": "Música", + "trash": "Papelera", + "sharedwithme": "Compartidos conmigo" + }, + "photos": { + "empty_state": "Aún no hay fotos", + "empty_hint": "Sube imágenes o videos para verlos aquí", + "items_selected": "seleccionados", + "view_daily": "Día", + "view_monthly": "Mes", + "view_yearly": "Año" + }, + "music": { + "create_playlist": "Crear Lista", + "playlists": "Listas", + "no_playlists": "Sin listas aún", + "empty_hint": "Crea tu primera lista para empezar a organizar tu música", + "select_playlist": "Selecciona una lista", + "select_hint": "Elige una lista de la barra lateral o crea una nueva", + "add_tracks": "Añadir Pistas", + "no_tracks": "No hay pistas en esta lista", + "unknown_artist": "Artista Desconocido", + "unknown_title": "Desconocido", + "confirm_delete": "¿Eliminar esta lista?", + "playlist_name": "Nombre de la lista", + "create": "Crear", + "delete": "Eliminar", + "share": "Compartir", + "edit": "Editar", + "play_all": "Reproducir Todo", + "shuffle": "Aleatorio", + "repeat": "Repetir", + "repeat_one": "Repetir Una", + "queue": "Cola", + "queue_empty": "Cola vacía", + "not_playing": "No reproduciendo", + "play": "Reproducir", + "pause": "Pausar", + "previous": "Anterior", + "next": "Siguiente", + "volume": "Volumen", + "mute": "Silenciar", + "unmute": "Activar sonido", + "title": "Título", + "artist": "Artista", + "album": "Álbum", + "tracks": "pistas", + "add": "Añadir", + "added": "¡Añadido!", + "added_to_playlist": "añadido a la lista", + "add_to_playlist": "Añadir a playlist", + "load_error": "Error al cargar listas", + "add_error": "No se pudieron añadir las pistas", + "no_playlists_yet": "No hay listas aún. ¡Crea una primero!", + "selected_files": "Seleccionados:", + "share_with_user": "ID de usuario o email", + "playback_error": "Error de reproducción", + "error": "Error", + "remove": "Eliminar", + "track_removed": "Pista eliminada", + "manage_shares": "Gestionar compartidos", + "no_shares": "Sin compartidos aún", + "remove_share": "Eliminar compartido", + "can_write": "Puede editar", + "read_only": "Solo lectura", + "public": "Pública", + "private": "Privada", + "toggle_public": "Visibilidad", + "make_public": "Hacer pública", + "make_private": "Hacer privada", + "set_cover": "Establecer portada", + "cover_updated": "Portada actualizada", + "search_audio": "Buscar archivos de audio…", + "no_audio_files": "No se encontraron archivos de audio", + "selected": "seleccionados", + "loading": "Cargando…", + "search_error": "No se pudieron cargar los archivos de audio", + "adding": "Añadiendo…" + }, + "share": { + "dialogTitle": "Compartir Enlace", + "linkLabel": "Enlace compartido:", + "copyLink": "Copiar", + "permissions": "Permisos:", + "permissionRead": "Lectura", + "permissionWrite": "Escritura", + "permissionReshare": "Recompartir", + "password": "Protección con contraseña:", + "generatePassword": "Generar", + "expiration": "Fecha de caducidad:", + "update": "Actualizar compartido", + "remove": "Eliminar compartido", + "notifyTitle": "Enviar notificación", + "notifyEmailLabel": "Dirección de correo:", + "notifyMessageLabel": "Mensaje (opcional):", + "notifySend": "Enviar notificación", + "shareWithOthers": "Compartir con otros", + "sharePublicly": "Compartir públicamente", + "shareSettings": "Configuración de compartido", + "shareCopied": "Enlace copiado al portapapeles", + "shareCreated": "Enlace compartido creado correctamente", + "shareUpdated": "Configuración de compartido actualizada", + "shareRemoved": "Compartido eliminado correctamente", + "inviteByEmail": "Invitar por correo — se enviará una invitación", + "directoryUnavailable": "Directorio de usuarios no disponible", + "linkNamePlaceholder": "Nombre del enlace (opcional)", + "newLink": "Nuevo enlace", + "noExpiry": "Sin caducidad", + "pending": "Pendiente", + "people": "Personas", + "publicLinks": "Enlaces públicos", + "role": { + "canEdit": "Puede editar", + "canManage": "Puede gestionar", + "canView": "Puede ver" + }, + "searchPlaceholder": "Buscar personas…", + "shareOf": "Compartir:", + "sharedLink": "Enlace compartido" + }, + "share_dialogTitle": "Compartir Enlace", + "share_linkLabel": "Enlace compartido:", + "share_copyLink": "Copiar", + "share_permissions": "Permisos:", + "share_permissionRead": "Lectura", + "share_permissionWrite": "Escritura", + "share_permissionReshare": "Recompartir", + "share_password": "Protección con contraseña:", + "share_generatePassword": "Generar", + "share_expiration": "Fecha de caducidad:", + "share_update": "Actualizar compartido", + "share_remove": "Eliminar compartido", + "share_notifyTitle": "Enviar notificación", + "share_notifyEmailLabel": "Dirección de correo:", + "share_notifyMessageLabel": "Mensaje (opcional):", + "share_notifySend": "Enviar notificación", + "shared": { + "backToFiles": "Volver a Archivos", + "pageTitle": "Recursos Compartidos", + "pageDescription": "Administra tus archivos y carpetas compartidos", + "filterType": "Tipo:", + "filterAll": "Todos", + "filterFiles": "Archivos", + "filterFolders": "Carpetas", + "sortBy": "Ordenar por:", + "sortByName": "Nombre", + "sortByDate": "Fecha compartido", + "sortByExpiration": "Caducidad", + "search": "Buscar", + "colName": "Nombre", + "colType": "Tipo", + "colDateShared": "Fecha compartido", + "colExpiration": "Caducidad", + "colPermissions": "Permisos", + "colPassword": "Contraseña", + "colActions": "Acciones", + "emptyStateTitle": "Aún no hay recursos compartidos", + "emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", + "goToFiles": "Ir a Archivos", + "typeFile": "Archivo", + "typeFolder": "Carpeta", + "noExpiration": "Sin caducidad", + "hasPassword": "Sí", + "noPassword": "No", + "editShare": "Editar compartido", + "notifyShare": "Notificar a alguien", + "copyLink": "Copiar enlace", + "removeShare": "Eliminar compartido", + "linkCopied": "¡Enlace copiado al portapapeles!", + "linkCopyFailed": "Error al copiar el enlace", + "itemUpdated": "Configuración de compartido actualizada", + "itemRemoved": "Compartido eliminado correctamente", + "invalidEmail": "Por favor, introduce una dirección de correo válida", + "notificationSent": "Notificación enviada correctamente", + "notificationFailed": "Error al enviar la notificación", + "shared_backToFiles": "Volver a Archivos", + "shared_pageTitle": "Recursos Compartidos", + "shared_pageDescription": "Administra tus archivos y carpetas compartidos", + "shared_filterType": "Tipo:", + "shared_filterAll": "Todos", + "shared_filterFiles": "Archivos", + "shared_filterFolders": "Carpetas", + "shared_sortBy": "Ordenar por:", + "shared_sortByName": "Nombre", + "shared_sortByDate": "Fecha compartido", + "shared_sortByExpiration": "Caducidad", + "shared_search": "Buscar", + "shared_colName": "Nombre", + "shared_colType": "Tipo", + "shared_colDateShared": "Fecha compartido", + "shared_colExpiration": "Caducidad", + "shared_colPermissions": "Permisos", + "shared_colPassword": "Contraseña", + "shared_colActions": "Acciones", + "shared_emptyStateTitle": "Aún no hay recursos compartidos", + "shared_emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", + "shared_goToFiles": "Ir a Archivos", + "shared_typeFile": "Archivo", + "shared_typeFolder": "Carpeta", + "shared_noExpiration": "Sin caducidad", + "shared_hasPassword": "Sí", + "shared_noPassword": "No", + "shared_editShare": "Editar compartido", + "shared_notifyShare": "Notificar a alguien", + "shared_copyLink": "Copiar enlace", + "shared_removeShare": "Eliminar compartido", + "shared_linkCopied": "¡Enlace copiado al portapapeles!", + "shared_linkCopyFailed": "Error al copiar el enlace", + "shared_itemUpdated": "Configuración de compartido actualizada", + "shared_itemRemoved": "Compartido eliminado correctamente", + "shared_invalidEmail": "Por favor, introduce una dirección de correo válida", + "shared_notificationSent": "Notificación enviada correctamente", + "shared_notificationFailed": "Error al enviar la notificación" + }, + "actions": { + "search": "Buscar archivos...", + "new_folder": "Nueva carpeta", + "upload": "Subir", + "upload_files": "Subir archivos", + "upload_folder": "Subir carpeta", + "upload.uploading": "Subiendo...", + "upload.complete": "{count} / {total} subidos", + "upload.files": "archivos", + "rename": "Renombrar", + "move": "Mover a...", + "move_to": "Mover a", + "delete": "Eliminar", + "download": "Descargar", + "view": "Ver", + "cancel": "Cancelar", + "confirm": "Confirmar", + "share": "Compartir", + "favorite": "Añadir a favoritos", + "unfavorite": "Quitar de favoritos", + "copy": "Copiar", + "notify": "Notificar", + "send": "Enviar", + "clear_recent": "Limpiar recientes", + "logout": "Cerrar sesión", + "create": "Crear", + "search_btn": "Buscar", + "close": "Cerrar", + "delete_permanently": "Eliminar permanentemente", + "empty_trash": "Vaciar papelera", + "open_parent_folder": "Ir a la carpeta padre", + "add": "Añadir", + "apply": "Aplicar", + "clear": "Limpiar", + "remove": "Quitar" + }, + "user_menu": { + "appearance": "Apariencia", + "about": "Acerca de OxiCloud", + "about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada.", + "admin_panel": "Panel de administración", + "profile": "Mi perfil", + "role_user": "Usuario", + "theme": { + "light": "Claro", + "dark": "Oscuro", + "auto": "Como el sistema" + }, + "manage_groups": "Gestionar grupos" + }, + "files": { + "name": "Nombre", + "type": "Tipo", + "size": "Tamaño", + "modified": "Modificado", + "no_files": "No hay archivos en esta carpeta", + "empty_hint": "Sube archivos o crea carpetas para comenzar", + "loading": "Cargando archivos…", + "view_grid": "Vista de cuadrícula", + "view_list": "Vista de lista", + "file_types": { + "document": "Documento", + "image": "Imagen", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Texto", + "folder": "Carpeta", + "spreadsheet": "Hoja de cálculo", + "presentation": "Presentación", + "archive": "Archivo comprimido", + "installer": "Instalador", + "code": "Código" + }, + "owner": "Propietario" + }, + "dialogs": { + "rename_folder": "Renombrar carpeta", + "rename_file": "Renombrar archivo", + "new_name": "Nuevo nombre", + "new_folder_title": "Nueva carpeta", + "folder_name": "Nombre de la carpeta", + "folder_placeholder": "Mi carpeta", + "rename_title": "Renombrar", + "move_file": "Mover archivo", + "move_folder": "Mover carpeta", + "select_destination": "Selecciona la carpeta destino:", + "select_this_folder": "Seleccionar esta carpeta", + "go_to_parent": ".. (carpeta superior)", + "no_subfolders": "Sin subcarpetas", + "root": "Raíz", + "delete_confirmation": "¿Estás seguro de que quieres eliminar", + "and_contents": "y todo su contenido", + "no_undo": "Esta acción no se puede deshacer", + "confirm_title": "Confirmar acción", + "confirm_delete": "Mover a papelera", + "confirm_delete_file": "¿Estás seguro de que quieres mover a la papelera el archivo \"{{name}}\"?", + "confirm_delete_folder": "¿Estás seguro de que quieres mover a la papelera la carpeta \"{{name}}\" y todo su contenido?", + "confirm_permanent_delete": "Eliminar permanentemente", + "confirm_permanent_delete_msg": "¿Estás seguro de que quieres eliminar permanentemente este elemento? Esta acción no se puede deshacer.", + "confirm_empty_trash": "Vaciar papelera", + "confirm_delete_share": "Eliminar enlace compartido", + "confirm_delete_share_msg": "¿Estás seguro de que quieres eliminar este enlace compartido?", + "share_file": "Compartir Archivo", + "share_folder": "Compartir Carpeta", + "existing_shares": "Compartidos Existentes", + "share_options": "Opciones de Compartición", + "password": "Contraseña", + "expiration": "Caducidad", + "permissions": "Permisos", + "generated_link": "Enlace Generado", + "notify": "Enviar Notificación", + "recipient": "Destinatario", + "message": "Mensaje", + "move_to_home": "Mover a la carpeta de inicio" + }, + "dropzone": { + "drag_files": "Arrastra archivos aquí o haz clic para seleccionar", + "drop_files": "Suelta los archivos para subirlos" + }, + "permissions": { + "read": "Lectura", + "write": "Escritura", + "reshare": "Recompartir" + }, + "errors": { + "file_not_found": "Archivo no encontrado", + "folder_not_found": "Carpeta no encontrada", + "delete_error": "Error al eliminar", + "upload_error": "Error al subir el archivo", + "rename_error": "Error al renombrar", + "move_error": "Error al mover", + "empty_name": "El nombre no puede estar vacío", + "name_exists": "Ya existe un archivo o carpeta con ese nombre", + "generic_error": "Ha ocurrido un error", + "group_name_invalid": "El nombre del grupo debe seguir el formato de prefijo de correo (letras, dígitos, punto, guión, guion bajo; 1–64 caracteres).", + "group_cycle": "Este miembro creaería una referencia circular entre grupos.", + "group_depth_exceeded": "Esta profundidad de anidamiento excede el máximo permitido (8).", + "group_virtual_immutable": "El grupo «Internal» es gestionado por el sistema y no se puede modificar.", + "group_not_found": "Grupo no encontrado.", + "group_name_taken": "Ya existe un grupo con este nombre." + }, + "breadcrumb": { + "home": "Inicio" + }, + "trash": { + "empty_trash": "Vaciar papelera", + "empty_state": "La papelera está vacía", + "original_location": "Ubicación original", + "deleted_date": "Fecha de eliminación", + "remaining": "Restante", + "actions": "Acciones", + "restore": "Restaurar", + "delete_permanently": "Eliminar permanentemente", + "empty_confirm": "¿Estás seguro de que quieres vaciar la papelera? Esta acción eliminará permanentemente todos los elementos.", + "groupby": { + "remaining_days": "Días restantes", + "trashed_time": "Fecha de eliminación" + } + }, + "daysRemaining": { + "expired": "Caducado", + "today": "Hoy", + "tomorrow": "Mañana", + "inDays": "{{count}} días" + }, + "expiryChip": { + "never": "Nunca caduca", + "expired": "Caducado", + "today": "Caduca hoy", + "tomorrow": "Caduca mañana", + "inDays": "Caduca en {{count}} días", + "onDate": "Caduca el {{date}}" + }, + "auth": { + "login_title": "Iniciar sesión", + "username": "Usuario", + "username_placeholder": "Ingresa tu nombre de usuario", + "login_identifier": "Usuario o correo electrónico", + "login_identifier_placeholder": "Ingresa tu usuario o correo electrónico", + "password": "Contraseña", + "password_placeholder": "Ingresa tu contraseña", + "login_button": "Iniciar sesión", + "no_account": "¿No tienes cuenta?", + "register": "Regístrate", + "admin_setup": "¿Primera vez?", + "setup": "Configurar administrador", + "register_title": "Crear cuenta", + "email": "Email", + "email_placeholder": "Ingresa tu email", + "confirm_password": "Confirmar contraseña", + "confirm_password_placeholder": "Confirma tu contraseña", + "register_button": "Crear cuenta", + "have_account": "¿Ya tienes cuenta?", + "login": "Iniciar sesión", + "setup_title": "Configuración inicial", + "setup_step1": "Admin", + "setup_step2": "Sistema", + "setup_step3": "Completado", + "admin_username": "Usuario administrador", + "admin_email": "Email administrador", + "admin_password": "Contraseña administrador", + "create_admin": "Crear administrador", + "back_to_login": "¿Ya está configurado?", + "admin_success": "¡Cuenta de administrador creada con éxito! Ahora puedes iniciar sesión.", + "account_success": "¡Cuenta creada con éxito! Ahora puedes iniciar sesión.", + "passwords_mismatch": "Las contraseñas no coinciden", + "admin_create_error": "Error al crear cuenta de administrador", + "or": "o", + "sso_login": "Iniciar sesión con SSO", + "sso_login_provider": "Iniciar sesión con {{provider}}", + "magicLinkHint": "¿Sin contraseña? Introduce tu correo electrónico y te enviaremos un enlace de inicio de sesión único.", + "magicLinkEmailLabel": "Correo electrónico", + "magicLinkEmailPlaceholder": "tu@ejemplo.com", + "magicLinkSubmit": "Enviar enlace de inicio de sesión", + "magicLinkSent": "Si existe una cuenta para ese correo, se ha enviado un enlace de inicio de sesión. Revisa tu bandeja de entrada.", + "magicLinkUnavailable": "El inicio de sesión por correo electrónico no está disponible en este servidor.", + "magicLinkNetworkError": "No se pudo conectar con el servidor: {{message}}", + "magicLinkToggle": "¿Sin contraseña? Recíbelo por correo", + "passwordsMatch": "Las contraseñas coinciden", + "capsLock": "Bloq Mayús activado" + }, + "storage": { + "title": "Almacenamiento", + "calculating": "Calculando...", + "used": "{{percentage}}% usado ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Este tipo de archivo no se puede previsualizar.", + "download_file": "Descargar archivo", + "zoom_in": "Acercar", + "zoom_out": "Alejar", + "zoom_reset": "Restablecer zoom" + }, + "language_selector": { + "title": "¡Bienvenido!", + "subtitle": "Selecciona tu idioma para continuar", + "continue": "Continuar", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Aún no hay favoritos", + "empty_hint": "Marca archivos o carpetas con estrella para añadirlos a favoritos", + "add": "Añadir a favoritos", + "remove": "Quitar de favoritos", + "added_title": "Añadido a favoritos", + "added_msg": "añadido a favoritos", + "removed_title": "Quitado de favoritos", + "removed_msg": "quitado de favoritos" + }, + "recent": { + "title": "Recientes", + "clear": "Limpiar recientes", + "accessed": "Accedido", + "empty_state": "No hay archivos recientes", + "empty_hint": "Los archivos que abras aparecerán aquí", + "loadMore": "Cargar más" + }, + "notifications": { + "file_renamed": "Archivo renombrado", + "file_renamed_to": "Archivo renombrado a \"{{name}}\"", + "folder_renamed": "Carpeta renombrada", + "folder_renamed_to": "Carpeta renombrada a \"{{name}}\"", + "file_uploaded": "Archivo subido", + "file_deleted": "Archivo movido a papelera", + "folder_deleted": "Carpeta movida a papelera", + "item_deleted_permanently": "Elemento eliminado permanentemente", + "trash_emptied": "Papelera vaciada correctamente", + "title": "Notificaciones", + "empty": "Sin notificaciones", + "link_created": "Enlace creado", + "share_success": "Enlace compartido creado correctamente", + "upload_files_section_title": "Subida no disponible aquí", + "upload_files_section_body": "Ve a la sección Archivos para subir archivos" + }, + "batch": { + "one_selected": "1 elemento seleccionado", + "n_selected": "{{count}} elementos seleccionados", + "confirm_delete": "¿Estás seguro de que quieres mover {{count}} elementos a la papelera?", + "move_title": "Mover {{count}} elemento(s)", + "add_favorites": "Añadir a favoritos", + "move_copy": "Mover o copiar" + }, + "admin": { + "page_title": "Panel de Administración", + "back_to_app": "Volver a OxiCloud", + "loading": "Cargando…", + "access_denied": "Acceso Denegado", + "access_denied_desc": "Se requieren privilegios de administrador para acceder a este panel.", + "sign_in": "Iniciar sesión", + "tab_dashboard": "Panel", + "tab_users": "Usuarios", + "tab_oidc": "SSO / OIDC", + "total_users": "Usuarios Totales", + "active_users": "Usuarios Activos", + "admins": "Administradores", + "version": "Versión", + "storage_overview": "Resumen de Almacenamiento", + "used": "Usado", + "total_quota": "Cuota Total", + "usage_pct": "Uso %", + "users_over_80": "Usuarios >80% cuota", + "users_over_quota": "Usuarios sobre cuota", + "system": "Sistema", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Cuotas", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "active": "Activo", + "off": "Inactivo", + "allow_registration": "Permitir registro público", + "registration_warning": "El registro público está deshabilitado. Solo los administradores pueden crear nuevos usuarios.", + "user_management": "Gestión de Usuarios", + "create_user": "Crear Usuario", + "col_user": "Usuario", + "col_role": "Rol", + "col_auth": "Auth", + "col_status": "Estado", + "col_storage": "Almacenamiento", + "col_last_login": "Último Acceso", + "col_actions": "Acciones", + "loading_users": "Cargando usuarios…", + "failed_load_users": "Error al cargar usuarios", + "no_users_found": "No se encontraron usuarios", + "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", + "prev": "Anterior", + "next": "Siguiente", + "inactive": "Inactivo", + "you_badge": "(tú)", + "local": "Local", + "never": "Nunca", + "just_now": "Ahora mismo", + "minutes_ago": "hace {{n}}m", + "hours_ago": "hace {{n}}h", + "days_ago": "hace {{n}}d", + "edit_quota_title": "Editar cuota", + "reset_password_title": "Restablecer contraseña", + "toggle_role_title": "Cambiar rol", + "deactivate_title": "Desactivar", + "activate_title": "Activar", + "delete_title": "Eliminar", + "sso_title": "Inicio de Sesión Único (OIDC / SSO)", + "enable_sso": "Habilitar autenticación SSO", + "provider_name": "Nombre del Proveedor", + "issuer_url": "URL del Emisor", + "issuer_url_hint": "URL del emisor OpenID Connect de tu proveedor de identidad", + "auto_discover": "Auto-descubrir", + "discovering": "Descubriendo…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Dejar vacío para mantener el valor actual", + "secret_configured": "Ya hay un client secret configurado", + "callback_url": "URL de Callback", + "callback_url_hint": "(registrar en tu IdP)", + "advanced_settings": "Configuración Avanzada", + "scopes": "Scopes", + "auto_provision": "Auto-provisionar usuarios en el primer inicio de sesión", + "admin_groups": "Grupos de Admin", + "admin_groups_hint": "Nombres de grupos OIDC separados por comas que mapean al rol de admin", + "disable_password": "Desactivar inicio de sesión con contraseña (solo OIDC)", + "password_warning": "¡Esto impedirá TODOS los inicios de sesión con contraseña!", + "test_btn": "Probar", + "save_btn": "Guardar", + "saving": "Guardando…", + "settings_saved": "Configuración guardada — OIDC ahora está {{status}}", + "quota_modal_title": "Actualizar Cuota de Almacenamiento", + "quota_user_label": "Usuario:", + "new_quota": "Nueva Cuota", + "quota_unlimited_hint": "Establecer 0 para ilimitado", + "cancel": "Cancelar", + "create_user_title": "Crear Nuevo Usuario", + "username_label": "Nombre de usuario", + "username_placeholder": "juanperez", + "username_hint": "3–32 caracteres", + "password_label": "Contraseña", + "password_placeholder": "Mín 8 caracteres", + "email_label": "Correo", + "email_optional": "(opcional)", + "email_placeholder": "usuario@ejemplo.com (auto-generado si vacío)", + "role_label": "Rol", + "role_user": "Usuario", + "role_admin": "Admin", + "quota_label": "Cuota", + "creating": "Creando…", + "reset_pw_title": "Restablecer Contraseña", + "new_password_label": "Nueva Contraseña", + "resetting": "Restableciendo…", + "reset_btn": "Restablecer", + "confirm_role_change": "¿Cambiar rol a {{role}}?", + "confirm_deactivate": "¿Estás seguro de que quieres desactivar este usuario?", + "confirm_activate": "¿Estás seguro de que quieres activar este usuario?", + "confirm_delete_user": "¿ELIMINAR usuario \"{{name}}\"? ¡Esto no se puede deshacer!", + "confirm_action": "Confirmar Acción", + "confirm_yes": "Confirmar", + "confirm_no": "Cancelar", + "error_username_short": "El nombre de usuario debe tener al menos 3 caracteres", + "error_password_short": "La contraseña debe tener al menos 8 caracteres", + "error_generic": "Error", + "error_network": "Error de red: {{message}}", + "error_create_user": "Error al crear usuario", + "tab_storage": "Almacenamiento", + "storage_title": "Backend de Almacenamiento", + "storage_current_backend": "Backend Activo", + "storage_total_blobs": "Total de Blobs", + "storage_total_size": "Tamaño Total", + "storage_dedup_ratio": "Ratio de Dedup", + "storage_backend": "Tipo de Backend", + "storage_local": "Sistema de Archivos Local", + "storage_s3": "Compatible con S3", + "storage_provider_preset": "Proveedor Preconfigurado", + "storage_preset_custom": "Personalizado", + "storage_endpoint_url": "URL del Endpoint", + "storage_endpoint_hint": "Dejar vacío para usar Amazon S3 por defecto", + "storage_bucket": "Bucket", + "storage_region": "Región", + "storage_access_key": "Access Key ID", + "storage_secret_key": "Secret Access Key", + "storage_secret_configured": "Ya hay una clave secreta configurada", + "storage_key_placeholder": "Dejar vacío para mantener el valor actual", + "storage_path_style": "Forzar Path Style", + "storage_path_style_hint": "Requerido para MinIO y algunos proveedores compatibles con S3", + "storage_test_connection": "Probar Conexión", + "storage_test_success": "Conexión exitosa", + "storage_test_failure": "Conexión fallida", + "storage_save": "Guardar", + "storage_saved": "Configuración de almacenamiento guardada correctamente", + "storage_migration": "Migración de Backend", + "storage_migration_coming_soon": "La migración de backend estará disponible en una futura actualización.", + "migration_status_label": "Estado:", + "migration_start": "Iniciar Migración", + "migration_pause": "Pausar", + "migration_resume": "Reanudar", + "migration_verify": "Verificar Integridad", + "migration_complete": "Finalizar", + "migration_started": "Migración iniciada", + "migration_paused_msg": "Migración pausada", + "migration_resumed_msg": "Migración reanudada", + "migration_completed_msg": "Migración finalizada. Reinicia el servidor para usar el nuevo backend.", + "migration_verifying": "Verificando…", + "migration_verify_passed": "Verificación exitosa", + "migration_verify_failed": "Verificación fallida", + "migration_failed_blobs": "blobs fallidos", + "testing": "Probando…", + "smtp_disabled": "Desactivado (host no configurado)", + "smtp_enabled": "Activado", + "smtp_enabled_label": "Estado", + "smtp_intro": "SMTP se configura exclusivamente a través de variables de entorno (OXICLOUD_SMTP_*). Los valores siguientes se leen del servidor en ejecución — para modificarlos, edita el entorno y reinicia OxiCloud.", + "smtp_not_configured": "SMTP no está configurado en este servidor.", + "smtp_send_failed": "Fallo al enviar.", + "smtp_send_test": "Enviar correo de prueba", + "smtp_sending": "Enviando…", + "smtp_sent": "Correo de prueba enviado.", + "smtp_server_code": "Respuesta del servidor", + "smtp_test_intro": "Envía un mensaje de diagnóstico predefinido al destinatario indicado abajo e informa de la respuesta del servidor SMTP para que puedas cruzarla con los registros de tu relay.", + "smtp_test_missing_to": "Introduce una dirección de destinatario.", + "smtp_test_title": "Enviar correo de prueba", + "smtp_test_to": "Dirección del destinatario", + "smtp_title": "Correo saliente (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Perfil", + "back_to_app": "Volver a OxiCloud", + "loading": "Cargando…", + "not_authenticated": "No Autenticado", + "not_authenticated_desc": "Inicia sesión para ver tu perfil.", + "sign_in": "Iniciar sesión", + "role_admin": "Administrador", + "role_user": "Usuario", + "account_details": "Detalles de la Cuenta", + "username": "Nombre de usuario", + "email": "Correo electrónico", + "role": "Rol", + "last_login": "Último acceso", + "storage": "Almacenamiento", + "used": "Usado", + "quota": "Cuota", + "usage": "Uso", + "unlimited": "Ilimitado", + "app_passwords": "Contraseñas de Aplicación", + "app_pw_desc": "Genera contraseñas para clientes WebDAV, CalDAV y CardDAV. Cada contraseña se muestra solo una vez.", + "app_pw_label_placeholder": "Etiqueta (ej. Thunderbird, macOS)", + "generate": "Generar", + "generating": "Generando…", + "new_password_for": "Nueva contraseña para", + "copy_warning": "Copia esta contraseña ahora. No podrás verla de nuevo.", + "copy_to_clipboard": "Copiar al portapapeles", + "col_label": "Etiqueta", + "col_created": "Creado", + "col_last_used": "Último uso", + "col_status": "Estado", + "active": "Activa", + "revoked": "Revocada", + "revoke_title": "Revocar", + "no_app_passwords": "Aún no hay contraseñas de aplicación.", + "client_sessions": "Sesiones de cliente", + "client_sessions_desc": "Generadas automáticamente al conectar un cliente compatible con Nextcloud.", + "col_client": "Cliente", + "never": "Nunca", + "just_now": "Ahora mismo", + "minutes_ago": "hace {{n}} min", + "hours_ago": "hace {{n}}h", + "days_ago": "hace {{n}} días", + "edit_profile": "Editar perfil", + "edit_oidc_managed": "Para cambiar tu información (nombre, apellidos, foto de perfil, …), actualízala en tu proveedor de identidad. Los cambios se aplicarán en tu próximo inicio de sesión.", + "username_claim_hint": "Entre 2 y 64 caracteres, letras / dígitos / punto / guion / subrayado. Una vez elegido, el nombre de usuario no se puede cambiar (los clientes DAV/NextCloud dependen de él).", + "username_already_claimed": "Nombre de usuario fijado y no modificable (los clientes DAV/NextCloud dependen de él).", + "given_name": "Nombre", + "family_name": "Apellidos", + "notify_on_share": "Enviarme un correo cuando alguien comparta conmigo", + "notify_on_share_hint": "Cuando esté desmarcado, los recursos compartidos seguirán apareciendo en tu cuenta — simplemente no recibirás un correo sobre ellos.", + "save_profile": "Guardar cambios", + "profile_saved": "Perfil actualizado", + "profile_no_changes": "Sin cambios que guardar.", + "profile_save_failed": "Error al guardar", + "username_taken_error": "Ese nombre de usuario ya está en uso.", + "username_immutable_error": "Tu nombre de usuario ya está fijado y no se puede cambiar aquí. Contacta con un administrador si necesitas renombrarlo.", + "change_password": "Cambiar Contraseña", + "current_password": "Contraseña Actual", + "new_password": "Nueva Contraseña", + "min_8_chars": "Al menos 8 caracteres", + "confirm_password": "Confirmar Nueva Contraseña", + "update_password": "Actualizar Contraseña", + "updating": "Actualizando…", + "password_updated": "Contraseña actualizada correctamente", + "passwords_no_match": "Las contraseñas no coinciden", + "password_too_short": "La contraseña debe tener al menos 8 caracteres", + "password_change_failed": "Error al cambiar la contraseña", + "error_network": "Error de red: {{message}}", + "error_label_required": "Introduce una etiqueta", + "error_create_pw": "Error al crear contraseña de aplicación", + "confirm_revoke": "¿Revocar contraseña \"{{label}}\"? Los clientes que la usen dejarán de funcionar.", + "error_revoke": "Error al revocar contraseña", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Subiendo...", + "files": "archivos", + "complete": "{{count}} / {{total}} subidos" + }, + "storage_quota_exceeded": "Cuota de almacenamiento superada", + "sharedwithme": { + "pageTitle": "Compartido conmigo", + "pageDescription": "Archivos y carpetas que otros usuarios han compartido contigo", + "emptyStateTitle": "Aún no hay nada compartido contigo", + "emptyStateDesc": "Los elementos que otros usuarios compartan contigo aparecerán aquí", + "loadMore": "Cargar más", + "sharedBy": "Compartido por", + "colName": "Nombre", + "colType": "Tipo", + "colSharedBy": "Compartido por", + "colDate": "Fecha de compartición", + "colPermissions": "Permisos" + }, + "groupby": { + "none": "Ninguno", + "title": "Agrupar por", + "owner": "Propietario", + "shareDate": "Fecha de compartición", + "type": "Tipo", + "type.folders": "Carpetas", + "accessedAt": "Fecha de acceso", + "modifiedAt": "Fecha de modificación", + "createdAt": "Fecha de creación", + "size": "Tamaño", + "favoriteDate": "Fecha de favorito", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nuevo" + }, + "dateBucket": { + "today": "Hoy", + "last7days": "Últimos 7 días", + "last30days": "Últimos 30 días" + }, + "groups": { + "title": "Gestionar grupos", + "create_button": "Crear grupo", + "create_dialog_title": "Nuevo grupo", + "edit_dialog_title": "Renombrar grupo", + "name_label": "Nombre", + "name_placeholder": "ingenieria", + "description_label": "Descripción (opcional)", + "members_section": "Miembros", + "add_member_placeholder": "Añadir un usuario o grupo…", + "no_members": "Aún no hay miembros.", + "remove_member": "Eliminar", + "delete_group": "Eliminar grupo", + "delete_confirm": "¿Eliminar el grupo «{name}»? Se revocarán las concesiones que hagan referencia a este grupo.", + "empty_state": "Aún no hay grupos.", + "load_more": "Cargar más", + "back_to_list": "Volver", + "loading": "Cargando…", + "virtual_badge": "Sistema", + "member_count_zero": "Sin miembros", + "member_count_one": "1 miembro", + "member_count_other": "{count} miembros", + "delete_confirm_label": "Escribe el nombre del grupo para confirmar:", + "delete_confirm_mismatch": "Escribe el nombre del grupo exactamente para confirmar.", + "virtual_internal_name": "Interno", + "members_loading": "Cargando miembros…", + "members_empty": "Sin miembros", + "virtual_internal_explanation": "Todos los usuarios internos de este servidor" + }, + "myshares": { + "copyLink": "Copiar enlace", + "deleteLink": "Eliminar enlace", + "notifyByEmail": "Notificar por correo", + "notifyFailed": "No se pudo enviar la notificación.", + "notifyGroupMembers": "Notificar a los miembros del grupo", + "notifyRateLimited": "Demasiadas notificaciones para este destinatario — inténtalo más tarde.", + "removeAccess": "Quitar acceso", + "resendInvitation": "Reenviar correo de invitación" + }, + "sort": { + "asc": "ascendente", + "desc": "descendente" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error al realizar la búsqueda", + "cleanupCompleted": "Limpieza completada", + "cleanupCompletedBody": "Se ha borrado el historial de archivos recientes", + "batchCopy": "Copia en lote", + "batchCopyBody": "{{success}} copiados, {{errors}} fallidos", + "itemsCopied": "Elementos copiados", + "itemsCopiedBody": "{{count}} elementos copiados correctamente", + "batchMove": "Movimiento en lote", + "batchMoveBody": "{{success}} movidos, {{errors}} fallidos", + "itemsMoved": "Elementos movidos", + "itemsMovedBody": "{{count}} elementos movidos correctamente", + "batchDelete": "Borrado en lote", + "batchDeleteBody": "{{success}} movidos a la papelera, {{errors}} fallidos", + "movedToTrash": "Movido a la papelera", + "movedToTrashBody": "{{count}} elementos movidos a la papelera", + "trashItemsError": "No se pudieron mover los elementos a la papelera", + "preparingDownload": "Preparando descarga", + "preparingDownloadBody": "Preparando la descarga…", + "downloadItemsError": "No se pudieron descargar los elementos seleccionados", + "favoritesAddError": "No se pudieron añadir los elementos a favoritos", + "invalidEmail": "Introduce una dirección de correo válida", + "notificationSendError": "No se pudo enviar la notificación", + "folderCreated": "Carpeta creada", + "folderCreatedBody": "«{{name}}» creada correctamente", + "fileMoved": "Archivo movido", + "fileMovedBody": "Archivo movido correctamente", + "fileMoveError": "Error al mover el archivo: {{error}}", + "fileMoveErrorGeneric": "Error al mover el archivo", + "folderMoved": "Carpeta movida", + "folderMovedBody": "Carpeta movida correctamente", + "folderMoveError": "Error al mover la carpeta: {{error}}", + "folderMoveErrorGeneric": "Error al mover la carpeta", + "fileCopied": "Archivo copiado", + "fileCopiedBody": "Archivo copiado correctamente", + "fileCopyError": "Error al copiar el archivo: {{error}}", + "fileCopyErrorGeneric": "Error al copiar el archivo", + "folderRenamed": "Carpeta renombrada", + "folderRenamedBody": "Carpeta renombrada a «{{name}}»", + "fileTrashed": "Archivo movido a la papelera", + "fileTrashedBody": "«{{name}}» movido a la papelera", + "fileDeleted": "Archivo eliminado", + "fileDeletedBody": "«{{name}}» eliminado correctamente", + "fileDeleteError": "Error al eliminar el archivo", + "folderTrashed": "Carpeta movida a la papelera", + "folderTrashedBody": "«{{name}}» movida a la papelera", + "folderDeleted": "Carpeta eliminada", + "folderDeletedBody": "«{{name}}» eliminada correctamente", + "folderDeleteError": "Error al eliminar la carpeta", + "itemRestored": "Elemento restaurado", + "itemRestoredBody": "Elemento restaurado correctamente", + "itemRestoreError": "Error al restaurar el elemento", + "itemDeleted": "Elemento eliminado", + "itemDeletedBody": "Elemento eliminado permanentemente", + "itemDeleteError": "Error al eliminar el elemento", + "trashEmptied": "Papelera vaciada", + "trashEmptiedBody": "La papelera se ha vaciado correctamente", + "trashEmptyError": "Error al vaciar la papelera", + "cacheCleared": "Caché limpiada", + "cacheClearedBody": "Caché de búsqueda limpiada correctamente", + "cacheClearError": "Error al limpiar la caché de búsqueda", + "wopiOpenError": "No se pudo abrir el editor de documentos.", + "linkCopied": "Enlace copiado", + "linkCopiedBody": "Enlace copiado al portapapeles", + "linkCopyError": "No se pudo copiar el enlace", + "notificationSent": "Notificación enviada", + "notificationSentBody": "Notificación enviada a {{email}}" + } +} diff --git a/frontend/static/locales/fa.json b/frontend/static/locales/fa.json new file mode 100644 index 00000000..b99bd940 --- /dev/null +++ b/frontend/static/locales/fa.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "این پیوند ورود دیگر معتبر نیست", + "expired_body": "ممکن است پیوند منقضی شده یا قبلاً استفاده شده باشد. می‌توانیم پیوند جدیدی برایتان ارسال کنیم — ظرف چند ثانیه به صندوق ورودی شما می‌رسد.", + "resend_to": "ارسال پیوند جدید به {{email}}", + "generic_unavailable": "این پیوند ورود دیگر معتبر نیست. ممکن است قبلاً استفاده شده باشد یا منقضی شده باشد. پیوند جدیدی را از صفحهٔ ورود درخواست کنید.", + "service_unavailable": "ورود از طریق پیوند جادویی روی این سرور فعال نیست.", + "internal_error": "هنگام ورود خطایی رخ داد. لطفاً دوباره تلاش کنید.", + "resend_failure": "هنگام ارسال پیوند خطایی رخ داد. لطفاً دوباره تلاش کنید.", + "cross_browser_title": "آیا می‌خواهید ورود در این دستگاه ادامه یابد؟", + "cross_browser_body": "این پیوند ورود را در مرورگر یا دستگاهی متفاوت از جایی که درخواست کرده‌اید باز کرده‌اید.", + "cross_browser_warning": "اگر این پیوند را خودتان درخواست کرده‌اید، ادامه دادن ایمن است. در غیر این صورت این صفحه را ببندید — کلیک روی ادامه باعث ورود شخص دیگری به حساب شما خواهد شد.", + "cross_browser_continue": "ادامه و ورود", + "resend_confirmation_title": "صندوق ورودی خود را بررسی کنید", + "resend_confirmation_body": "اگر پیوند ورود متعلق به یک حساب فعال بوده، پیوند جدیدی هم اکنون ارسال شد. لطفاً صندوق ورودی خود را بررسی کنید.", + "return_link": "بازگشت به OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", + "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبا کلیک روی پیوند زیر آن را باز کنید:\n{{link}}\n\nپیوند یک‌بار مصرف است و در {{ttl_hours}} ساعت منقضی می‌شود.\nاگر منتظر این دعوت نبودید، می‌توانید این پیام را نادیده بگیرید.\n\n— OxiCloud" + }, + "login": { + "subject": "ورود به OxiCloud", + "body": "سلام،\n\nبرای ورود به OxiCloud از پیوند زیر استفاده کنید. پیوند یک‌بار مصرف است و در {{ttl_minutes}} دقیقه منقضی می‌شود. آن را در همان دستگاهی که درخواست کرده‌اید باز کنید.\n\n{{link}}\n\nاگر این پیوند ورود را درخواست نکرده‌اید، می‌توانید این پیام را نادیده بگیرید — اقدام دیگری لازم نیست.\n\n— OxiCloud" + }, + "kind_file": "فایل", + "kind_folder": "پوشه", + "english_fallback_divider": "--- نسخهٔ انگلیسی در پایین ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", + "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبرای دیدن اشتراک‌گذاری جدید خود، OxiCloud را باز کنید:\n{{login_link}}\n\nممکن است اشتراک‌گذاری‌های جدید دیگری از {{inviter}} داشته باشید — وارد شوید تا همه موارد به اشتراک گذاشته‌شده با خود را ببینید.\n\n— OxiCloud\n\nشما این پیام را دریافت می‌کنید زیرا حساب OxiCloud دارید و گزینه اعلان اشتراک‌گذاری شما روشن است. می‌توانید آن را در پروفایل خود خاموش کنید (وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "سیستم ذخیره‌سازی ابری ساده‌گرا" + }, + "nav": { + "files": "پرونده‌ها", + "shared": "هم‌رسانی‌های من", + "recent": "اخیر", + "favorites": "موردعلاقه‌ها", + "photos": "عکس‌ها", + "music": "موسیقی", + "trash": "سطل زباله", + "sharedwithme": "به اشتراک‌گذاشته شده با من" + }, + "photos": { + "empty_state": "هنوز عکسی نیست", + "empty_hint": "تصاویر یا ویدیوها را آپلود کنید تا اینجا نمایش داده شوند", + "items_selected": "انتخاب شده", + "view_daily": "روز", + "view_monthly": "ماه", + "view_yearly": "سال" + }, + "music": { + "create_playlist": "ایجاد فهرست پخش", + "playlists": "فهرست‌های پخش", + "no_playlists": "هنوز فهرست پخشی نیست", + "select_playlist": "یک فهرست پخش انتخاب کنید", + "select_hint": "از نوار کناری یک فهرست پخش انتخاب کنید یا یکی جدید بسازید", + "add_tracks": "افزودن آهنگ‌ها", + "no_tracks": "هیچ آهنگی در این فهرست پخش نیست", + "unknown_artist": "هنرمند ناشناس", + "unknown_title": "ناشناس", + "confirm_delete": "این فهرست پخش حذف شود؟", + "playlist_name": "نام فهرست پخش", + "create": "ایجاد", + "delete": "حذف", + "share": "هم‌رسانی", + "edit": "ویرایش", + "play_all": "پخش همه", + "shuffle": "تصادفی", + "repeat": "تکرار", + "repeat_one": "تکرار یک", + "queue": "صف", + "queue_empty": "صف خالی است", + "not_playing": "در حال پخش نیست", + "play": "پخش", + "pause": "توقف", + "previous": "قبلی", + "next": "بعدی", + "volume": "صدا", + "mute": "بی‌صدا", + "unmute": "صدا فعال", + "title": "عنوان", + "artist": "هنرمند", + "album": "آلبوم", + "tracks": "آهنگ", + "add": "افزودن", + "added": "افزوده شد!", + "added_to_playlist": "به فهرست پخش افزوده شد", + "add_to_playlist": "افزودن به فهرست پخش", + "load_error": "خطا در بارگیری فهرست پخش", + "add_error": "امکان افزودن آهنگ‌ها به فهرست پخش نیست", + "no_playlists_yet": "فهرست پخشی وجود ندارد. اول یکی بسازید!", + "selected_files": "انتخاب شده:", + "error": "خطا", + "search_audio": "جستجوی فایل‌های صوتی…", + "no_audio_files": "فایل صوتی یافت نشد", + "selected": "انتخاب شده", + "loading": "در حال بارگذاری…", + "search_error": "بارگذاری فایل‌های صوتی ممکن نشد", + "adding": "در حال افزودن…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "جست‌و‌جوی پرونده‌ها..", + "new_folder": "پوشهٔ جدید", + "upload": "بارگذاری", + "upload_files": "بارگذاری پرونده‌ها", + "upload_folder": "بارگذاری پوشه", + "upload.uploading": "...در حال بارگذاری", + "upload.complete": "{count} / {total} بارگذاری شد", + "upload.files": "فایل‌ها", + "rename": "تغییر نام", + "move": "انتقال به...", + "move_to": "انتقال به", + "delete": "حذف", + "download": "بارگیری", + "view": "مشاهده", + "cancel": "لغو", + "confirm": "تأیید", + "share": "هم‌رسانی", + "favorite": "افزودن به موردعلاقه‌ها", + "unfavorite": "حذف از موردعلاقه‌ها", + "copy": "رونوشت", + "notify": "آگاه‌سازی", + "send": "ارسال", + "clear_recent": "پاک‌کردن موارد اخیر", + "logout": "خروج", + "create": "ایجاد", + "search_btn": "جست‌و‌جو", + "close": "بستن", + "delete_permanently": "Delete permanently", + "empty_trash": "Empty trash", + "open_parent_folder": "رفتن به پوشه والد", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "ظاهر", + "about": "درباره OxiCloud", + "about_description": "پلتفرم ذخیره‌سازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی.", + "admin_panel": "پنل مدیریت", + "profile": "نمایه من", + "role_user": "کاربر", + "theme": { + "light": "روشن", + "dark": "تاریک", + "auto": "مانند سیستم" + }, + "manage_groups": "مدیریت گروه‌ها" + }, + "share": { + "dialogTitle": "پیوند هم‌رسانی", + "linkLabel": "پیوند هم‌سانی:", + "copyLink": "رونوشت", + "permissions": "دسترسی‌ها:", + "permissionRead": "خواندن", + "permissionWrite": "نوشتن", + "permissionReshare": "هم‌رسانی دوباره", + "password": "محافظت با گذرواژه:", + "generatePassword": "تولید", + "expiration": "تاریخ انقضا:", + "update": "به‌روزرسانی هم‌رسانی", + "remove": "پاک‌کردن هم‌رسانی", + "notifyTitle": "ارسال آگاه‌سازی", + "notifyEmailLabel": "نشانی رایانامه:", + "notifyMessageLabel": "پیام (اختیاری):", + "notifySend": "ارسال آگاه‌سازی", + "shareWithOthers": "هم‌رسانی با دیگران", + "sharePublicly": "هم‌رسانی عمومی", + "shareSettings": "تنظیمات هم‌رسانی", + "shareCopied": "پیوند به بُریده‌دان رونوشت شد", + "shareCreated": "پیوند هم‌رسانی با موفقیت ایجاد شد", + "shareUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", + "shareRemoved": "هم‌رسانی با موفقیت پاک شد", + "inviteByEmail": "دعوت از طریق ایمیل — دعوت ارسال خواهد شد", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "پیوند هم‌رسانی", + "share_linkLabel": "پیوند هم‌رسانی:", + "share_copyLink": "رونوشت", + "share_permissions": "دسترسی‌ها:", + "share_permissionRead": "خواندن", + "share_permissionWrite": "نوشتن", + "share_permissionReshare": "هم‌رسانی دوباره", + "share_password": "محافظت با گذرواژه:", + "share_generatePassword": "تولید", + "share_expiration": "تاریخ انقضا:", + "share_update": "به‌روزرسانی هم‌رسانی", + "share_remove": "پاک‌کردن هم‌رسانی", + "share_notifyTitle": "ارسال آگاه‌سازی", + "share_notifyEmailLabel": "نشانی رایانامه:", + "share_notifyMessageLabel": "پیام (اختیاری):", + "share_notifySend": "ارسال آگاه‌سازی", + "shared": { + "backToFiles": "بازگشت به پرونده‌ها", + "pageTitle": "منابع هم‌رسانی شده", + "pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", + "filterType": "نوع:", + "filterAll": "همه", + "filterFiles": "پرونده‌ها", + "filterFolders": "پوشه‌ها", + "sortBy": "مرتب‌سازی بر اساس:", + "sortByName": "نام", + "sortByDate": "تاریخ هم‌رسانی", + "sortByExpiration": "تاریخ انقضا", + "search": "جست‌و‌جو", + "colName": "نام", + "colType": "نوع", + "colDateShared": "تاریخ هم‌رسانی", + "colExpiration": "تاریخ انقضا", + "colPermissions": "دسترسی‌ها", + "colPassword": "گذرواژه", + "colActions": "عملیات", + "emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", + "emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", + "goToFiles": "رفتن به پرونده‌ها", + "typeFile": "پرونده", + "typeFolder": "پوشه", + "noExpiration": "بدون انقضا", + "hasPassword": "بله", + "noPassword": "خیر", + "editShare": "ویرایش هم‌رسانی", + "notifyShare": "آگاه‌سازی کسی", + "copyLink": "رونوشت پیوند", + "removeShare": "حذف هم‌رسانی", + "linkCopied": "پیوند به بُریده‌دان رونوشت شد", + "linkCopyFailed": "رونوشت پیوند ناموفق بود", + "itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", + "itemRemoved": "هم‌رسانی با موفقیت پاک شد", + "invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", + "notificationSent": "آگاه‌سازی با موفقیت ارسال شد", + "notificationFailed": "ارسال آگاه‌سازی ناموفق بود", + "shared_backToFiles": "بازگشت به پرونده‌ها", + "shared_pageTitle": "منابع هم‌رسانی شده", + "shared_pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", + "shared_filterType": "نوع:", + "shared_filterAll": "همه", + "shared_filterFiles": "پرونده‌ها", + "shared_filterFolders": "پوشه‌ها", + "shared_sortBy": "مرتب‌سازی بر اساس:", + "shared_sortByName": "نام", + "shared_sortByDate": "تاریخ هم‌رسانی", + "shared_sortByExpiration": "تاریخ انقضا", + "shared_search": "جست‌و‌جو", + "shared_colName": "نام", + "shared_colType": "نوع", + "shared_colDateShared": "تاریخ هم‌رسانی", + "shared_colExpiration": "تاریخ انقضا", + "shared_colPermissions": "دسترسی‌ها", + "shared_colPassword": "گذرواژه", + "shared_colActions": "عملیات", + "shared_emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", + "shared_emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", + "shared_goToFiles": "رفتن به پرونده‌ها", + "shared_typeFile": "پرونده", + "shared_typeFolder": "پوشه", + "shared_noExpiration": "بدون انقضا", + "shared_hasPassword": "بله", + "shared_noPassword": "خیر", + "shared_editShare": "ویرایش هم‌رسانی", + "shared_notifyShare": "آگاه‌سازی کسی", + "shared_copyLink": "رونوشت پیوند", + "shared_removeShare": "حذف هم‌رسانی", + "shared_linkCopied": "پیوند به بُریده‌دان رونوشت شد", + "shared_linkCopyFailed": "رونوشت پیوند ناموفق بود", + "shared_itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", + "shared_itemRemoved": "هم‌رسانی با موفقیت پاک شد", + "shared_invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", + "shared_notificationSent": "آگاه‌سازی با موفقیت ارسال شد", + "shared_notificationFailed": "ارسال آگاه‌سازی ناموفق بود" + }, + "files": { + "name": "نام", + "type": "نوع", + "size": "اندازه", + "modified": "تاریخ تغییر", + "no_files": "هنوز هیچ پرونده‌ای در این پوشه وجود ندارد", + "empty_hint": "برای شروع، فایل‌ها را آپلود کنید یا پوشه بسازید", + "loading": "در حال بارگذاری فایل‌ها…", + "view_grid": "نمای شبکه‌ای", + "view_list": "نمای فهرستی", + "file_types": { + "document": "سند", + "image": "تصویر", + "video": "ویدیو", + "audio": "صوتی", + "pdf": "PDF", + "text": "متن", + "folder": "پوشه", + "spreadsheet": "صفحه گسترده", + "presentation": "ارائه", + "archive": "بایگانی", + "installer": "نصب‌کننده", + "code": "کد" + }, + "owner": "مالک" + }, + "dialogs": { + "rename_folder": "تغییر نام پوشه", + "new_name": "نام جدید", + "new_folder_title": "پوشه جدید", + "folder_name": "نام پوشه", + "folder_placeholder": "پوشه من", + "rename_title": "تغییر نام", + "move_file": "انتقال پرونده", + "select_destination": "انتخاب پوشهٔ مقصد", + "root": "ریشه", + "delete_confirmation": "آیا مطمئن هستید که می‌خواهید حذف کنید", + "and_contents": "و همهٔ محتویات آن", + "no_undo": "این عملیات قابل بازگردانی نیست", + "share_file": "هم‌رسانی پرونده", + "share_folder": "هم‌رسانی پوشه", + "existing_shares": "هم‌رسانی موجود", + "share_options": "گزینه‌های هم‌رسانی", + "password": "گذرواژه", + "expiration": "تاریخ انقضا", + "permissions": "دسترسی‌ها", + "generated_link": "پیوند تولید شده", + "notify": "ارسال آگاه‌سازی", + "recipient": "گیرنده", + "message": "پیام", + "confirm_delete": "Move to trash", + "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", + "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", + "confirm_delete_share": "Delete share link", + "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", + "confirm_empty_trash": "Empty trash", + "confirm_permanent_delete": "Delete permanently", + "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", + "confirm_title": "Confirm action", + "go_to_parent": ".. (parent folder)", + "move_folder": "Move folder", + "no_subfolders": "No subfolders", + "rename_file": "Rename file", + "select_this_folder": "Select this folder", + "move_to_home": "انتقال به پوشه خانگی" + }, + "dropzone": { + "drag_files": "پرونده‌ها را اینجا بکشید یا برای انتخاب کلیک کنید", + "drop_files": "پرونده‌ها را رها کنید تا بارگذاری شوند" + }, + "permissions": { + "read": "خواندن", + "write": "نوشتن", + "reshare": "هم‌رسانی دوباره" + }, + "errors": { + "file_not_found": "پرونده پیدا نشد", + "folder_not_found": "پوشه پیدا نشد", + "delete_error": "خطا در پاک کردن", + "upload_error": "خطا در بارگذاری پرونده", + "rename_error": "خطا در تغییر نام", + "move_error": "خطا در انتقال", + "empty_name": "نام نمی‌تواند خالی باشد", + "name_exists": "پرونده یا پوشه‌ای با این نام قبلا وجود دارد", + "generic_error": "خطایی رخ داده است", + "group_name_invalid": "نام گروه باید با قالب پیشوند ایمیل مطابقت داشته باشد (حروف، ارقام، نقطه، خط تیره، زیرخط؛ 1–64 نویسه).", + "group_cycle": "این عضو باعث ایجاد ارجاع چرخه‌ای بین گروه‌ها می‌شود.", + "group_depth_exceeded": "عمق تودرتو بیش از حداکثر مجاز (8) است.", + "group_virtual_immutable": "گروه «Internal» توسط سامانه مدیریت می‌شود و قابل تغییر نیست.", + "group_not_found": "گروه پیدا نشد.", + "group_name_taken": "گروهی با این نام پیش‌از این وجود دارد." + }, + "breadcrumb": { + "home": "صفحه اصلی" + }, + "trash": { + "empty_trash": "خالی کردن سطل زباله", + "empty_state": "سطل زباله خالی است", + "original_location": "محل اصلی", + "deleted_date": "تاریخ حذف", + "remaining": "باقی‌مانده", + "actions": "عملیات", + "restore": "بازیابی", + "delete_permanently": "حذف دائمی", + "empty_confirm": "آیا مطمئن هستید که می‌خواهید سطل زباله را خالی کنید؟ این کار همهٔ موارد را به‌طور دائمی حذف خواهد کرد.", + "groupby": { + "remaining_days": "روزهای باقی‌مانده", + "trashed_time": "زمان حذف" + } + }, + "daysRemaining": { + "expired": "منقضی شده", + "today": "امروز", + "tomorrow": "فردا", + "inDays": "{{count}} روز" + }, + "expiryChip": { + "never": "هرگز منقضی نمی‌شود", + "expired": "منقضی شده", + "today": "امروز منقضی می‌شود", + "tomorrow": "فردا منقضی می‌شود", + "inDays": "در {{count}} روز منقضی می‌شود", + "onDate": "در {{date}} منقضی می‌شود" + }, + "auth": { + "login_title": "ورود", + "username": "نام‌کاربری", + "username_placeholder": "نام‌کاربری خود را وارد کنید", + "login_identifier": "نام کاربری یا ایمیل", + "login_identifier_placeholder": "نام کاربری یا ایمیل خود را وارد کنید", + "password": "گذرواژه", + "password_placeholder": "گذرواژه خود را وارد کنید", + "login_button": "ورود", + "no_account": "حساب کاربری ندارید؟", + "register": "نام‌نویسی", + "admin_setup": "اولین بار است؟", + "setup": "راه‌اندازی اولیه مدیریت", + "register_title": "ایجاد حساب کاربری", + "email": "رایانامه", + "email_placeholder": "رایانامه خود را وارد کنید", + "confirm_password": "تأیید گذرواژه", + "confirm_password_placeholder": "گذرواژه خود را تأیید کنید", + "register_button": "ایجاد حساب کاربری", + "have_account": "حساب کاربری دارید؟", + "login": "ورود", + "setup_title": "راه‌اندازی اولیه", + "setup_step1": "مدیر", + "setup_step2": "سیستم", + "setup_step3": "تکمیل", + "admin_username": "نام‌کاربری مدیر", + "admin_email": "رایانامه مدیر", + "admin_password": "گذرواژه مدیر", + "create_admin": "ایجاد مدیر", + "back_to_login": "قبلا راه‌اندازی شده است؟", + "admin_success": "حساب کاربری مدیر با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", + "account_success": "حساب کاربری با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", + "passwords_mismatch": "گذرواژه‌ها مطابقت ندارند", + "admin_create_error": "خطا در ایجاد حساب کاربری مدیر", + "or": "یا", + "sso_login": "ورود با SSO", + "sso_login_provider": "ورود با {{provider}}", + "magicLinkHint": "رمز عبور ندارید؟ ایمیل خود را وارد کنید تا یک پیوند ورود یک‌بار‌مصرف برایتان ارسال شود.", + "magicLinkEmailLabel": "آدرس ایمیل", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "ارسال پیوند ورود", + "magicLinkSent": "اگر برای این ایمیل حسابی وجود داشته باشد، پیوند ورود ارسال شده است. صندوق ورودی خود را بررسی کنید.", + "magicLinkUnavailable": "ورود با ایمیل در این سرور در دسترس نیست.", + "magicLinkNetworkError": "ارتباط با سرور برقرار نشد: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "فضای ذخیره‌سازی", + "calculating": "در حال محاسبه...", + "used": "{{percentage}}% استفاده شده ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "این نوع پرونده قابل پیش‌نمایش نیست.", + "download_file": "بارگیری پرونده", + "zoom_in": "بزرگ‌نمایی", + "zoom_out": "کوچک‌نمایی", + "zoom_reset": "بازنشانی بزرگ‌نمایی" + }, + "language_selector": { + "title": "!خوش آمدید", + "subtitle": "زبان خود را برای ادامه انتخاب کنید", + "continue": "ادامه", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "هنوز هیچ مورد علاقه‌ای وجود ندارد", + "empty_hint": "برای افزودن به موارد علاقه‌مند، پرونده‌ها یا پوشه‌ها را ستاره‌دار کنید", + "add": "افزودن به موارد علاقه‌مند", + "remove": "حذف از موارد علاقه‌مند", + "added_title": "به موارد علاقه‌مند افزوده شد", + "added_msg": "به موارد علاقه‌مند افزوده شد", + "removed_title": "از موارد علاقه‌مند حذف شد", + "removed_msg": "از موارد علاقه‌مند حذف شد" + }, + "recent": { + "title": "اخیر", + "clear": "پاک کردن اخیر", + "accessed": "دسترسی یافته", + "empty_state": "هنوز هیچ پروندهٔ اخیر وجود ندارد", + "empty_hint": "پرونده‌هایی که باز می‌کنید اینجا ظاهر می‌شوند", + "loadMore": "بارگذاری بیشتر" + }, + "batch": { + "one_selected": "۱ مورد انتخاب شده", + "n_selected": "{{count}} مورد انتخاب شده", + "confirm_delete": "آیا مطمئنید که می‌خواهید {{count}} مورد را به سطل زباله منتقل کنید؟", + "move_title": "انتقال {{count}} مورد", + "add_favorites": "افزودن به موارد علاقه‌مند", + "move_copy": "انتقال یا کپی" + }, + "admin": { + "page_title": "پنل مدیریت", + "back_to_app": "بازگشت به OxiCloud", + "loading": "در حال بارگذاری…", + "access_denied": "دسترسی ممنوع", + "access_denied_desc": "امتیازات مدیر لازم است.", + "sign_in": "ورود", + "tab_dashboard": "داشبورد", + "tab_users": "کاربران", + "tab_oidc": "SSO / OIDC", + "total_users": "کل کاربران", + "active_users": "کاربران فعال", + "admins": "مدیران", + "version": "نسخه", + "storage_overview": "نمای کلی فضا", + "used": "استفاده شده", + "total_quota": "سهمیه کل", + "usage_pct": "درصد استفاده", + "users_over_80": "کاربران بالای ۸۰٪", + "users_over_quota": "کاربران بالای سهمیه", + "system": "سیستم", + "auth_label": "احراز هویت", + "oidc_label": "OIDC", + "quotas_label": "سهمیه‌ها", + "enabled": "فعال", + "disabled": "غیرفعال", + "active": "فعال", + "off": "خاموش", + "allow_registration": "اجازه ثبت‌نام عمومی", + "registration_warning": "ثبت‌نام عمومی غیرفعال است. فقط مدیران می‌توانند کاربر جدید بسازند.", + "user_management": "مدیریت کاربران", + "create_user": "ایجاد کاربر", + "col_user": "کاربر", + "col_role": "نقش", + "col_auth": "احراز هویت", + "col_status": "وضعیت", + "col_storage": "فضا", + "col_last_login": "آخرین ورود", + "col_actions": "عملیات", + "loading_users": "در حال بارگذاری…", + "failed_load_users": "خطا در بارگذاری", + "no_users_found": "کاربری یافت نشد", + "showing_users": "نمایش {{from}}-{{to}} از {{total}}", + "prev": "قبلی", + "next": "بعدی", + "inactive": "غیرفعال", + "you_badge": "(شما)", + "local": "محلی", + "never": "هرگز", + "just_now": "همین الان", + "minutes_ago": "{{n}} دقیقه پیش", + "hours_ago": "{{n}} ساعت پیش", + "days_ago": "{{n}} روز پیش", + "edit_quota_title": "ویرایش سهمیه", + "reset_password_title": "بازنشانی رمز", + "toggle_role_title": "تغییر نقش", + "deactivate_title": "غیرفعال کردن", + "activate_title": "فعال کردن", + "delete_title": "حذف", + "sso_title": "ورود یکپارچه (OIDC / SSO)", + "enable_sso": "فعال‌سازی SSO", + "provider_name": "نام ارائه‌دهنده", + "issuer_url": "آدرس صادرکننده", + "issuer_url_hint": "آدرس صادرکننده OpenID Connect", + "auto_discover": "کشف خودکار", + "discovering": "در حال کشف…", + "client_id": "شناسه مشتری", + "client_secret": "رمز مشتری", + "client_secret_placeholder": "خالی بگذارید تا مقدار فعلی حفظ شود", + "secret_configured": "رمز مشتری قبلاً پیکربندی شده", + "callback_url": "آدرس بازگشت", + "callback_url_hint": "(در IdP ثبت کنید)", + "advanced_settings": "تنظیمات پیشرفته", + "scopes": "محدوده‌ها", + "auto_provision": "تامین خودکار کاربران", + "admin_groups": "گروه‌های مدیر", + "admin_groups_hint": "نام گروه‌های OIDC جدا شده با کاما", + "disable_password": "غیرفعال‌سازی ورود با رمز (فقط OIDC)", + "password_warning": "تمام ورودهای رمزی متوقف می‌شود!", + "test_btn": "آزمایش", + "save_btn": "ذخیره", + "saving": "در حال ذخیره…", + "settings_saved": "تنظیمات ذخیره شد — OIDC اکنون {{status}}", + "quota_modal_title": "به‌روزرسانی سهمیه", + "quota_user_label": "کاربر:", + "new_quota": "سهمیه جدید", + "quota_unlimited_hint": "۰ برای نامحدود", + "cancel": "انصراف", + "create_user_title": "ایجاد کاربر جدید", + "username_label": "نام کاربری", + "username_placeholder": "نام‌کاربری", + "username_hint": "۳ تا ۳۲ کاراکتر", + "password_label": "رمز عبور", + "password_placeholder": "حداقل ۸ کاراکتر", + "email_label": "ایمیل", + "email_optional": "(اختیاری)", + "email_placeholder": "user@example.com (خودکار اگر خالی)", + "role_label": "نقش", + "role_user": "کاربر", + "role_admin": "مدیر", + "quota_label": "سهمیه", + "creating": "در حال ایجاد…", + "reset_pw_title": "بازنشانی رمز عبور", + "new_password_label": "رمز عبور جدید", + "resetting": "در حال بازنشانی…", + "reset_btn": "بازنشانی", + "confirm_role_change": "نقش به {{role}} تغییر یابد؟", + "confirm_deactivate": "آیا از غیرفعال‌سازی این کاربر مطمئنید؟", + "confirm_activate": "آیا از فعال‌سازی این کاربر مطمئنید؟", + "confirm_delete_user": "کاربر «{{name}}» حذف شود؟ قابل بازگشت نیست!", + "confirm_action": "تأیید عملیات", + "confirm_yes": "تأیید", + "confirm_no": "انصراف", + "error_username_short": "نام کاربری حداقل ۳ کاراکتر", + "error_password_short": "رمز عبور حداقل ۸ کاراکتر", + "error_generic": "خطا", + "error_network": "خطای شبکه: {{message}}", + "error_create_user": "خطا در ایجاد کاربر", + "tab_storage": "فضای ذخیره‌سازی", + "storage_title": "تنظیمات فضای ذخیره‌سازی", + "storage_current_backend": "بک‌اند فعلی", + "storage_total_blobs": "مجموع بلوب‌ها", + "storage_total_size": "حجم کل", + "storage_dedup_ratio": "نسبت حذف تکراری", + "storage_backend": "بک‌اند", + "storage_local": "محلی", + "storage_s3": "سازگار با S3", + "storage_provider_preset": "پیش‌تنظیم ارائه‌دهنده", + "storage_preset_custom": "سفارشی", + "storage_endpoint_url": "آدرس نقطه پایانی", + "storage_endpoint_hint": "برای AWS S3 خالی بگذارید", + "storage_bucket": "باکت", + "storage_region": "منطقه", + "storage_access_key": "کلید دسترسی", + "storage_secret_key": "کلید مخفی", + "storage_secret_configured": "کلید تنظیم شد", + "storage_key_placeholder": "کلید جدید وارد کنید", + "storage_path_style": "اجبار سبک مسیر", + "storage_path_style_hint": "برای MinIO و برخی سرویس‌های سازگار با S3 لازم است", + "storage_test_connection": "آزمایش اتصال", + "storage_test_success": "اتصال موفق", + "storage_test_failure": "اتصال ناموفق", + "storage_save": "ذخیره تنظیمات", + "storage_saved": "تنظیمات ذخیره شد", + "storage_migration": "انتقال داده", + "storage_migration_coming_soon": "ابزارهای انتقال به زودی", + "migration_status_label": "وضعیت انتقال", + "migration_start": "شروع انتقال", + "migration_pause": "توقف", + "migration_resume": "ادامه", + "migration_verify": "تأیید", + "migration_complete": "تکمیل", + "migration_started": "انتقال شروع شد", + "migration_paused_msg": "انتقال متوقف شد", + "migration_resumed_msg": "انتقال ادامه یافت", + "migration_completed_msg": "انتقال با موفقیت تکمیل شد", + "migration_verifying": "در حال تأیید...", + "migration_verify_passed": "تأیید موفق", + "migration_verify_failed": "تأیید ناموفق", + "migration_failed_blobs": "بلوب‌های ناموفق", + "testing": "در حال آزمایش...", + "smtp_disabled": "غیرفعال (میزبان تنظیم نشده)", + "smtp_enabled": "فعال", + "smtp_enabled_label": "وضعیت", + "smtp_intro": "SMTP فقط از طریق متغیرهای محیطی (OXICLOUD_SMTP_*) پیکربندی می‌شود. مقادیر زیر از سرور در حال اجرا خوانده می‌شوند — برای تغییر آن‌ها، محیط را ویرایش کرده و OxiCloud را راه‌اندازی مجدد کنید.", + "smtp_not_configured": "SMTP روی این سرور پیکربندی نشده است.", + "smtp_send_failed": "ارسال ناموفق.", + "smtp_send_test": "ارسال ایمیل آزمایشی", + "smtp_sending": "در حال ارسال…", + "smtp_sent": "ایمیل آزمایشی ارسال شد.", + "smtp_server_code": "پاسخ سرور", + "smtp_test_intro": "یک پیام تشخیصی از پیش تعریف‌شده را به گیرنده زیر ارسال می‌کند و پاسخ سرور SMTP را گزارش می‌دهد تا بتوانید آن را با گزارش‌های ریلی خود مطابقت دهید.", + "smtp_test_missing_to": "آدرس گیرنده را وارد کنید.", + "smtp_test_title": "ارسال ایمیل آزمایشی", + "smtp_test_to": "آدرس گیرنده", + "smtp_title": "ایمیل خروجی (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "پروفایل", + "back_to_app": "بازگشت به OxiCloud", + "loading": "در حال بارگذاری…", + "not_authenticated": "احراز هویت نشده", + "not_authenticated_desc": "برای مشاهده پروفایل وارد شوید.", + "sign_in": "ورود", + "role_admin": "مدیر", + "role_user": "کاربر", + "account_details": "جزئیات حساب", + "username": "نام کاربری", + "email": "ایمیل", + "role": "نقش", + "last_login": "آخرین ورود", + "storage": "فضای ذخیره‌سازی", + "used": "استفاده شده", + "quota": "سهمیه", + "usage": "مصرف", + "unlimited": "نامحدود", + "app_passwords": "رمزهای برنامه", + "app_pw_desc": "رمزهایی برای کلاینت‌های WebDAV، CalDAV و CardDAV ایجاد کنید. هر رمز فقط یک بار نمایش داده می‌شود.", + "app_pw_label_placeholder": "برچسب (مثلاً Thunderbird، macOS)", + "generate": "ایجاد", + "generating": "در حال ایجاد…", + "new_password_for": "رمز جدید برای", + "copy_warning": "این رمز را اکنون کپی کنید. دوباره قابل مشاهده نیست.", + "copy_to_clipboard": "کپی به کلیپ‌بورد", + "col_label": "برچسب", + "col_created": "ایجاد شده", + "col_last_used": "آخرین استفاده", + "col_status": "وضعیت", + "active": "فعال", + "revoked": "ابطال شده", + "revoke_title": "ابطال", + "no_app_passwords": "هنوز رمز برنامه‌ای وجود ندارد.", + "client_sessions": "نشست‌های کلاینت", + "client_sessions_desc": "هنگام اتصال کلاینت سازگار با Nextcloud به صورت خودکار ایجاد می‌شود.", + "col_client": "کلاینت", + "never": "هرگز", + "just_now": "همین الان", + "minutes_ago": "{{n}} دقیقه پیش", + "hours_ago": "{{n}} ساعت پیش", + "days_ago": "{{n}} روز پیش", + "edit_profile": "ویرایش نمایه", + "edit_oidc_managed": "برای تغییر اطلاعات خود (نام، نام خانوادگی، عکس نمایه، …)، لطفاً آن‌ها را در ارائه‌دهنده هویت خود به‌روز کنید. تغییرات شما در ورود بعدی ظاهر خواهد شد.", + "username_claim_hint": "۲ تا ۶۴ کاراکتر، حروف / ارقام / نقطه / خط تیره / زیرخط. پس از انتخاب، نام کاربری قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", + "username_already_claimed": "نام کاربری تنظیم شده و قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", + "given_name": "نام", + "family_name": "نام خانوادگی", + "notify_on_share": "وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن", + "notify_on_share_hint": "وقتی تیک‌خورده نباشد، اشتراک‌گذاری‌ها همچنان در حساب شما نمایش داده می‌شوند — فقط ایمیلی درباره آنها دریافت نخواهید کرد.", + "save_profile": "ذخیره تغییرات", + "profile_saved": "نمایه به‌روز شد", + "profile_no_changes": "تغییری برای ذخیره وجود ندارد.", + "profile_save_failed": "ذخیره ناموفق بود", + "username_taken_error": "این نام کاربری قبلاً گرفته شده است.", + "username_immutable_error": "نام کاربری شما قبلاً تنظیم شده و در اینجا قابل تغییر نیست. در صورت نیاز به تغییر نام، با مدیر تماس بگیرید.", + "change_password": "تغییر رمز عبور", + "current_password": "رمز فعلی", + "new_password": "رمز جدید", + "min_8_chars": "حداقل ۸ کاراکتر", + "confirm_password": "تأیید رمز جدید", + "update_password": "به‌روزرسانی رمز", + "updating": "در حال به‌روزرسانی…", + "password_updated": "رمز عبور با موفقیت به‌روز شد", + "passwords_no_match": "رمزها مطابقت ندارند", + "password_too_short": "رمز باید حداقل ۸ کاراکتر باشد", + "password_change_failed": "تغییر رمز ناموفق بود", + "error_network": "خطای شبکه: {{message}}", + "error_label_required": "لطفاً برچسب وارد کنید", + "error_create_pw": "ایجاد رمز ناموفق بود", + "confirm_revoke": "رمز «{{label}}» ابطال شود؟ کلاینت‌ها از کار می‌افتند.", + "error_revoke": "ابطال ناموفق بود", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "notifications": { + "file_renamed": "فایل تغییر نام داد", + "file_renamed_to": "فایل به \"{{name}}\" تغییر نام داد", + "folder_renamed": "پوشه تغییر نام داد", + "folder_renamed_to": "پوشه به \"{{name}}\" تغییر نام داد", + "file_uploaded": "فایل آپلود شد", + "file_deleted": "فایل به زباله‌دان منتقل شد", + "folder_deleted": "پوشه به زباله‌دان منتقل شد", + "item_deleted_permanently": "آیتم برای همیشه حذف شد", + "trash_emptied": "زباله‌دان خالی شد", + "title": "اعلان‌ها", + "empty": "بدون اعلان", + "link_created": "پیوند ایجاد شد", + "share_success": "پیوند اشتراک‌گذاری با موفقیت ایجاد شد", + "upload_files_section_title": "بارگذاری اینجا در دسترس نیست", + "upload_files_section_body": "برای بارگذاری فایل‌ها به بخش فایل‌ها بروید" + }, + "upload": { + "uploading": "در حال آپلود...", + "files": "فایل‌ها", + "complete": "{{count}} / {{total}} آپلود شد" + }, + "storage_quota_exceeded": "سهمیه فضای ذخیره‌سازی تجاوز کرده است", + "sharedwithme": { + "pageTitle": "به اشتراک‌گذاشته شده با من", + "pageDescription": "فایل‌ها و پوشه‌هایی که کاربران دیگر با شما به اشتراک گذاشته‌اند", + "emptyStateTitle": "هنوز چیزی با شما به اشتراک گذاشته نشده", + "emptyStateDesc": "مواردی که کاربران دیگر با شما به اشتراک می‌گذارند اینجا نمایش داده می‌شوند", + "loadMore": "بارگذاری بیشتر", + "sharedBy": "به اشتراک‌گذاشته توسط", + "colName": "نام", + "colType": "نوع", + "colSharedBy": "به اشتراک‌گذاشته توسط", + "colDate": "تاریخ اشتراک‌گذاری", + "colPermissions": "مجوزها" + }, + "groupby": { + "none": "هیچ", + "title": "گروه‌بندی بر اساس", + "owner": "مالک", + "shareDate": "تاریخ اشتراک", + "type": "نوع", + "type.folders": "پوشه‌ها", + "accessedAt": "تاریخ دسترسی", + "modifiedAt": "تاریخ تغییر", + "createdAt": "تاریخ ایجاد", + "size": "اندازه", + "favoriteDate": "تاریخ مورد علاقه", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "جدید" + }, + "dateBucket": { + "today": "امروز", + "last7days": "۷ روز گذشته", + "last30days": "۳۰ روز گذشته" + }, + "groups": { + "title": "مدیریت گروه‌ها", + "create_button": "ایجاد گروه", + "create_dialog_title": "گروه جدید", + "edit_dialog_title": "تغییر نام گروه", + "name_label": "نام", + "name_placeholder": "engineering", + "description_label": "توضیحات (اختیاری)", + "members_section": "اعضا", + "add_member_placeholder": "افزودن کاربر یا گروه…", + "no_members": "هنوز عضوی وجود ندارد.", + "remove_member": "حذف", + "delete_group": "حذف گروه", + "delete_confirm": "گروه «{name}» حذف شود؟ مجوزهای مرتبط با این گروه باطل خواهند شد.", + "empty_state": "هنوز گروهی وجود ندارد.", + "load_more": "بارگیری بیشتر", + "back_to_list": "بازگشت", + "loading": "در حال بارگذاری…", + "virtual_badge": "سامانه", + "member_count_zero": "بدون عضو", + "member_count_one": "۱ عضو", + "member_count_other": "{count} عضو", + "delete_confirm_label": "نام گروه را برای تأیید وارد کنید:", + "delete_confirm_mismatch": "نام گروه را دقیقاً برای تأیید وارد کنید.", + "virtual_internal_name": "داخلی", + "members_loading": "در حال بارگیری اعضا…", + "members_empty": "بدون عضو", + "virtual_internal_explanation": "هر کاربر داخلی روی این سرور" + }, + "myshares": { + "copyLink": "کپی پیوند", + "deleteLink": "حذف پیوند", + "notifyByEmail": "اطلاع‌رسانی از طریق ایمیل", + "notifyFailed": "ارسال اعلان ممکن نشد.", + "notifyGroupMembers": "اطلاع‌رسانی به اعضای گروه", + "notifyRateLimited": "اعلان‌های زیادی برای این گیرنده — بعداً دوباره تلاش کنید.", + "removeAccess": "حذف دسترسی", + "resendInvitation": "ارسال مجدد ایمیل دعوت" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/fr.json b/frontend/static/locales/fr.json new file mode 100644 index 00000000..10e04013 --- /dev/null +++ b/frontend/static/locales/fr.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Ce lien de connexion n'est plus valide", + "expired_body": "Le lien a peut-être expiré ou a déjà été utilisé. Nous pouvons vous en envoyer un nouveau — il arrivera dans votre boîte de réception dans quelques secondes.", + "resend_to": "Envoyer un nouveau lien à {{email}}", + "generic_unavailable": "Ce lien de connexion n'est plus valide. Il a peut-être déjà été utilisé ou a expiré. Demandez un nouveau lien depuis la page de connexion.", + "service_unavailable": "La connexion par lien magique n'est pas activée sur ce serveur.", + "internal_error": "Une erreur s'est produite lors de votre connexion. Veuillez réessayer.", + "resend_failure": "Une erreur s'est produite lors de l'envoi du lien. Veuillez réessayer.", + "cross_browser_title": "Continuer la connexion sur cet appareil ?", + "cross_browser_body": "Vous avez ouvert ce lien de connexion dans un navigateur ou un appareil différent de celui où vous l'avez demandé.", + "cross_browser_warning": "Si vous avez demandé ce lien, vous pouvez continuer en toute sécurité. Sinon, fermez cette page — cliquer sur Continuer connecterait quelqu'un d'autre à votre compte.", + "cross_browser_continue": "Continuer et se connecter", + "resend_confirmation_title": "Vérifiez votre boîte de réception", + "resend_confirmation_body": "Si le lien de connexion correspondait à un compte actif, un nouveau lien vient d'être envoyé. Veuillez vérifier votre boîte de réception.", + "return_link": "Retour à OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", + "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez-le en cliquant sur le lien ci-dessous :\n{{link}}\n\nLe lien est à usage unique et expire dans {{ttl_hours}} heures.\nSi vous n'attendiez pas cette invitation, vous pouvez ignorer ce message.\n\n— OxiCloud" + }, + "login": { + "subject": "Connexion à OxiCloud", + "body": "Bonjour,\n\nUtilisez le lien ci-dessous pour vous connecter à OxiCloud. Le lien est à usage unique et expire dans {{ttl_minutes}} minutes. Ouvrez-le sur le même appareil que celui où vous l'avez demandé.\n\n{{link}}\n\nSi vous n'avez pas demandé ce lien de connexion, vous pouvez ignorer ce message — aucune action supplémentaire n'est nécessaire.\n\n— OxiCloud" + }, + "kind_file": "fichier", + "kind_folder": "dossier", + "english_fallback_divider": "--- Version anglaise ci-dessous ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", + "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez OxiCloud pour voir votre nouveau partage :\n{{login_link}}\n\nVous avez peut-être d'autres nouveaux partages de {{inviter}} — connectez-vous pour voir tous vos éléments partagés.\n\n— OxiCloud\n\nVous recevez ce message parce que vous avez un compte OxiCloud et que la préférence de notification de partage est activée. Vous pouvez la désactiver dans votre profil (M'avertir par e-mail quand quelqu'un partage avec moi)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Système de stockage cloud minimaliste" + }, + "nav": { + "files": "Fichiers", + "shared": "Partages", + "recent": "Récents", + "favorites": "Favoris", + "photos": "Photos", + "music": "Musique", + "trash": "Corbeille", + "sharedwithme": "Partages avec moi" + }, + "photos": { + "empty_state": "Pas encore de photos", + "empty_hint": "Téléchargez des images ou des vidéos pour les voir ici", + "items_selected": "sélectionnés", + "view_daily": "Jour", + "view_monthly": "Mois", + "view_yearly": "Année" + }, + "music": { + "create_playlist": "Créer une Playlist", + "playlists": "Playlists", + "no_playlists": "Aucune playlist", + "select_playlist": "Sélectionnez une playlist", + "select_hint": "Choisissez une playlist dans la barre latérale ou créez-en une nouvelle", + "add_tracks": "Ajouter des Pistes", + "no_tracks": "Aucune piste dans cette playlist", + "unknown_artist": "Artiste Inconnu", + "unknown_title": "Inconnu", + "confirm_delete": "Supprimer cette playlist ?", + "playlist_name": "Nom de la playlist", + "create": "Créer", + "delete": "Supprimer", + "share": "Partager", + "edit": "Modifier", + "play_all": "Tout Lire", + "shuffle": "Aléatoire", + "repeat": "Répéter", + "repeat_one": "Répéter Une", + "queue": "File d'attente", + "queue_empty": "File d'attente vide", + "not_playing": "Pas en lecture", + "play": "Lecture", + "pause": "Pause", + "previous": "Précédent", + "next": "Suivant", + "volume": "Volume", + "mute": "Muet", + "unmute": "Activer le son", + "title": "Titre", + "artist": "Artiste", + "album": "Album", + "tracks": "pistes", + "add": "Ajouter", + "added": "Ajouté !", + "added_to_playlist": "ajouté à la playlist", + "add_to_playlist": "Ajouter à la playlist", + "load_error": "Erreur de chargement des playlists", + "add_error": "Impossible d'ajouter les pistes", + "no_playlists_yet": "Pas encore de playlists. Créez-en une d'abord !", + "selected_files": "Sélectionnés :", + "error": "Erreur", + "search_audio": "Rechercher des fichiers audio…", + "no_audio_files": "Aucun fichier audio trouvé", + "selected": "sélectionnés", + "loading": "Chargement…", + "search_error": "Impossible de charger les fichiers audio", + "adding": "Ajout en cours…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "Rechercher des fichiers...", + "new_folder": "Nouveau dossier", + "upload": "Téléverser", + "upload_files": "Téléverser des fichiers", + "upload_folder": "Téléverser un dossier", + "upload.uploading": "Envoi en cours...", + "upload.complete": "{count} / {total} envoyés", + "upload.files": "fichiers", + "rename": "Renommer", + "move": "Déplacer vers...", + "move_to": "Déplacer vers", + "delete": "Supprimer", + "download": "Télécharger", + "view": "Afficher", + "cancel": "Annuler", + "confirm": "Confirmer", + "share": "Partager", + "favorite": "Ajouter aux favoris", + "unfavorite": "Retirer des favoris", + "copy": "Copier", + "notify": "Notifier", + "send": "Envoyer", + "clear_recent": "Effacer les récents", + "logout": "Se déconnecter", + "create": "Créer", + "search_btn": "Rechercher", + "close": "Fermer", + "delete_permanently": "Supprimer définitivement", + "empty_trash": "Vider la corbeille", + "open_parent_folder": "Aller au dossier parent", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Apparence", + "about": "À propos d'OxiCloud", + "about_description": "Plateforme de stockage cloud construite avec Rust et Architecture Propre. Rapide, sécurisée et privée.", + "admin_panel": "Panneau d'administration", + "profile": "Mon profil", + "role_user": "Utilisateur", + "theme": { + "light": "Clair", + "dark": "Sombre", + "auto": "Comme le système" + }, + "manage_groups": "Gérer les groupes" + }, + "share": { + "dialogTitle": "Lien de partage", + "linkLabel": "Lien partagé :", + "copyLink": "Copier", + "permissions": "Permissions :", + "permissionRead": "Lecture", + "permissionWrite": "Écriture", + "permissionReshare": "Repartager", + "password": "Protection par mot de passe :", + "generatePassword": "Générer", + "expiration": "Date d'expiration :", + "update": "Mettre à jour le partage", + "remove": "Supprimer le partage", + "notifyTitle": "Envoyer une notification", + "notifyEmailLabel": "Adresse e-mail :", + "notifyMessageLabel": "Message (facultatif) :", + "notifySend": "Envoyer la notification", + "shareWithOthers": "Partager avec d'autres", + "sharePublicly": "Partager publiquement", + "shareSettings": "Paramètres de partage", + "shareCopied": "Lien copié dans le presse-papiers", + "shareCreated": "Lien de partage créé avec succès", + "shareUpdated": "Paramètres de partage mis à jour", + "shareRemoved": "Partage supprimé avec succès", + "inviteByEmail": "Inviter par e-mail — une invitation sera envoyée", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Lien de partage", + "share_linkLabel": "Lien partagé :", + "share_copyLink": "Copier", + "share_permissions": "Permissions :", + "share_permissionRead": "Lecture", + "share_permissionWrite": "Écriture", + "share_permissionReshare": "Repartager", + "share_password": "Protection par mot de passe :", + "share_generatePassword": "Générer", + "share_expiration": "Date d'expiration :", + "share_update": "Mettre à jour le partage", + "share_remove": "Supprimer le partage", + "share_notifyTitle": "Envoyer une notification", + "share_notifyEmailLabel": "Adresse e-mail :", + "share_notifyMessageLabel": "Message (facultatif) :", + "share_notifySend": "Envoyer la notification", + "shared": { + "backToFiles": "Retour aux fichiers", + "pageTitle": "Ressources partagées", + "pageDescription": "Gérez vos fichiers et dossiers partagés", + "filterType": "Type :", + "filterAll": "Tous", + "filterFiles": "Fichiers", + "filterFolders": "Dossiers", + "sortBy": "Trier par :", + "sortByName": "Nom", + "sortByDate": "Date de partage", + "sortByExpiration": "Expiration", + "search": "Rechercher", + "colName": "Nom", + "colType": "Type", + "colDateShared": "Date de partage", + "colExpiration": "Expiration", + "colPermissions": "Permissions", + "colPassword": "Mot de passe", + "colActions": "Actions", + "emptyStateTitle": "Aucune ressource partagée", + "emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", + "goToFiles": "Aller aux fichiers", + "typeFile": "Fichier", + "typeFolder": "Dossier", + "noExpiration": "Sans expiration", + "hasPassword": "Oui", + "noPassword": "Non", + "editShare": "Modifier le partage", + "notifyShare": "Notifier quelqu'un", + "copyLink": "Copier le lien", + "removeShare": "Supprimer le partage", + "linkCopied": "Lien copié dans le presse-papiers !", + "linkCopyFailed": "Erreur lors de la copie du lien", + "itemUpdated": "Paramètres de partage mis à jour", + "itemRemoved": "Partage supprimé avec succès", + "invalidEmail": "Veuillez entrer une adresse e-mail valide", + "notificationSent": "Notification envoyée avec succès", + "notificationFailed": "Erreur lors de l'envoi de la notification", + "shared_backToFiles": "Retour aux fichiers", + "shared_pageTitle": "Ressources partagées", + "shared_pageDescription": "Gérez vos fichiers et dossiers partagés", + "shared_filterType": "Type :", + "shared_filterAll": "Tous", + "shared_filterFiles": "Fichiers", + "shared_filterFolders": "Dossiers", + "shared_sortBy": "Trier par :", + "shared_sortByName": "Nom", + "shared_sortByDate": "Date de partage", + "shared_sortByExpiration": "Expiration", + "shared_search": "Rechercher", + "shared_colName": "Nom", + "shared_colType": "Type", + "shared_colDateShared": "Date de partage", + "shared_colExpiration": "Expiration", + "shared_colPermissions": "Permissions", + "shared_colPassword": "Mot de passe", + "shared_colActions": "Actions", + "shared_emptyStateTitle": "Aucune ressource partagée", + "shared_emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", + "shared_goToFiles": "Aller aux fichiers", + "shared_typeFile": "Fichier", + "shared_typeFolder": "Dossier", + "shared_noExpiration": "Sans expiration", + "shared_hasPassword": "Oui", + "shared_noPassword": "Non", + "shared_editShare": "Modifier le partage", + "shared_notifyShare": "Notifier quelqu'un", + "shared_copyLink": "Copier le lien", + "shared_removeShare": "Supprimer le partage", + "shared_linkCopied": "Lien copié dans le presse-papiers !", + "shared_linkCopyFailed": "Erreur lors de la copie du lien", + "shared_itemUpdated": "Paramètres de partage mis à jour", + "shared_itemRemoved": "Partage supprimé avec succès", + "shared_invalidEmail": "Veuillez entrer une adresse e-mail valide", + "shared_notificationSent": "Notification envoyée avec succès", + "shared_notificationFailed": "Erreur lors de l'envoi de la notification" + }, + "files": { + "name": "Nom", + "type": "Type", + "size": "Taille", + "modified": "Modifié", + "no_files": "Aucun fichier dans ce dossier", + "empty_hint": "Téléversez des fichiers ou créez des dossiers pour commencer", + "loading": "Chargement des fichiers…", + "view_grid": "Vue en grille", + "view_list": "Vue en liste", + "file_types": { + "document": "Document", + "image": "Image", + "video": "Vidéo", + "audio": "Audio", + "pdf": "PDF", + "text": "Texte", + "folder": "Dossier", + "spreadsheet": "Tableur", + "presentation": "Présentation", + "archive": "Archive", + "installer": "Installateur", + "code": "Code" + }, + "owner": "Propriétaire" + }, + "dialogs": { + "rename_folder": "Renommer le dossier", + "rename_file": "Renommer le fichier", + "new_name": "Nouveau nom", + "new_folder_title": "Nouveau dossier", + "folder_name": "Nom du dossier", + "folder_placeholder": "Mon dossier", + "rename_title": "Renommer", + "move_file": "Déplacer le fichier", + "move_folder": "Déplacer le dossier", + "select_destination": "Sélectionnez le dossier de destination :", + "root": "Racine", + "delete_confirmation": "Êtes-vous sûr de vouloir supprimer", + "and_contents": "et tout son contenu", + "no_undo": "Cette action est irréversible", + "confirm_title": "Confirmer l'action", + "confirm_delete": "Déplacer vers la corbeille", + "confirm_delete_file": "Êtes-vous sûr de vouloir déplacer le fichier « {{name}} » vers la corbeille ?", + "confirm_delete_folder": "Êtes-vous sûr de vouloir déplacer le dossier « {{name}} » et tout son contenu vers la corbeille ?", + "confirm_permanent_delete": "Supprimer définitivement", + "confirm_permanent_delete_msg": "Êtes-vous sûr de vouloir supprimer définitivement cet élément ? Cette action est irréversible.", + "confirm_empty_trash": "Vider la corbeille", + "confirm_delete_share": "Supprimer le lien de partage", + "confirm_delete_share_msg": "Êtes-vous sûr de vouloir supprimer ce lien de partage ?", + "share_file": "Partager le fichier", + "share_folder": "Partager le dossier", + "existing_shares": "Partages existants", + "share_options": "Options de partage", + "password": "Mot de passe", + "expiration": "Expiration", + "permissions": "Permissions", + "generated_link": "Lien généré", + "notify": "Envoyer une notification", + "recipient": "Destinataire", + "message": "Message", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Déplacer vers le dossier personnel" + }, + "dropzone": { + "drag_files": "Glissez des fichiers ici ou cliquez pour sélectionner", + "drop_files": "Déposez les fichiers pour téléverser" + }, + "permissions": { + "read": "Lecture", + "write": "Écriture", + "reshare": "Repartager" + }, + "errors": { + "file_not_found": "Fichier introuvable", + "folder_not_found": "Dossier introuvable", + "delete_error": "Erreur lors de la suppression", + "upload_error": "Erreur lors du téléversement", + "rename_error": "Erreur lors du renommage", + "move_error": "Erreur lors du déplacement", + "empty_name": "Le nom ne peut pas être vide", + "name_exists": "Un fichier ou dossier portant ce nom existe déjà", + "generic_error": "Une erreur est survenue", + "group_name_invalid": "Le nom du groupe doit respecter le format préfixe d'email (lettres, chiffres, point, tiret, souligné ; 1–64 caractères).", + "group_cycle": "Ce membre créerait une référence circulaire entre groupes.", + "group_depth_exceeded": "Cette profondeur d'imbrication dépasse le maximum autorisé (8).", + "group_virtual_immutable": "Le groupe « Internal » est géré par le système et ne peut pas être modifié.", + "group_not_found": "Groupe introuvable.", + "group_name_taken": "Un groupe portant ce nom existe déjà." + }, + "breadcrumb": { + "home": "Accueil" + }, + "trash": { + "empty_trash": "Vider la corbeille", + "empty_state": "La corbeille est vide", + "original_location": "Emplacement d'origine", + "deleted_date": "Date de suppression", + "remaining": "Restant", + "actions": "Actions", + "restore": "Restaurer", + "delete_permanently": "Supprimer définitivement", + "empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés.", + "groupby": { + "remaining_days": "Jours restants", + "trashed_time": "Date de suppression" + } + }, + "daysRemaining": { + "expired": "Expiré", + "today": "Aujourd'hui", + "tomorrow": "Demain", + "inDays": "{{count}} jours" + }, + "expiryChip": { + "never": "N'expire jamais", + "expired": "Expiré", + "today": "Expire aujourd'hui", + "tomorrow": "Expire demain", + "inDays": "Expire dans {{count}} jours", + "onDate": "Expire le {{date}}" + }, + "auth": { + "login_title": "Se connecter", + "username": "Nom d'utilisateur", + "username_placeholder": "Entrez votre nom d'utilisateur", + "login_identifier": "Nom d'utilisateur ou e-mail", + "login_identifier_placeholder": "Saisissez votre nom d'utilisateur ou e-mail", + "password": "Mot de passe", + "password_placeholder": "Entrez votre mot de passe", + "login_button": "Se connecter", + "no_account": "Vous n'avez pas de compte ?", + "register": "S'inscrire", + "admin_setup": "Première fois ?", + "setup": "Configurer l'administrateur", + "register_title": "Créer un compte", + "email": "E-mail", + "email_placeholder": "Entrez votre e-mail", + "confirm_password": "Confirmer le mot de passe", + "confirm_password_placeholder": "Confirmez votre mot de passe", + "register_button": "Créer un compte", + "have_account": "Vous avez déjà un compte ?", + "login": "Se connecter", + "setup_title": "Configuration initiale", + "setup_step1": "Admin", + "setup_step2": "Système", + "setup_step3": "Terminé", + "admin_username": "Nom d'utilisateur administrateur", + "admin_email": "E-mail administrateur", + "admin_password": "Mot de passe administrateur", + "create_admin": "Créer l'administrateur", + "back_to_login": "Déjà configuré ?", + "admin_success": "Compte administrateur créé avec succès ! Vous pouvez maintenant vous connecter.", + "account_success": "Compte créé avec succès ! Vous pouvez maintenant vous connecter.", + "passwords_mismatch": "Les mots de passe ne correspondent pas", + "admin_create_error": "Erreur lors de la création du compte administrateur", + "or": "ou", + "sso_login": "Se connecter avec SSO", + "sso_login_provider": "Se connecter avec {{provider}}", + "magicLinkHint": "Pas de mot de passe ? Saisissez votre adresse e-mail et nous vous enverrons un lien de connexion à usage unique.", + "magicLinkEmailLabel": "Adresse e-mail", + "magicLinkEmailPlaceholder": "vous@exemple.com", + "magicLinkSubmit": "Envoyer le lien de connexion", + "magicLinkSent": "Si un compte existe pour cette adresse, un lien de connexion vient d'être envoyé. Consultez votre boîte de réception.", + "magicLinkUnavailable": "La connexion par e-mail n'est pas disponible sur ce serveur.", + "magicLinkNetworkError": "Impossible de joindre le serveur : {{message}}", + "magicLinkToggle": "Pas de mot de passe ? Recevez un lien par e-mail", + "passwordsMatch": "Les mots de passe correspondent", + "capsLock": "Verr. Maj activé" + }, + "storage": { + "title": "Stockage", + "calculating": "Calcul en cours...", + "used": "{{percentage}}% utilisé ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Ce type de fichier ne peut pas être prévisualisé.", + "download_file": "Télécharger le fichier", + "zoom_in": "Zoom avant", + "zoom_out": "Zoom arrière", + "zoom_reset": "Réinitialiser le zoom" + }, + "language_selector": { + "title": "Bienvenue !", + "subtitle": "Sélectionnez votre langue pour continuer", + "continue": "Continuer", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Aucun favori pour le moment", + "empty_hint": "Marquez des fichiers ou dossiers avec une étoile pour les ajouter à vos favoris", + "add": "Ajouter aux favoris", + "remove": "Retirer des favoris", + "added_title": "Ajouté aux favoris", + "added_msg": "ajouté aux favoris", + "removed_title": "Retiré des favoris", + "removed_msg": "retiré des favoris" + }, + "recent": { + "title": "Récents", + "clear": "Effacer les récents", + "accessed": "Consulté", + "empty_state": "Aucun fichier récent", + "empty_hint": "Les fichiers que vous ouvrez apparaîtront ici", + "loadMore": "Charger plus" + }, + "notifications": { + "file_renamed": "Fichier renommé", + "file_renamed_to": "Fichier renommé en « {{name}} »", + "folder_renamed": "Dossier renommé", + "folder_renamed_to": "Dossier renommé en « {{name}} »", + "file_uploaded": "Fichier téléversé", + "file_deleted": "Fichier déplacé vers la corbeille", + "folder_deleted": "Dossier déplacé vers la corbeille", + "item_deleted_permanently": "Élément supprimé définitivement", + "trash_emptied": "Corbeille vidée avec succès", + "empty": "No notifications", + "title": "Notifications", + "link_created": "Lien créé", + "share_success": "Lien de partage créé avec succès", + "upload_files_section_title": "Dépôt non disponible ici", + "upload_files_section_body": "Accédez à la section Fichiers pour déposer des fichiers" + }, + "batch": { + "one_selected": "1 élément sélectionné", + "n_selected": "{{count}} éléments sélectionnés", + "confirm_delete": "Voulez-vous vraiment déplacer {{count}} éléments vers la corbeille ?", + "move_title": "Déplacer {{count}} élément(s)", + "add_favorites": "Ajouter aux favoris", + "move_copy": "Déplacer ou copier" + }, + "admin": { + "page_title": "Panneau d'administration", + "back_to_app": "Retour à OxiCloud", + "loading": "Chargement…", + "access_denied": "Accès refusé", + "access_denied_desc": "Privilèges d'administrateur requis.", + "sign_in": "Se connecter", + "tab_dashboard": "Tableau de bord", + "tab_users": "Utilisateurs", + "tab_oidc": "SSO / OIDC", + "total_users": "Utilisateurs totaux", + "active_users": "Utilisateurs actifs", + "admins": "Admins", + "version": "Version", + "storage_overview": "Aperçu du stockage", + "used": "Utilisé", + "total_quota": "Quota total", + "usage_pct": "Utilisation %", + "users_over_80": "Utilisateurs >80% quota", + "users_over_quota": "Utilisateurs dépassant le quota", + "system": "Système", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quotas", + "enabled": "Activé", + "disabled": "Désactivé", + "active": "Actif", + "off": "Inactif", + "allow_registration": "Autoriser l'inscription publique", + "registration_warning": "L'inscription publique est désactivée. Seuls les admins peuvent créer des utilisateurs.", + "user_management": "Gestion des utilisateurs", + "create_user": "Créer un utilisateur", + "col_user": "Utilisateur", + "col_role": "Rôle", + "col_auth": "Auth", + "col_status": "Statut", + "col_storage": "Stockage", + "col_last_login": "Dernière connexion", + "col_actions": "Actions", + "loading_users": "Chargement des utilisateurs…", + "failed_load_users": "Échec du chargement", + "no_users_found": "Aucun utilisateur trouvé", + "showing_users": "Affichage {{from}}-{{to}} sur {{total}}", + "prev": "Précédent", + "next": "Suivant", + "inactive": "Inactif", + "you_badge": "(vous)", + "local": "Local", + "never": "Jamais", + "just_now": "À l'instant", + "minutes_ago": "il y a {{n}}min", + "hours_ago": "il y a {{n}}h", + "days_ago": "il y a {{n}}j", + "edit_quota_title": "Modifier le quota", + "reset_password_title": "Réinitialiser le mot de passe", + "toggle_role_title": "Changer de rôle", + "deactivate_title": "Désactiver", + "activate_title": "Activer", + "delete_title": "Supprimer", + "sso_title": "Authentification unique (OIDC / SSO)", + "enable_sso": "Activer l'authentification SSO", + "provider_name": "Nom du fournisseur", + "issuer_url": "URL de l'émetteur", + "issuer_url_hint": "URL de l'émetteur OpenID Connect", + "auto_discover": "Auto-découverte", + "discovering": "Découverte…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Laisser vide pour conserver la valeur", + "secret_configured": "Un client secret est déjà configuré", + "callback_url": "URL de rappel", + "callback_url_hint": "(enregistrer dans votre IdP)", + "advanced_settings": "Paramètres avancés", + "scopes": "Scopes", + "auto_provision": "Provisionner automatiquement les utilisateurs", + "admin_groups": "Groupes admin", + "admin_groups_hint": "Noms de groupes OIDC séparés par des virgules", + "disable_password": "Désactiver la connexion par mot de passe (OIDC uniquement)", + "password_warning": "Cela empêchera TOUTES les connexions par mot de passe !", + "test_btn": "Tester", + "save_btn": "Enregistrer", + "saving": "Enregistrement…", + "settings_saved": "Paramètres enregistrés — OIDC est maintenant {{status}}", + "quota_modal_title": "Mettre à jour le quota", + "quota_user_label": "Utilisateur :", + "new_quota": "Nouveau quota", + "quota_unlimited_hint": "0 pour illimité", + "cancel": "Annuler", + "create_user_title": "Créer un nouvel utilisateur", + "username_label": "Nom d'utilisateur", + "username_placeholder": "jeandupont", + "username_hint": "3–32 caractères", + "password_label": "Mot de passe", + "password_placeholder": "Min 8 caractères", + "email_label": "E-mail", + "email_optional": "(facultatif)", + "email_placeholder": "utilisateur@exemple.com (auto-généré si vide)", + "role_label": "Rôle", + "role_user": "Utilisateur", + "role_admin": "Admin", + "quota_label": "Quota", + "creating": "Création…", + "reset_pw_title": "Réinitialiser le mot de passe", + "new_password_label": "Nouveau mot de passe", + "resetting": "Réinitialisation…", + "reset_btn": "Réinitialiser", + "confirm_role_change": "Changer le rôle en {{role}} ?", + "confirm_deactivate": "Voulez-vous vraiment désactiver cet utilisateur ?", + "confirm_activate": "Voulez-vous vraiment activer cet utilisateur ?", + "confirm_delete_user": "SUPPRIMER l'utilisateur \"{{name}}\" ? Irréversible !", + "confirm_action": "Confirmer l'action", + "confirm_yes": "Confirmer", + "confirm_no": "Annuler", + "error_username_short": "Le nom d'utilisateur doit contenir au moins 3 caractères", + "error_password_short": "Le mot de passe doit contenir au moins 8 caractères", + "error_generic": "Échec", + "error_network": "Erreur réseau : {{message}}", + "error_create_user": "Impossible de créer l'utilisateur", + "tab_storage": "Stockage", + "storage_title": "Configuration du stockage", + "storage_current_backend": "Backend actuel", + "storage_total_blobs": "Total des blobs", + "storage_total_size": "Taille totale", + "storage_dedup_ratio": "Taux de déduplication", + "storage_backend": "Backend", + "storage_local": "Local", + "storage_s3": "Compatible S3", + "storage_provider_preset": "Préréglage du fournisseur", + "storage_preset_custom": "Personnalisé", + "storage_endpoint_url": "URL du point de terminaison", + "storage_endpoint_hint": "Laisser vide pour AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Région", + "storage_access_key": "Clé d'accès", + "storage_secret_key": "Clé secrète", + "storage_secret_configured": "Clé configurée", + "storage_key_placeholder": "Saisir une nouvelle clé", + "storage_path_style": "Forcer le style de chemin", + "storage_path_style_hint": "Requis pour MinIO et certains services compatibles S3", + "storage_test_connection": "Tester la connexion", + "storage_test_success": "Connexion réussie", + "storage_test_failure": "Échec de la connexion", + "storage_save": "Enregistrer la configuration", + "storage_saved": "Configuration enregistrée", + "storage_migration": "Migration des données", + "storage_migration_coming_soon": "Outils de migration bientôt disponibles", + "migration_status_label": "État de la migration", + "migration_start": "Démarrer la migration", + "migration_pause": "Pause", + "migration_resume": "Reprendre", + "migration_verify": "Vérifier", + "migration_complete": "Terminer", + "migration_started": "Migration démarrée", + "migration_paused_msg": "Migration en pause", + "migration_resumed_msg": "Migration reprise", + "migration_completed_msg": "Migration terminée avec succès", + "migration_verifying": "Vérification en cours...", + "migration_verify_passed": "Vérification réussie", + "migration_verify_failed": "Échec de la vérification", + "migration_failed_blobs": "Blobs échoués", + "testing": "Test en cours...", + "tab_smtp": "SMTP", + "smtp_title": "E-mail sortant (SMTP)", + "smtp_intro": "Le SMTP est configuré exclusivement via les variables d'environnement (OXICLOUD_SMTP_*). Les valeurs ci-dessous proviennent du serveur en cours d'exécution — pour les modifier, éditez l'environnement et redémarrez OxiCloud.", + "smtp_enabled_label": "État", + "smtp_enabled": "Activé", + "smtp_disabled": "Désactivé (hôte non défini)", + "smtp_test_title": "Envoyer un e-mail de test", + "smtp_test_intro": "Envoie un message de diagnostic au destinataire ci-dessous et affiche la réponse du serveur SMTP afin que vous puissiez la corréler avec les journaux de votre relais.", + "smtp_test_to": "Adresse du destinataire", + "smtp_send_test": "Envoyer l'e-mail de test", + "smtp_sending": "Envoi…", + "smtp_sent": "E-mail de test envoyé.", + "smtp_send_failed": "Échec de l'envoi.", + "smtp_server_code": "Le serveur a répondu", + "smtp_test_missing_to": "Veuillez saisir une adresse de destinataire.", + "smtp_not_configured": "Le SMTP n'est pas configuré sur ce serveur." + }, + "profile": { + "page_title": "Profil", + "back_to_app": "Retour à OxiCloud", + "loading": "Chargement…", + "not_authenticated": "Non authentifié", + "not_authenticated_desc": "Connectez-vous pour voir votre profil.", + "sign_in": "Se connecter", + "role_admin": "Administrateur", + "role_user": "Utilisateur", + "account_details": "Détails du compte", + "username": "Nom d'utilisateur", + "email": "E-mail", + "role": "Rôle", + "last_login": "Dernière connexion", + "storage": "Stockage", + "used": "Utilisé", + "quota": "Quota", + "usage": "Utilisation", + "unlimited": "Illimité", + "app_passwords": "Mots de passe d'application", + "app_pw_desc": "Générez des mots de passe pour les clients WebDAV, CalDAV et CardDAV. Chaque mot de passe n'est affiché qu'une seule fois.", + "app_pw_label_placeholder": "Libellé (ex. Thunderbird, macOS)", + "generate": "Générer", + "generating": "Génération…", + "new_password_for": "Nouveau mot de passe pour", + "copy_warning": "Copiez ce mot de passe maintenant. Vous ne pourrez plus le revoir.", + "copy_to_clipboard": "Copier dans le presse-papiers", + "col_label": "Libellé", + "col_created": "Créé", + "col_last_used": "Dernière utilisation", + "col_status": "Statut", + "active": "Actif", + "revoked": "Révoqué", + "revoke_title": "Révoquer", + "no_app_passwords": "Aucun mot de passe d'application.", + "client_sessions": "Sessions client", + "client_sessions_desc": "Générées automatiquement lors de la connexion d'un client compatible Nextcloud.", + "col_client": "Client", + "never": "Jamais", + "just_now": "À l'instant", + "minutes_ago": "il y a {{n}} min", + "hours_ago": "il y a {{n}}h", + "days_ago": "il y a {{n}} jours", + "edit_profile": "Modifier le profil", + "edit_oidc_managed": "Pour modifier vos informations (nom, prénom, photo de profil, …), veuillez les mettre à jour chez votre fournisseur d'identité. Vos changements apparaîtront à votre prochaine connexion.", + "username_claim_hint": "2 à 64 caractères, lettres / chiffres / point / tiret / souligné. Une fois choisi, le nom d'utilisateur ne peut plus être modifié (les clients DAV/NextCloud en dépendent).", + "username_already_claimed": "Nom d'utilisateur fixé et non modifiable (les clients DAV/NextCloud en dépendent).", + "given_name": "Prénom", + "family_name": "Nom", + "notify_on_share": "M'avertir par e-mail quand quelqu'un partage avec moi", + "notify_on_share_hint": "Lorsque décoché, les partages apparaissent toujours dans votre compte — vous ne recevrez simplement pas d'e-mail à leur sujet.", + "save_profile": "Enregistrer", + "profile_saved": "Profil mis à jour", + "profile_no_changes": "Aucun changement à enregistrer.", + "profile_save_failed": "Échec de l'enregistrement", + "username_taken_error": "Ce nom d'utilisateur est déjà pris.", + "username_immutable_error": "Votre nom d'utilisateur est déjà défini et ne peut plus être modifié ici. Contactez un administrateur si vous souhaitez le renommer.", + "change_password": "Changer le mot de passe", + "current_password": "Mot de passe actuel", + "new_password": "Nouveau mot de passe", + "min_8_chars": "Au moins 8 caractères", + "confirm_password": "Confirmer le nouveau mot de passe", + "update_password": "Mettre à jour le mot de passe", + "updating": "Mise à jour…", + "password_updated": "Mot de passe mis à jour avec succès", + "passwords_no_match": "Les mots de passe ne correspondent pas", + "password_too_short": "Le mot de passe doit contenir au moins 8 caractères", + "password_change_failed": "Échec du changement de mot de passe", + "error_network": "Erreur réseau : {{message}}", + "error_label_required": "Veuillez entrer un libellé", + "error_create_pw": "Impossible de créer le mot de passe", + "confirm_revoke": "Révoquer le mot de passe \"{{label}}\" ? Les clients l'utilisant ne fonctionneront plus.", + "error_revoke": "Échec de la révocation", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Téléchargement en cours...", + "files": "fichiers", + "complete": "{{count}} / {{total}} téléchargés" + }, + "storage_quota_exceeded": "Quota de stockage dépassé", + "sharedwithme": { + "pageTitle": "Partagé avec moi", + "pageDescription": "Fichiers et dossiers que d'autres utilisateurs ont partagés avec vous", + "emptyStateTitle": "Rien n'a encore été partagé avec vous", + "emptyStateDesc": "Les éléments partagés avec vous par d'autres utilisateurs apparaîtront ici", + "loadMore": "Charger plus", + "sharedBy": "Partagé par", + "colName": "Nom", + "colType": "Type", + "colSharedBy": "Partagé par", + "colDate": "Date de partage", + "colPermissions": "Permissions" + }, + "groupby": { + "none": "Aucun", + "title": "Grouper par", + "type": "Type", + "type.folders": "Dossiers", + "owner": "Propriétaire", + "shareDate": "Date de partage", + "favoriteDate": "Date d'ajout aux favoris", + "accessedAt": "Date d'accès", + "modifiedAt": "Date de modification", + "createdAt": "Date de création", + "size": "Taille", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nouveau" + }, + "dateBucket": { + "today": "Aujourd'hui", + "last7days": "7 derniers jours", + "last30days": "30 derniers jours" + }, + "groups": { + "title": "Gérer les groupes", + "create_button": "Créer un groupe", + "create_dialog_title": "Nouveau groupe", + "edit_dialog_title": "Renommer le groupe", + "name_label": "Nom", + "name_placeholder": "ingenierie", + "description_label": "Description (facultatif)", + "members_section": "Membres", + "add_member_placeholder": "Ajouter un utilisateur ou un groupe…", + "no_members": "Aucun membre pour le moment.", + "remove_member": "Retirer", + "delete_group": "Supprimer le groupe", + "delete_confirm": "Supprimer le groupe « {name} » ? Les autorisations associées à ce groupe seront révoquées.", + "empty_state": "Aucun groupe pour le moment.", + "load_more": "Charger plus", + "back_to_list": "Retour", + "loading": "Chargement…", + "virtual_badge": "Système", + "member_count_zero": "Aucun membre", + "member_count_one": "1 membre", + "member_count_other": "{count} membres", + "delete_confirm_label": "Tapez le nom du groupe pour confirmer :", + "delete_confirm_mismatch": "Tapez le nom du groupe exactement pour confirmer.", + "virtual_internal_name": "Interne", + "members_loading": "Chargement des membres…", + "members_empty": "Aucun membre", + "virtual_internal_explanation": "Tous les utilisateurs internes de ce serveur" + }, + "myshares": { + "copyLink": "Copier le lien", + "deleteLink": "Supprimer le lien", + "notifyByEmail": "Notifier par e-mail", + "notifyFailed": "Impossible d'envoyer la notification.", + "notifyGroupMembers": "Notifier les membres du groupe", + "notifyRateLimited": "Trop de notifications pour ce destinataire — réessayez plus tard.", + "removeAccess": "Retirer l'accès", + "resendInvitation": "Renvoyer l'e-mail d'invitation" + }, + "sort": { + "asc": "croissant", + "desc": "décroissant" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/hi.json b/frontend/static/locales/hi.json new file mode 100644 index 00000000..87725c07 --- /dev/null +++ b/frontend/static/locales/hi.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "यह साइन-इन लिंक अब वैध नहीं है", + "expired_body": "लिंक समाप्त हो गया हो सकता है या पहले से उपयोग किया जा चुका हो सकता है। हम आपको एक नया भेज सकते हैं — यह कुछ ही सेकंड में आपके इनबॉक्स में पहुँच जाएगा।", + "resend_to": "{{email}} को नया लिंक भेजें", + "generic_unavailable": "यह साइन-इन लिंक अब वैध नहीं है। यह पहले से उपयोग किया जा चुका हो सकता है या समाप्त हो गया हो सकता है। लॉगिन पृष्ठ से नया लिंक माँगें।", + "service_unavailable": "इस सर्वर पर मैजिक-लिंक साइन-इन सक्षम नहीं है।", + "internal_error": "साइन इन करते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", + "resend_failure": "लिंक भेजते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", + "cross_browser_title": "इस डिवाइस पर साइन-इन जारी रखें?", + "cross_browser_body": "आपने यह साइन-इन लिंक उससे भिन्न ब्राउज़र या डिवाइस में खोला है जहाँ से आपने इसका अनुरोध किया था।", + "cross_browser_warning": "यदि आपने यह लिंक माँगा है, तो आगे बढ़ना सुरक्षित है। यदि नहीं, तो इस पृष्ठ को बंद कर दें — जारी रखें पर क्लिक करने से कोई और आपके खाते में साइन-इन हो जाएगा।", + "cross_browser_continue": "जारी रखें और साइन इन करें", + "resend_confirmation_title": "अपना इनबॉक्स देखें", + "resend_confirmation_body": "यदि साइन-इन लिंक किसी सक्रिय खाते का था, तो अभी एक नया लिंक भेजा गया है। कृपया अपना इनबॉक्स देखें।", + "return_link": "OxiCloud पर वापस जाएँ" + }, + "email": { + "invitation": { + "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", + "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nइसे नीचे दिए गए लिंक पर क्लिक करके खोलें:\n{{link}}\n\nलिंक केवल एक बार काम करता है और {{ttl_hours}} घंटों में समाप्त हो जाता है।\nयदि आप इस आमंत्रण की अपेक्षा नहीं कर रहे थे, तो आप इस संदेश को अनदेखा कर सकते हैं।\n\n— OxiCloud" + }, + "login": { + "subject": "OxiCloud में साइन इन करें", + "body": "नमस्ते,\n\nOxiCloud में साइन इन करने के लिए नीचे दिए गए लिंक का उपयोग करें। लिंक केवल एक बार काम करता है और {{ttl_minutes}} मिनट में समाप्त हो जाता है। इसे उसी डिवाइस पर खोलें जहाँ से आपने अनुरोध किया था।\n\n{{link}}\n\nयदि आपने यह साइन-इन लिंक नहीं माँगा था, तो आप इस संदेश को अनदेखा कर सकते हैं — किसी और कार्रवाई की आवश्यकता नहीं है।\n\n— OxiCloud" + }, + "kind_file": "फ़ाइल", + "kind_folder": "फ़ोल्डर", + "english_fallback_divider": "--- अंग्रेज़ी संस्करण नीचे ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", + "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nअपना नया साझाकरण देखने के लिए OxiCloud खोलें:\n{{login_link}}\n\nहो सकता है आपके पास {{inviter}} से और भी नए साझाकरण हों — साइन इन करें और अपने सभी साझा किए गए आइटम देखें।\n\n— OxiCloud\n\nआपको यह संदेश इसलिए मिल रहा है क्योंकि आपका OxiCloud खाता है और साझाकरण-सूचना प्राथमिकता चालू है। आप इसे अपनी प्रोफ़ाइल में बंद कर सकते हैं (जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें)।" + } + } + }, + "app": { + "title": "OxiCloud", + "description": "न्यूनतम क्लाउड स्टोरेज सिस्टम" + }, + "nav": { + "files": "फ़ाइलें", + "shared": "साझा", + "recent": "हाल ही में", + "favorites": "पसंदीदा", + "photos": "फ़ोटो", + "music": "संगीत", + "trash": "रद्दी", + "sharedwithme": "मेरे साथ साझा किए गए" + }, + "photos": { + "empty_state": "अभी कोई फ़ोटो नहीं", + "empty_hint": "यहाँ देखने के लिए चित्र या वीडियो अपलोड करें", + "items_selected": "चयनित", + "view_daily": "दिन", + "view_monthly": "महीना", + "view_yearly": "वर्ष" + }, + "music": { + "create_playlist": "प्लेलिस्ट बनाएँ", + "playlists": "प्लेलिस्ट", + "no_playlists": "अभी कोई प्लेलिस्ट नहीं", + "select_playlist": "प्लेलिस्ट चुनें", + "select_hint": "साइडबार से प्लेलिस्ट चुनें या नई बनाएँ", + "add_tracks": "ट्रैक जोड़ें", + "no_tracks": "इस प्लेलिस्ट में कोई ट्रैक नहीं", + "unknown_artist": "अज्ञात कलाकार", + "unknown_title": "अज्ञात", + "confirm_delete": "इस प्लेलिस्ट को हटाएँ?", + "playlist_name": "प्लेलिस्ट का नाम", + "create": "बनाएँ", + "delete": "हटाएँ", + "share": "साझा करें", + "edit": "संपादित करें", + "play_all": "सभी चलाएँ", + "shuffle": "शफल", + "repeat": "दोहराएँ", + "repeat_one": "एक दोहराएँ", + "queue": "कतार", + "queue_empty": "कतार खाली है", + "not_playing": "नहीं चल रहा", + "play": "चलाएँ", + "pause": "रोकें", + "previous": "पिछला", + "next": "अगला", + "volume": "आवाज़", + "mute": "म्यूट", + "unmute": "अनम्यूट", + "title": "शीर्षक", + "artist": "कलाकार", + "album": "एल्बम", + "tracks": "ट्रैक", + "add": "जोड़ें", + "added": "जोड़ा गया!", + "added_to_playlist": "प्लेलिस्ट में जोड़ा गया", + "add_to_playlist": "प्लेलिस्ट में जोड़ें", + "load_error": "प्लेलिस्ट लोड करने में त्रुटि", + "add_error": "प्लेलिस्ट में ट्रैक नहीं जोड़े जा सके", + "no_playlists_yet": "अभी तक कोई प्लेलिस्ट नहीं। पहले एक बनाएं!", + "selected_files": "चयनित:", + "error": "त्रुटि", + "search_audio": "ऑडियो फ़ाइलें खोजें…", + "no_audio_files": "कोई ऑडियो फ़ाइल नहीं मिली", + "selected": "चयनित", + "loading": "लोड हो रहा है…", + "search_error": "ऑडियो फ़ाइलें लोड नहीं हो सकीं", + "adding": "जोड़ा जा रहा है…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "फ़ाइलें खोजें...", + "new_folder": "नया फ़ोल्डर", + "upload": "अपलोड", + "upload_files": "फ़ाइलें अपलोड करें", + "upload_folder": "फ़ोल्डर अपलोड करें", + "upload.uploading": "अपलोड हो रहा है...", + "upload.complete": "{count} / {total} अपलोड हुईं", + "upload.files": "फ़ाइलें", + "rename": "नाम बदलें", + "move": "यहाँ ले जाएँ...", + "move_to": "यहाँ ले जाएँ", + "delete": "हटाएँ", + "download": "डाउनलोड", + "view": "देखें", + "cancel": "रद्द करें", + "confirm": "पुष्टि करें", + "share": "साझा करें", + "favorite": "पसंदीदा में जोड़ें", + "unfavorite": "पसंदीदा से हटाएँ", + "copy": "कॉपी करें", + "notify": "सूचित करें", + "send": "भेजें", + "clear_recent": "हाल ही का साफ़ करें", + "logout": "लॉग आउट", + "create": "बनाएँ", + "search_btn": "खोजें", + "close": "बंद करें", + "delete_permanently": "स्थायी रूप से हटाएँ", + "empty_trash": "रद्दी खाली करें", + "open_parent_folder": "मूल फ़ोल्डर पर जाएं", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "दिखावट", + "about": "OxiCloud के बारे में", + "about_description": "Rust और Clean Architecture से बना क्लाउड स्टोरेज प्लेटफ़ॉर्म। तेज़, सुरक्षित और निजी।", + "admin_panel": "एडमिन पैनल", + "profile": "मेरी प्रोफ़ाइल", + "role_user": "उपयोगकर्ता", + "theme": { + "light": "हल्का", + "dark": "गहरा", + "auto": "सिस्टम जैसा" + }, + "manage_groups": "समूह प्रबंधित करें" + }, + "share": { + "dialogTitle": "शेयर लिंक", + "linkLabel": "शेयर लिंक:", + "copyLink": "कॉपी", + "permissions": "अनुमतियाँ:", + "permissionRead": "पढ़ें", + "permissionWrite": "लिखें", + "permissionReshare": "पुनः साझा करें", + "password": "पासवर्ड सुरक्षा:", + "generatePassword": "जनरेट करें", + "expiration": "समाप्ति तिथि:", + "update": "शेयर अपडेट करें", + "remove": "शेयर हटाएँ", + "notifyTitle": "सूचना भेजें", + "notifyEmailLabel": "ईमेल पता:", + "notifyMessageLabel": "संदेश (वैकल्पिक):", + "notifySend": "सूचना भेजें", + "shareWithOthers": "दूसरों के साथ साझा करें", + "sharePublicly": "सार्वजनिक रूप से साझा करें", + "shareSettings": "साझा सेटिंग्स", + "shareCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ", + "shareCreated": "शेयर लिंक सफलतापूर्वक बनाया गया", + "shareUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", + "shareRemoved": "शेयर सफलतापूर्वक हटाया गया", + "inviteByEmail": "ईमेल द्वारा आमंत्रित करें — आमंत्रण भेजा जाएगा", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "शेयर लिंक", + "share_linkLabel": "शेयर लिंक:", + "share_copyLink": "कॉपी", + "share_permissions": "अनुमतियाँ:", + "share_permissionRead": "पढ़ें", + "share_permissionWrite": "लिखें", + "share_permissionReshare": "पुनः साझा करें", + "share_password": "पासवर्ड सुरक्षा:", + "share_generatePassword": "जनरेट करें", + "share_expiration": "समाप्ति तिथि:", + "share_update": "शेयर अपडेट करें", + "share_remove": "शेयर हटाएँ", + "share_notifyTitle": "सूचना भेजें", + "share_notifyEmailLabel": "ईमेल पता:", + "share_notifyMessageLabel": "संदेश (वैकल्पिक):", + "share_notifySend": "सूचना भेजें", + "shared": { + "backToFiles": "फ़ाइलों पर वापस", + "pageTitle": "साझा संसाधन", + "pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", + "filterType": "प्रकार:", + "filterAll": "सभी", + "filterFiles": "फ़ाइलें", + "filterFolders": "फ़ोल्डर", + "sortBy": "क्रमबद्ध:", + "sortByName": "नाम", + "sortByDate": "साझा तिथि", + "sortByExpiration": "समाप्ति", + "search": "खोजें", + "colName": "नाम", + "colType": "प्रकार", + "colDateShared": "साझा तिथि", + "colExpiration": "समाप्ति", + "colPermissions": "अनुमतियाँ", + "colPassword": "पासवर्ड", + "colActions": "कार्य", + "emptyStateTitle": "अभी कोई साझा संसाधन नहीं", + "emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", + "goToFiles": "फ़ाइलों पर जाएँ", + "typeFile": "फ़ाइल", + "typeFolder": "फ़ोल्डर", + "noExpiration": "कोई समाप्ति नहीं", + "hasPassword": "हाँ", + "noPassword": "नहीं", + "editShare": "शेयर संपादित करें", + "notifyShare": "किसी को सूचित करें", + "copyLink": "लिंक कॉपी करें", + "removeShare": "शेयर हटाएँ", + "linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", + "linkCopyFailed": "लिंक कॉपी करने में विफल", + "itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", + "itemRemoved": "शेयर सफलतापूर्वक हटाया गया", + "invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", + "notificationSent": "सूचना सफलतापूर्वक भेजी गई", + "notificationFailed": "सूचना भेजने में विफल", + "shared_backToFiles": "फ़ाइलों पर वापस", + "shared_pageTitle": "साझा संसाधन", + "shared_pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", + "shared_filterType": "प्रकार:", + "shared_filterAll": "सभी", + "shared_filterFiles": "फ़ाइलें", + "shared_filterFolders": "फ़ोल्डर", + "shared_sortBy": "क्रमबद्ध:", + "shared_sortByName": "नाम", + "shared_sortByDate": "साझा तिथि", + "shared_sortByExpiration": "समाप्ति", + "shared_search": "खोजें", + "shared_colName": "नाम", + "shared_colType": "प्रकार", + "shared_colDateShared": "साझा तिथि", + "shared_colExpiration": "समाप्ति", + "shared_colPermissions": "अनुमतियाँ", + "shared_colPassword": "पासवर्ड", + "shared_colActions": "कार्य", + "shared_emptyStateTitle": "अभी कोई साझा संसाधन नहीं", + "shared_emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", + "shared_goToFiles": "फ़ाइलों पर जाएँ", + "shared_typeFile": "फ़ाइल", + "shared_typeFolder": "फ़ोल्डर", + "shared_noExpiration": "कोई समाप्ति नहीं", + "shared_hasPassword": "हाँ", + "shared_noPassword": "नहीं", + "shared_editShare": "शेयर संपादित करें", + "shared_notifyShare": "किसी को सूचित करें", + "shared_copyLink": "लिंक कॉपी करें", + "shared_removeShare": "शेयर हटाएँ", + "shared_linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", + "shared_linkCopyFailed": "लिंक कॉपी करने में विफल", + "shared_itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", + "shared_itemRemoved": "शेयर सफलतापूर्वक हटाया गया", + "shared_invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", + "shared_notificationSent": "सूचना सफलतापूर्वक भेजी गई", + "shared_notificationFailed": "सूचना भेजने में विफल" + }, + "files": { + "name": "नाम", + "type": "प्रकार", + "size": "आकार", + "modified": "संशोधित", + "no_files": "इस फ़ोल्डर में कोई फ़ाइल नहीं", + "empty_hint": "शुरू करने के लिए ह़ैलें अपलोड करें या होल्डर बनाएँ", + "loading": "फ़ाइलें लोड हो रही हैं…", + "view_grid": "ग्रिड दृश्य", + "view_list": "सूची दृश्य", + "file_types": { + "document": "दस्तावेज़", + "image": "चित्र", + "video": "वीडियो", + "audio": "ऑडियो", + "pdf": "PDF", + "text": "टेक्स्ट", + "folder": "फ़ोल्डर", + "spreadsheet": "स्प्रेडशीट", + "presentation": "प्रेज़ेंटेशन", + "archive": "संग्रह", + "installer": "इंस्टॉलर", + "code": "कोड" + }, + "owner": "स्वामी" + }, + "dialogs": { + "rename_folder": "फ़ोल्डर का नाम बदलें", + "rename_file": "फ़ाइल का नाम बदलें", + "new_name": "नया नाम", + "new_folder_title": "नया फ़ोल्डर", + "folder_name": "फ़ोल्डर का नाम", + "folder_placeholder": "मेरा फ़ोल्डर", + "rename_title": "नाम बदलें", + "move_file": "फ़ाइल ले जाएँ", + "move_folder": "फ़ोल्डर ले जाएँ", + "select_destination": "गंतव्य फ़ोल्डर चुनें:", + "select_this_folder": "यह फ़ोल्डर चुनें", + "go_to_parent": ".. (पैरेंट फ़ोल्डर)", + "no_subfolders": "कोई सब-फ़ोल्डर नहीं", + "root": "रूट", + "delete_confirmation": "क्या आप वाकई हटाना चाहते हैं", + "and_contents": "और इसकी सभी सामग्री", + "no_undo": "यह कार्य पूर्ववत नहीं किया जा सकता", + "confirm_title": "कार्य की पुष्टि करें", + "confirm_delete": "रद्दी में भेजें", + "confirm_delete_file": "क्या आप वाकई फ़ाइल \"{{name}}\" को रद्दी में भेजना चाहते हैं?", + "confirm_delete_folder": "क्या आप वाकई फ़ोल्डर \"{{name}}\" और उसकी सभी सामग्री को रद्दी में भेजना चाहते हैं?", + "confirm_permanent_delete": "स्थायी रूप से हटाएँ", + "confirm_permanent_delete_msg": "क्या आप वाकई इस आइटम को स्थायी रूप से हटाना चाहते हैं? यह कार्य पूर्ववत नहीं किया जा सकता।", + "confirm_empty_trash": "रद्दी खाली करें", + "confirm_delete_share": "शेयर लिंक हटाएँ", + "confirm_delete_share_msg": "क्या आप वाकई इस शेयर लिंक को हटाना चाहते हैं?", + "share_file": "फ़ाइल साझा करें", + "share_folder": "फ़ोल्डर साझा करें", + "existing_shares": "मौजूदा शेयर", + "share_options": "शेयर विकल्प", + "password": "पासवर्ड", + "expiration": "समाप्ति", + "permissions": "अनुमतियाँ", + "generated_link": "जनरेट किया गया लिंक", + "notify": "सूचना भेजें", + "recipient": "प्राप्तकर्ता", + "message": "संदेश", + "move_to_home": "होम फ़ोल्डर में ले जाएं" + }, + "dropzone": { + "drag_files": "फ़ाइलें यहाँ खींचें या चुनने के लिए क्लिक करें", + "drop_files": "अपलोड करने के लिए फ़ाइलें छोड़ें" + }, + "permissions": { + "read": "पढ़ें", + "write": "लिखें", + "reshare": "पुनः साझा करें" + }, + "errors": { + "file_not_found": "फ़ाइल नहीं मिली", + "folder_not_found": "फ़ोल्डर नहीं मिला", + "delete_error": "हटाने में त्रुटि", + "upload_error": "फ़ाइल अपलोड करने में त्रुटि", + "rename_error": "नाम बदलने में त्रुटि", + "move_error": "ले जाने में त्रुटि", + "empty_name": "नाम खाली नहीं हो सकता", + "name_exists": "इस नाम की फ़ाइल या फ़ोल्डर पहले से मौजूद है", + "generic_error": "एक त्रुटि हुई है", + "group_name_invalid": "समूह का नाम ईमेल उपसर्ग प्रारूप के अनुरूप होना चाहिए (अक्षर, अंक, बिंदु, डैश, अंडरस्कोर; 1–64 वर्ण).", + "group_cycle": "यह सदस्य समूहों के बीच चक्रीय संदर्भ बनाएगा।", + "group_depth_exceeded": "यह नेस्टिंग गहराई अनुमत अधिकतम (8) से अधिक है।", + "group_virtual_immutable": "«Internal» समूह सिस्टम द्वारा प्रबंधित है और इसे संशोधित नहीं किया जा सकता।", + "group_not_found": "समूह नहीं मिला।", + "group_name_taken": "इस नाम का एक समूह पहले से मौजूद है।" + }, + "breadcrumb": { + "home": "होम" + }, + "trash": { + "empty_trash": "रद्दी खाली करें", + "empty_state": "रद्दी खाली है", + "original_location": "मूल स्थान", + "deleted_date": "हटाने की तिथि", + "remaining": "शेष", + "actions": "कार्य", + "restore": "पुनर्स्थापित करें", + "delete_permanently": "स्थायी रूप से हटाएँ", + "empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।", + "groupby": { + "remaining_days": "शेष दिन", + "trashed_time": "हटाने का समय" + } + }, + "daysRemaining": { + "expired": "समाप्त", + "today": "आज", + "tomorrow": "कल", + "inDays": "{{count}} दिन" + }, + "expiryChip": { + "never": "कभी समाप्त नहीं होता", + "expired": "समाप्त", + "today": "आज समाप्त होता है", + "tomorrow": "कल समाप्त होता है", + "inDays": "{{count}} दिनों में समाप्त होता है", + "onDate": "{{date}} को समाप्त होता है" + }, + "auth": { + "login_title": "साइन इन", + "username": "उपयोगकर्ता नाम", + "username_placeholder": "अपना उपयोगकर्ता नाम दर्ज करें", + "login_identifier": "उपयोगकर्ता नाम या ईमेल", + "login_identifier_placeholder": "अपना उपयोगकर्ता नाम या ईमेल दर्ज करें", + "password": "पासवर्ड", + "password_placeholder": "अपना पासवर्ड दर्ज करें", + "login_button": "साइन इन", + "no_account": "खाता नहीं है?", + "register": "साइन अप करें", + "admin_setup": "पहली बार?", + "setup": "एडमिन सेटअप करें", + "register_title": "खाता बनाएँ", + "email": "ईमेल", + "email_placeholder": "अपना ईमेल दर्ज करें", + "confirm_password": "पासवर्ड की पुष्टि करें", + "confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें", + "register_button": "खाता बनाएँ", + "have_account": "पहले से खाता है?", + "login": "साइन इन", + "setup_title": "प्रारंभिक सेटअप", + "setup_step1": "एडमिन", + "setup_step2": "सिस्टम", + "setup_step3": "पूर्ण", + "admin_username": "एडमिन उपयोगकर्ता नाम", + "admin_email": "एडमिन ईमेल", + "admin_password": "एडमिन पासवर्ड", + "create_admin": "एडमिन बनाएँ", + "back_to_login": "पहले से सेटअप है?", + "admin_success": "एडमिन खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", + "account_success": "खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", + "passwords_mismatch": "पासवर्ड मेल नहीं खाते", + "admin_create_error": "एडमिन खाता बनाने में त्रुटि", + "or": "या", + "sso_login": "SSO से साइन इन करें", + "sso_login_provider": "{{provider}} से साइन इन करें", + "magicLinkHint": "पासवर्ड नहीं है? अपना ईमेल दर्ज करें और हम आपको एक बार उपयोग होने वाला साइन-इन लिंक भेज देंगे।", + "magicLinkEmailLabel": "ईमेल पता", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "साइन-इन लिंक भेजें", + "magicLinkSent": "यदि उस ईमेल के लिए कोई खाता मौजूद है, तो एक साइन-इन लिंक भेज दिया गया है। अपना इनबॉक्स देखें।", + "magicLinkUnavailable": "इस सर्वर पर ईमेल द्वारा साइन-इन उपलब्ध नहीं है।", + "magicLinkNetworkError": "सर्वर से कनेक्ट नहीं हो सका: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "स्टोरेज", + "calculating": "गणना हो रही है...", + "used": "{{percentage}}% उपयोग ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "इस फ़ाइल प्रकार का पूर्वावलोकन नहीं किया जा सकता।", + "download_file": "फ़ाइल डाउनलोड करें", + "zoom_in": "ज़ूम इन", + "zoom_out": "ज़ूम आउट", + "zoom_reset": "ज़ूम रीसेट" + }, + "language_selector": { + "title": "स्वागत है!", + "subtitle": "जारी रखने के लिए अपनी भाषा चुनें", + "continue": "आगे बढ़ें", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "hi": "हिन्दी", + "ar": "العربية", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "अभी कोई पसंदीदा नहीं", + "empty_hint": "पसंदीदा में जोड़ने के लिए फ़ाइलों या फ़ोल्डर को स्टार करें", + "add": "पसंदीदा में जोड़ें", + "remove": "पसंदीदा से हटाएँ", + "added_title": "पसंदीदा में जोड़ा गया", + "added_msg": "पसंदीदा में जोड़ा गया", + "removed_title": "पसंदीदा से हटाया गया", + "removed_msg": "पसंदीदा से हटाया गया" + }, + "recent": { + "title": "हाल ही में", + "clear": "हाल ही का साफ़ करें", + "accessed": "एक्सेस किया", + "empty_state": "कोई हाल की फ़ाइलें नहीं", + "empty_hint": "जो फ़ाइलें आप खोलेंगे वे यहाँ दिखेंगी", + "loadMore": "और लोड करें" + }, + "notifications": { + "file_renamed": "फ़ाइल का नाम बदला गया", + "file_renamed_to": "फ़ाइल का नाम \"{{name}}\" रखा गया", + "folder_renamed": "फ़ोल्डर का नाम बदला गया", + "folder_renamed_to": "फ़ोल्डर का नाम \"{{name}}\" रखा गया", + "file_uploaded": "फ़ाइल अपलोड हुई", + "file_deleted": "फ़ाइल रद्दी में भेजी गई", + "folder_deleted": "फ़ोल्डर रद्दी में भेजा गया", + "item_deleted_permanently": "आइटम स्थायी रूप से हटाया गया", + "trash_emptied": "रद्दी सफलतापूर्वक खाली की गई", + "title": "सूचनाएँ", + "empty": "कोई सूचना नहीं", + "link_created": "लिंक बनाया गया", + "share_success": "शेयर लिंक सफलतापूर्वक बनाया गया", + "upload_files_section_title": "यहाँ अपलोड उपलब्ध नहीं है", + "upload_files_section_body": "फ़ाइलें अपलोड करने के लिए फ़ाइलें अनुभाग पर जाएँ" + }, + "batch": { + "one_selected": "1 आइटम चयनित", + "n_selected": "{{count}} आइटम चयनित", + "confirm_delete": "क्या आप वाकई {{count}} आइटम रद्दी में भेजना चाहते हैं?", + "move_title": "{{count}} आइटम ले जाएँ", + "add_favorites": "पसंदीदा में जोड़ें", + "move_copy": "ले जाएँ या कॉपी करें" + }, + "admin": { + "page_title": "एडमिन पैनल", + "back_to_app": "OxiCloud पर वापस", + "loading": "लोड हो रहा है…", + "access_denied": "पहुंच अस्वीकृत", + "access_denied_desc": "व्यवस्थापक विशेषाधिकार आवश्यक।", + "sign_in": "साइन इन", + "tab_dashboard": "डैशबोर्ड", + "tab_users": "उपयोगकर्ता", + "tab_oidc": "SSO / OIDC", + "total_users": "कुल उपयोगकर्ता", + "active_users": "सक्रिय उपयोगकर्ता", + "admins": "व्यवस्थापक", + "version": "संस्करण", + "storage_overview": "स्टोरेज अवलोकन", + "used": "उपयोग किया", + "total_quota": "कुल कोटा", + "usage_pct": "उपयोग %", + "users_over_80": ">80% कोटा वाले", + "users_over_quota": "कोटा से अधिक", + "system": "सिस्टम", + "auth_label": "प्रमाणीकरण", + "oidc_label": "OIDC", + "quotas_label": "कोटा", + "enabled": "सक्षम", + "disabled": "अक्षम", + "active": "सक्रिय", + "off": "बंद", + "allow_registration": "सार्वजनिक पंजीकरण की अनुमति", + "registration_warning": "सार्वजनिक पंजीकरण अक्षम है। केवल व्यवस्थापक उपयोगकर्ता बना सकते हैं।", + "user_management": "उपयोगकर्ता प्रबंधन", + "create_user": "उपयोगकर्ता बनाएं", + "col_user": "उपयोगकर्ता", + "col_role": "भूमिका", + "col_auth": "प्रमाणीकरण", + "col_status": "स्थिति", + "col_storage": "स्टोरेज", + "col_last_login": "अंतिम लॉगिन", + "col_actions": "कार्रवाई", + "loading_users": "उपयोगकर्ता लोड हो रहे हैं…", + "failed_load_users": "लोड करने में विफल", + "no_users_found": "कोई उपयोगकर्ता नहीं मिला", + "showing_users": "{{from}}-{{to}} / {{total}} दिखा रहे हैं", + "prev": "पिछला", + "next": "अगला", + "inactive": "निष्क्रिय", + "you_badge": "(आप)", + "local": "स्थानीय", + "never": "कभी नहीं", + "just_now": "अभी", + "minutes_ago": "{{n}} मिनट पहले", + "hours_ago": "{{n}} घंटे पहले", + "days_ago": "{{n}} दिन पहले", + "edit_quota_title": "कोटा संपादित करें", + "reset_password_title": "पासवर्ड रीसेट", + "toggle_role_title": "भूमिका बदलें", + "deactivate_title": "निष्क्रिय करें", + "activate_title": "सक्रिय करें", + "delete_title": "हटाएं", + "sso_title": "सिंगल साइन-ऑन (OIDC / SSO)", + "enable_sso": "SSO सक्षम करें", + "provider_name": "प्रदाता का नाम", + "issuer_url": "जारीकर्ता URL", + "issuer_url_hint": "OpenID Connect जारीकर्ता URL", + "auto_discover": "स्वतः खोज", + "discovering": "खोज रहे हैं…", + "client_id": "क्लाइंट ID", + "client_secret": "क्लाइंट सीक्रेट", + "client_secret_placeholder": "वर्तमान मान बनाए रखने के लिए खाली छोड़ें", + "secret_configured": "क्लाइंट सीक्रेट पहले से कॉन्फ़िगर है", + "callback_url": "कॉलबैक URL", + "callback_url_hint": "(अपने IdP में पंजीकृत करें)", + "advanced_settings": "उन्नत सेटिंग्स", + "scopes": "स्कोप", + "auto_provision": "पहले लॉगिन पर स्वतः प्रावधान", + "admin_groups": "व्यवस्थापक समूह", + "admin_groups_hint": "अल्पविराम-पृथक OIDC समूह नाम", + "disable_password": "पासवर्ड लॉगिन अक्षम (केवल OIDC)", + "password_warning": "सभी पासवर्ड लॉगिन रुक जाएंगे!", + "test_btn": "परीक्षण", + "save_btn": "सहेजें", + "saving": "सहेज रहे हैं…", + "settings_saved": "सेटिंग्स सहेजी गईं — OIDC अब {{status}}", + "quota_modal_title": "स्टोरेज कोटा अपडेट", + "quota_user_label": "उपयोगकर्ता:", + "new_quota": "नया कोटा", + "quota_unlimited_hint": "असीमित के लिए 0", + "cancel": "रद्द करें", + "create_user_title": "नया उपयोगकर्ता बनाएं", + "username_label": "उपयोगकर्ता नाम", + "username_placeholder": "username", + "username_hint": "3–32 अक्षर", + "password_label": "पासवर्ड", + "password_placeholder": "न्यूनतम 8 अक्षर", + "email_label": "ईमेल", + "email_optional": "(वैकल्पिक)", + "email_placeholder": "user@example.com (खाली होने पर स्वतः)", + "role_label": "भूमिका", + "role_user": "उपयोगकर्ता", + "role_admin": "व्यवस्थापक", + "quota_label": "कोटा", + "creating": "बना रहे हैं…", + "reset_pw_title": "पासवर्ड रीसेट", + "new_password_label": "नया पासवर्ड", + "resetting": "रीसेट हो रहा है…", + "reset_btn": "रीसेट", + "confirm_role_change": "भूमिका {{role}} में बदलें?", + "confirm_deactivate": "इस उपयोगकर्ता को निष्क्रिय करें?", + "confirm_activate": "इस उपयोगकर्ता को सक्रिय करें?", + "confirm_delete_user": "उपयोगकर्ता \"{{name}}\" हटाएं? पूर्ववत नहीं होगा!", + "confirm_action": "कार्रवाई की पुष्टि", + "confirm_yes": "पुष्टि", + "confirm_no": "रद्द", + "error_username_short": "नाम कम से कम 3 अक्षर", + "error_password_short": "पासवर्ड कम से कम 8 अक्षर", + "error_generic": "विफल", + "error_network": "नेटवर्क त्रुटि: {{message}}", + "error_create_user": "उपयोगकर्ता बनाने में विफल", + "tab_storage": "स्टोरेज", + "storage_title": "स्टोरेज कॉन्फ़िगरेशन", + "storage_current_backend": "वर्तमान बैकएंड", + "storage_total_blobs": "कुल ब्लॉब्स", + "storage_total_size": "कुल आकार", + "storage_dedup_ratio": "डीडुप्लिकेशन अनुपात", + "storage_backend": "बैकएंड", + "storage_local": "स्थानीय", + "storage_s3": "S3 संगत", + "storage_provider_preset": "प्रदाता प्रीसेट", + "storage_preset_custom": "कस्टम", + "storage_endpoint_url": "एंडपॉइंट URL", + "storage_endpoint_hint": "AWS S3 के लिए खाली छोड़ें", + "storage_bucket": "बकेट", + "storage_region": "क्षेत्र", + "storage_access_key": "एक्सेस की", + "storage_secret_key": "सीक्रेट की", + "storage_secret_configured": "की कॉन्फ़िगर की गई", + "storage_key_placeholder": "नई की दर्ज करें", + "storage_path_style": "पाथ स्टाइल फ़ोर्स करें", + "storage_path_style_hint": "MinIO और कुछ S3-संगत सेवाओं के लिए आवश्यक", + "storage_test_connection": "कनेक्शन परीक्षण", + "storage_test_success": "कनेक्शन सफल", + "storage_test_failure": "कनेक्शन विफल", + "storage_save": "कॉन्फ़िगरेशन सहेजें", + "storage_saved": "कॉन्फ़िगरेशन सहेजी गई", + "storage_migration": "डेटा माइग्रेशन", + "storage_migration_coming_soon": "माइग्रेशन टूल्स जल्द आ रहे हैं", + "migration_status_label": "माइग्रेशन स्थिति", + "migration_start": "माइग्रेशन शुरू करें", + "migration_pause": "रोकें", + "migration_resume": "फिर से शुरू करें", + "migration_verify": "सत्यापित करें", + "migration_complete": "पूर्ण करें", + "migration_started": "माइग्रेशन शुरू हुआ", + "migration_paused_msg": "माइग्रेशन रोका गया", + "migration_resumed_msg": "माइग्रेशन फिर से शुरू हुआ", + "migration_completed_msg": "माइग्रेशन सफलतापूर्वक पूर्ण हुआ", + "migration_verifying": "सत्यापन हो रहा है...", + "migration_verify_passed": "सत्यापन पास", + "migration_verify_failed": "सत्यापन विफल", + "migration_failed_blobs": "विफल ब्लॉब्स", + "testing": "परीक्षण हो रहा है...", + "smtp_disabled": "अक्षम (होस्ट सेट नहीं)", + "smtp_enabled": "सक्षम", + "smtp_enabled_label": "स्थिति", + "smtp_intro": "SMTP केवल पर्यावरण चर (OXICLOUD_SMTP_*) के माध्यम से कॉन्फ़िगर किया जाता है। नीचे दिए गए मान चल रहे सर्वर से पढ़े जाते हैं — उन्हें बदलने के लिए, पर्यावरण संपादित करें और OxiCloud को पुनः आरंभ करें।", + "smtp_not_configured": "इस सर्वर पर SMTP कॉन्फ़िगर नहीं है।", + "smtp_send_failed": "भेजना विफल।", + "smtp_send_test": "परीक्षण ईमेल भेजें", + "smtp_sending": "भेजा जा रहा है…", + "smtp_sent": "परीक्षण ईमेल भेजा गया।", + "smtp_server_code": "सर्वर का उत्तर", + "smtp_test_intro": "नीचे दिए गए प्राप्तकर्ता को एक पूर्व-निर्धारित निदान संदेश भेजता है और SMTP सर्वर का उत्तर रिपोर्ट करता है ताकि आप इसे अपने रिले लॉग्स से मिला सकें।", + "smtp_test_missing_to": "प्राप्तकर्ता पता दर्ज करें।", + "smtp_test_title": "परीक्षण ईमेल भेजें", + "smtp_test_to": "प्राप्तकर्ता का पता", + "smtp_title": "जावक ईमेल (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "प्रोफ़ाइल", + "back_to_app": "OxiCloud पर वापस", + "loading": "लोड हो रहा है…", + "not_authenticated": "प्रमाणित नहीं", + "not_authenticated_desc": "अपना प्रोफ़ाइल देखने के लिए साइन इन करें।", + "sign_in": "साइन इन", + "role_admin": "व्यवस्थापक", + "role_user": "उपयोगकर्ता", + "account_details": "खाता विवरण", + "username": "उपयोगकर्ता नाम", + "email": "ईमेल", + "role": "भूमिका", + "last_login": "अंतिम लॉगिन", + "storage": "स्टोरेज", + "used": "उपयोग किया", + "quota": "कोटा", + "usage": "उपयोग", + "unlimited": "असीमित", + "app_passwords": "ऐप पासवर्ड", + "app_pw_desc": "WebDAV, CalDAV और CardDAV क्लाइंट के लिए पासवर्ड जनरेट करें। प्रत्येक पासवर्ड केवल एक बार दिखाया जाता है।", + "app_pw_label_placeholder": "लेबल (जैसे Thunderbird, macOS)", + "generate": "जनरेट करें", + "generating": "जनरेट हो रहा है…", + "new_password_for": "नया पासवर्ड", + "copy_warning": "इस पासवर्ड को अभी कॉपी करें। आप इसे दोबारा नहीं देख पाएंगे।", + "copy_to_clipboard": "क्लिपबोर्ड पर कॉपी करें", + "col_label": "लेबल", + "col_created": "बनाया गया", + "col_last_used": "अंतिम उपयोग", + "col_status": "स्थिति", + "active": "सक्रिय", + "revoked": "रद्द", + "revoke_title": "रद्द करें", + "no_app_passwords": "अभी तक कोई ऐप पासवर्ड नहीं।", + "client_sessions": "क्लाइंट सत्र", + "client_sessions_desc": "Nextcloud-संगत क्लाइंट कनेक्ट करने पर स्वतः जनरेट।", + "col_client": "क्लाइंट", + "never": "कभी नहीं", + "just_now": "अभी", + "minutes_ago": "{{n}} मिनट पहले", + "hours_ago": "{{n}} घंटे पहले", + "days_ago": "{{n}} दिन पहले", + "edit_profile": "प्रोफ़ाइल संपादित करें", + "edit_oidc_managed": "अपनी जानकारी (नाम, प्रथम नाम, प्रोफ़ाइल चित्र, …) बदलने के लिए, कृपया अपने पहचान प्रदाता पर इसे अद्यतन करें। आपके परिवर्तन अगले साइन-इन पर दिखाई देंगे।", + "username_claim_hint": "2–64 अक्षर, अक्षर / अंक / डॉट / डैश / अंडरस्कोर। एक बार चुनने के बाद, उपयोगकर्ता नाम नहीं बदला जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", + "username_already_claimed": "उपयोगकर्ता नाम सेट है और बदला नहीं जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", + "given_name": "प्रथम नाम", + "family_name": "अंतिम नाम", + "notify_on_share": "जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें", + "notify_on_share_hint": "जब अनचेक किया जाए, तो साझाकरण आपके खाते में दिखाई देते रहेंगे — आपको बस उनके बारे में ईमेल नहीं मिलेगा।", + "save_profile": "परिवर्तन सहेजें", + "profile_saved": "प्रोफ़ाइल अद्यतन की गई", + "profile_no_changes": "सहेजने के लिए कोई परिवर्तन नहीं।", + "profile_save_failed": "सहेजना विफल", + "username_taken_error": "यह उपयोगकर्ता नाम पहले से उपयोग में है।", + "username_immutable_error": "आपका उपयोगकर्ता नाम पहले से सेट है और यहाँ नहीं बदला जा सकता। यदि आपको नाम बदलने की आवश्यकता है तो किसी व्यवस्थापक से संपर्क करें।", + "change_password": "पासवर्ड बदलें", + "current_password": "वर्तमान पासवर्ड", + "new_password": "नया पासवर्ड", + "min_8_chars": "कम से कम 8 अक्षर", + "confirm_password": "नया पासवर्ड पुष्टि करें", + "update_password": "पासवर्ड अपडेट करें", + "updating": "अपडेट हो रहा है…", + "password_updated": "पासवर्ड सफलतापूर्वक अपडेट हुआ", + "passwords_no_match": "पासवर्ड मेल नहीं खाते", + "password_too_short": "पासवर्ड कम से कम 8 अक्षर का होना चाहिए", + "password_change_failed": "पासवर्ड बदलने में विफल", + "error_network": "नेटवर्क त्रुटि: {{message}}", + "error_label_required": "कृपया एक लेबल दर्ज करें", + "error_create_pw": "ऐप पासवर्ड बनाने में विफल", + "confirm_revoke": "ऐप पासवर्ड \"{{label}}\" रद्द करें? इसका उपयोग करने वाले क्लाइंट काम करना बंद कर देंगे।", + "error_revoke": "रद्द करने में विफल", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "अपलोड हो रहा है...", + "files": "फ़ाइलें", + "complete": "{{count}} / {{total}} अपलोड हुए" + }, + "storage_quota_exceeded": "स्टोरेज कोटा पार हो गया", + "sharedwithme": { + "pageTitle": "मेरे साथ साझा किया", + "pageDescription": "फ़ाइलें और फ़ोल्डर जो अन्य उपयोगकर्ताओं ने आपके साथ साझा किए हैं", + "emptyStateTitle": "अभी तक आपके साथ कुछ भी साझा नहीं किया गया", + "emptyStateDesc": "अन्य उपयोगकर्ताओं द्वारा आपके साथ साझा किए गए आइटम यहाँ दिखाई देंगे", + "loadMore": "और लोड करें", + "sharedBy": "द्वारा साझा किया", + "colName": "नाम", + "colType": "प्रकार", + "colSharedBy": "द्वारा साझा किया", + "colDate": "साझाकरण तिथि", + "colPermissions": "अनुमतियाँ" + }, + "groupby": { + "none": "कोई नहीं", + "title": "इसके अनुसार समूहीकृत करें", + "owner": "स्वामी", + "shareDate": "साझा तिथि", + "type": "प्रकार", + "type.folders": "फ़ोल्डर", + "accessedAt": "पहुँच की तारीख", + "modifiedAt": "संशोधन की तारीख", + "createdAt": "बनाने की तारीख", + "size": "आकार", + "favoriteDate": "पसंदीदा की तारीख", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "नया" + }, + "dateBucket": { + "today": "आज", + "last7days": "पिछले 7 दिन", + "last30days": "पिछले 30 दिन" + }, + "groups": { + "title": "समूह प्रबंधित करें", + "create_button": "समूह बनाएँ", + "create_dialog_title": "नया समूह", + "edit_dialog_title": "समूह का नाम बदलें", + "name_label": "नाम", + "name_placeholder": "engineering", + "description_label": "विवरण (वैकल्पिक)", + "members_section": "सदस्य", + "add_member_placeholder": "उपयोगकर्ता या समूह जोड़ें…", + "no_members": "अभी तक कोई सदस्य नहीं।", + "remove_member": "हटाएँ", + "delete_group": "समूह हटाएँ", + "delete_confirm": "समूह \"{name}\" को हटाएँ? इस समूह से जुड़ी अनुमतियाँ रद्द कर दी जाएँगी।", + "empty_state": "अभी तक कोई समूह नहीं।", + "load_more": "और लोड करें", + "back_to_list": "वापस", + "loading": "लोड हो रहा है…", + "virtual_badge": "सिस्टम", + "member_count_zero": "कोई सदस्य नहीं", + "member_count_one": "1 सदस्य", + "member_count_other": "{count} सदस्य", + "delete_confirm_label": "पुष्टि के लिए समूह का नाम लिखें:", + "delete_confirm_mismatch": "पुष्टि के लिए समूह का नाम बिल्कुल वैसा ही लिखें।", + "virtual_internal_name": "आंतरिक", + "members_loading": "सदस्य लोड हो रहे हैं…", + "members_empty": "कोई सदस्य नहीं", + "virtual_internal_explanation": "इस सर्वर पर हर आंतरिक उपयोगकर्ता" + }, + "myshares": { + "copyLink": "लिंक कॉपी करें", + "deleteLink": "लिंक हटाएँ", + "notifyByEmail": "ईमेल से सूचित करें", + "notifyFailed": "सूचना नहीं भेजी जा सकी।", + "notifyGroupMembers": "समूह के सदस्यों को सूचित करें", + "notifyRateLimited": "इस प्राप्तकर्ता के लिए बहुत अधिक सूचनाएँ — बाद में पुनः प्रयास करें।", + "removeAccess": "पहुँच हटाएँ", + "resendInvitation": "आमंत्रण ईमेल पुनः भेजें" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/it.json b/frontend/static/locales/it.json new file mode 100644 index 00000000..31bb6052 --- /dev/null +++ b/frontend/static/locales/it.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Questo link di accesso non è più valido", + "expired_body": "Il link potrebbe essere scaduto o già stato utilizzato. Possiamo inviartene uno nuovo — arriverà nella tua casella di posta in pochi secondi.", + "resend_to": "Invia un nuovo link a {{email}}", + "generic_unavailable": "Questo link di accesso non è più valido. Potrebbe essere già stato utilizzato o essere scaduto. Richiedi un nuovo link dalla pagina di accesso.", + "service_unavailable": "L'accesso tramite magic link non è abilitato su questo server.", + "internal_error": "Si è verificato un errore durante l'accesso. Riprova.", + "resend_failure": "Si è verificato un errore durante l'invio del link. Riprova.", + "cross_browser_title": "Continuare l'accesso su questo dispositivo?", + "cross_browser_body": "Hai aperto questo link di accesso in un browser o dispositivo diverso da quello in cui l'hai richiesto.", + "cross_browser_warning": "Se hai richiesto questo link, puoi continuare in sicurezza. In caso contrario, chiudi questa pagina — cliccare su Continua effettuerebbe l'accesso di qualcun altro al tuo account.", + "cross_browser_continue": "Continua e accedi", + "resend_confirmation_title": "Controlla la tua casella di posta", + "resend_confirmation_body": "Se il link di accesso apparteneva a un account attivo, è appena stato inviato un nuovo link. Controlla la tua casella di posta.", + "return_link": "Torna a OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", + "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nAprilo facendo clic sul link sottostante:\n{{link}}\n\nIl link è monouso e scade tra {{ttl_hours}} ore.\nSe non ti aspettavi questo invito, puoi ignorare questo messaggio.\n\n— OxiCloud" + }, + "login": { + "subject": "Accedi a OxiCloud", + "body": "Ciao,\n\nUsa il link sottostante per accedere a OxiCloud. Il link è monouso e scade tra {{ttl_minutes}} minuti. Aprilo sullo stesso dispositivo da cui l'hai richiesto.\n\n{{link}}\n\nSe non hai richiesto questo link di accesso, puoi ignorare questo messaggio — non è necessaria alcuna ulteriore azione.\n\n— OxiCloud" + }, + "kind_file": "file", + "kind_folder": "cartella", + "english_fallback_divider": "--- Versione inglese qui sotto ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", + "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nApri OxiCloud per vedere la tua nuova condivisione:\n{{login_link}}\n\nPotresti avere altre nuove condivisioni da {{inviter}} — accedi per vedere tutti gli elementi condivisi con te.\n\n— OxiCloud\n\nRicevi questo messaggio perché hai un account OxiCloud e la preferenza di notifica delle condivisioni è attiva. Puoi disattivarla dal tuo profilo (Avvisami via email quando qualcuno condivide con me)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Sistema di archiviazione cloud minimalista" + }, + "nav": { + "files": "File", + "shared": "Condivisioni", + "recent": "Recenti", + "favorites": "Preferiti", + "photos": "Foto", + "music": "Musica", + "trash": "Cestino", + "sharedwithme": "Condivisi con me" + }, + "photos": { + "empty_state": "Nessuna foto ancora", + "empty_hint": "Carica immagini o video per vederli qui", + "items_selected": "selezionati", + "view_daily": "Giorno", + "view_monthly": "Mese", + "view_yearly": "Anno" + }, + "music": { + "create_playlist": "Crea Playlist", + "playlists": "Playlist", + "no_playlists": "Nessuna playlist", + "select_playlist": "Seleziona una playlist", + "select_hint": "Scegli una playlist dalla barra laterale o creane una nuova", + "add_tracks": "Aggiungi Tracce", + "no_tracks": "Nessuna traccia in questa playlist", + "unknown_artist": "Artista Sconosciuto", + "unknown_title": "Sconosciuto", + "confirm_delete": "Eliminare questa playlist?", + "playlist_name": "Nome playlist", + "create": "Crea", + "delete": "Elimina", + "share": "Condividi", + "edit": "Modifica", + "play_all": "Riproduci Tutto", + "shuffle": "Casuale", + "repeat": "Ripeti", + "repeat_one": "Ripeti Una", + "queue": "Coda", + "queue_empty": "Coda vuota", + "not_playing": "Non in riproduzione", + "play": "Riproduci", + "pause": "Pausa", + "previous": "Precedente", + "next": "Successivo", + "volume": "Volume", + "mute": "Muto", + "unmute": "Attiva audio", + "title": "Titolo", + "artist": "Artista", + "album": "Album", + "tracks": "tracce", + "add": "Aggiungi", + "added": "Aggiunto!", + "added_to_playlist": "aggiunto alla playlist", + "add_to_playlist": "Aggiungi alla playlist", + "load_error": "Errore nel caricamento delle playlist", + "add_error": "Impossibile aggiungere le tracce", + "no_playlists_yet": "Nessuna playlist ancora. Creane una prima!", + "selected_files": "Selezionati:", + "error": "Errore", + "search_audio": "Cerca file audio…", + "no_audio_files": "Nessun file audio trovato", + "selected": "selezionati", + "loading": "Caricamento…", + "search_error": "Impossibile caricare i file audio", + "adding": "Aggiunta in corso…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "Cerca file...", + "new_folder": "Nuova cartella", + "upload": "Carica", + "upload_files": "Carica file", + "upload_folder": "Carica cartella", + "upload.uploading": "Caricamento...", + "upload.complete": "{count} / {total} caricati", + "upload.files": "file", + "rename": "Rinomina", + "move": "Sposta in...", + "move_to": "Sposta in", + "delete": "Elimina", + "download": "Scarica", + "view": "Visualizza", + "cancel": "Annulla", + "confirm": "Conferma", + "share": "Condividi", + "favorite": "Aggiungi ai preferiti", + "unfavorite": "Rimuovi dai preferiti", + "copy": "Copia", + "notify": "Notifica", + "send": "Invia", + "clear_recent": "Cancella recenti", + "logout": "Disconnetti", + "create": "Crea", + "search_btn": "Cerca", + "close": "Chiudi", + "delete_permanently": "Elimina definitivamente", + "empty_trash": "Svuota il cestino", + "open_parent_folder": "Vai alla cartella padre", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Aspetto", + "about": "Informazioni su OxiCloud", + "about_description": "Piattaforma di archiviazione cloud realizzata con Rust & Architettura Pulita. Veloce, sicura e privata.", + "admin_panel": "Pannello di amministrazione", + "profile": "Il mio profilo", + "role_user": "Utente", + "theme": { + "light": "Chiaro", + "dark": "Scuro", + "auto": "Come il sistema" + }, + "manage_groups": "Gestisci gruppi" + }, + "share": { + "dialogTitle": "Link di condivisione", + "linkLabel": "Link di condivisione:", + "copyLink": "Copia", + "permissions": "Permessi:", + "permissionRead": "Lettura", + "permissionWrite": "Scrittura", + "permissionReshare": "Ricondivisione", + "password": "Protezione password:", + "generatePassword": "Genera", + "expiration": "Data di scadenza:", + "update": "Aggiorna condivisione", + "remove": "Rimuovi condivisione", + "notifyTitle": "Invia notifica", + "notifyEmailLabel": "Indirizzo email:", + "notifyMessageLabel": "Messaggio (opzionale):", + "notifySend": "Invia notifica", + "shareWithOthers": "Condividi con altri", + "sharePublicly": "Condividi pubblicamente", + "shareSettings": "Impostazioni di condivisione", + "shareCopied": "Link copiato negli appunti", + "shareCreated": "Link di condivisione creato con successo", + "shareUpdated": "Impostazioni di condivisione aggiornate con successo", + "shareRemoved": "Condivisione rimossa con successo", + "inviteByEmail": "Invita via email — verrà inviato un invito", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Link di condivisione", + "share_linkLabel": "Link di condivisione:", + "share_copyLink": "Copia", + "share_permissions": "Permessi:", + "share_permissionRead": "Lettura", + "share_permissionWrite": "Scrittura", + "share_permissionReshare": "Ricondivisione", + "share_password": "Protezione password:", + "share_generatePassword": "Genera", + "share_expiration": "Data di scadenza:", + "share_update": "Aggiorna condivisione", + "share_remove": "Rimuovi condivisione", + "share_notifyTitle": "Invia notifica", + "share_notifyEmailLabel": "Indirizzo email:", + "share_notifyMessageLabel": "Messaggio (opzionale):", + "share_notifySend": "Invia notifica", + "shared": { + "backToFiles": "Torna ai file", + "pageTitle": "Risorse condivise", + "pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", + "filterType": "Tipo:", + "filterAll": "Tutti", + "filterFiles": "File", + "filterFolders": "Cartelle", + "sortBy": "Ordina per:", + "sortByName": "Nome", + "sortByDate": "Data di condivisione", + "sortByExpiration": "Scadenza", + "search": "Cerca", + "colName": "Nome", + "colType": "Tipo", + "colDateShared": "Data di condivisione", + "colExpiration": "Scadenza", + "colPermissions": "Permessi", + "colPassword": "Password", + "colActions": "Azioni", + "emptyStateTitle": "Ancora nessuna risorsa condivisa", + "emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", + "goToFiles": "Vai ai file", + "typeFile": "File", + "typeFolder": "Cartella", + "noExpiration": "Nessuna scadenza", + "hasPassword": "Sì", + "noPassword": "No", + "editShare": "Modifica condivisione", + "notifyShare": "Notifica a qualcuno", + "copyLink": "Copia link", + "removeShare": "Rimuovi condivisione", + "linkCopied": "Link copiato negli appunti!", + "linkCopyFailed": "Impossibile copiare il link", + "itemUpdated": "Impostazioni di condivisione aggiornate con successo", + "itemRemoved": "Condivisione rimossa con successo", + "invalidEmail": "Inserisci un indirizzo email valido", + "notificationSent": "Notifica inviata con successo", + "notificationFailed": "Impossibile inviare la notifica", + "shared_backToFiles": "Torna ai file", + "shared_pageTitle": "Risorse condivise", + "shared_pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", + "shared_filterType": "Tipo:", + "shared_filterAll": "Tutti", + "shared_filterFiles": "File", + "shared_filterFolders": "Cartelle", + "shared_sortBy": "Ordina per:", + "shared_sortByName": "Nome", + "shared_sortByDate": "Data di condivisione", + "shared_sortByExpiration": "Scadenza", + "shared_search": "Cerca", + "shared_colName": "Nome", + "shared_colType": "Tipo", + "shared_colDateShared": "Data di condivisione", + "shared_colExpiration": "Scadenza", + "shared_colPermissions": "Permessi", + "shared_colPassword": "Password", + "shared_colActions": "Azioni", + "shared_emptyStateTitle": "Ancora nessuna risorsa condivisa", + "shared_emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", + "shared_goToFiles": "Vai ai file", + "shared_typeFile": "File", + "shared_typeFolder": "Cartella", + "shared_noExpiration": "Nessuna scadenza", + "shared_hasPassword": "Sì", + "shared_noPassword": "No", + "shared_editShare": "Modifica condivisione", + "shared_notifyShare": "Notifica a qualcuno", + "shared_copyLink": "Copia link", + "shared_removeShare": "Rimuovi condivisione", + "shared_linkCopied": "Link copiato negli appunti!", + "shared_linkCopyFailed": "Impossibile copiare il link", + "shared_itemUpdated": "Impostazioni di condivisione aggiornate con successo", + "shared_itemRemoved": "Condivisione rimossa con successo", + "shared_invalidEmail": "Inserisci un indirizzo email valido", + "shared_notificationSent": "Notifica inviata con successo", + "shared_notificationFailed": "Impossibile inviare la notifica" + }, + "files": { + "name": "Nome", + "type": "Tipo", + "size": "Dimensione", + "modified": "Modificato", + "no_files": "Nessun file in questa cartella", + "empty_hint": "Carica file o crea cartelle per iniziare", + "loading": "Caricamento file…", + "view_grid": "Visualizzazione griglia", + "view_list": "Visualizzazione elenco", + "file_types": { + "document": "Documento", + "image": "Immagine", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Testo", + "folder": "Cartella", + "spreadsheet": "Foglio di calcolo", + "presentation": "Presentazione", + "archive": "Archivio", + "installer": "Programma di installazione", + "code": "Codice" + }, + "owner": "Proprietario" + }, + "dialogs": { + "rename_folder": "Rinomina cartella", + "rename_file": "Rinomina file", + "new_name": "Nuovo nome", + "new_folder_title": "Nuova cartella", + "folder_name": "Nome cartella", + "folder_placeholder": "La mia cartella", + "rename_title": "Rinomina", + "move_file": "Sposta file", + "move_folder": "Sposta cartella", + "select_destination": "Seleziona cartella di destinazione:", + "root": "Root", + "delete_confirmation": "Sei sicuro di voler eliminare", + "and_contents": "e tutto il suo contenuto", + "no_undo": "Questa azione non può essere annullata", + "confirm_title": "Conferma azione", + "confirm_delete": "Sposta nel cestino", + "confirm_delete_file": "Sei sicuro di voler spostare il file \"{{name}}\" nel cestino?", + "confirm_delete_folder": "Sei sicuro di voler spostare la cartella \"{{name}}\" e tutto il suo contenuto nel cestino?", + "confirm_permanent_delete": "Elimina definitivamente", + "confirm_permanent_delete_msg": "Sei sicuro di voler eliminare definitivamente questo elemento? Questa azione non può essere annullata.", + "confirm_empty_trash": "Svuota il cestino", + "confirm_delete_share": "Elimina link di condivisione", + "confirm_delete_share_msg": "Sei sicuro di voler eliminare questo link di condivisione?", + "share_file": "Condividi File", + "share_folder": "Condividi Cartella", + "existing_shares": "Condivisioni Esistenti", + "share_options": "Opzioni di Condivisione", + "password": "Password", + "expiration": "Scadenza", + "permissions": "Permessi", + "generated_link": "Link Generato", + "notify": "Invia Notifica", + "recipient": "Destinatario", + "message": "Messaggio", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Sposta nella cartella home" + }, + "dropzone": { + "drag_files": "Trascina i file qui o clicca per selezionare", + "drop_files": "Rilascia i file per caricarli" + }, + "permissions": { + "read": "Lettura", + "write": "Scrittura", + "reshare": "Ricondividi" + }, + "errors": { + "file_not_found": "File non trovato", + "folder_not_found": "Cartella non trovata", + "delete_error": "Errore durante l'eliminazione", + "upload_error": "Errore durante il caricamento del file", + "rename_error": "Errore durante la rinomina", + "move_error": "Errore durante lo spostamento", + "empty_name": "Il nome non può essere vuoto", + "name_exists": "Un file o una cartella con quel nome esiste già", + "generic_error": "Si è verificato un errore", + "group_name_invalid": "Il nome del gruppo deve rispettare il formato del prefisso email (lettere, cifre, punto, trattino, trattino basso; 1–64 caratteri).", + "group_cycle": "Questo membro creerebbe un riferimento circolare tra gruppi.", + "group_depth_exceeded": "Questa profondità di annidamento supera il massimo consentito (8).", + "group_virtual_immutable": "Il gruppo «Internal» è gestito dal sistema e non può essere modificato.", + "group_not_found": "Gruppo non trovato.", + "group_name_taken": "Un gruppo con questo nome esiste già." + }, + "breadcrumb": { + "home": "Home" + }, + "trash": { + "empty_trash": "Svuota il cestino", + "empty_state": "Il cestino è vuoto", + "original_location": "Posizione originale", + "deleted_date": "Data di eliminazione", + "remaining": "Rimanente", + "actions": "Azioni", + "restore": "Ripristina", + "delete_permanently": "Elimina definitivamente", + "empty_confirm": "Sei sicuro di voler svuotare il cestino? Questa operazione eliminerà definitivamente tutti gli elementi.", + "groupby": { + "remaining_days": "Giorni rimanenti", + "trashed_time": "Data di eliminazione" + } + }, + "daysRemaining": { + "expired": "Scaduto", + "today": "Oggi", + "tomorrow": "Domani", + "inDays": "{{count}} giorni" + }, + "expiryChip": { + "never": "Non scade mai", + "expired": "Scaduto", + "today": "Scade oggi", + "tomorrow": "Scade domani", + "inDays": "Scade tra {{count}} giorni", + "onDate": "Scade il {{date}}" + }, + "auth": { + "login_title": "Accedi", + "username": "Nome utente", + "username_placeholder": "Inserisci il tuo nome utente", + "login_identifier": "Nome utente o email", + "login_identifier_placeholder": "Inserisci il tuo nome utente o email", + "password": "Password", + "password_placeholder": "Inserisci la tua password", + "login_button": "Accedi", + "no_account": "Non hai un account?", + "register": "Registrati", + "admin_setup": "È la prima volta?", + "setup": "Configura amministratore", + "register_title": "Crea account", + "email": "Email", + "email_placeholder": "Inserisci la tua email", + "confirm_password": "Conferma password", + "confirm_password_placeholder": "Conferma la tua password", + "register_button": "Crea account", + "have_account": "Hai già un account?", + "login": "Accedi", + "setup_title": "Configurazione iniziale", + "setup_step1": "Amministratore", + "setup_step2": "Sistema", + "setup_step3": "Completa", + "admin_username": "Nome utente amministratore", + "admin_email": "Email amministratore", + "admin_password": "Password amministratore", + "create_admin": "Crea amministratore", + "back_to_login": "Già configurato?", + "admin_success": "Account amministratore creato con successo! Ora puoi accedere.", + "account_success": "Account creato con successo! Ora puoi accedere.", + "passwords_mismatch": "Le password non corrispondono", + "admin_create_error": "Errore durante la creazione dell'account amministratore", + "or": "o", + "sso_login": "Accedi con SSO", + "sso_login_provider": "Accedi con {{provider}}", + "magicLinkHint": "Niente password? Inserisci la tua email e ti invieremo un link di accesso monouso.", + "magicLinkEmailLabel": "Indirizzo email", + "magicLinkEmailPlaceholder": "tu@esempio.com", + "magicLinkSubmit": "Invia link di accesso", + "magicLinkSent": "Se esiste un account per questa email, è stato inviato un link di accesso. Controlla la tua casella di posta.", + "magicLinkUnavailable": "L'accesso tramite email non è disponibile su questo server.", + "magicLinkNetworkError": "Impossibile raggiungere il server: {{message}}", + "magicLinkToggle": "Nessuna password? Ricevi un link via e-mail", + "passwordsMatch": "Le password corrispondono", + "capsLock": "Bloc Maiusc attivo" + }, + "storage": { + "title": "Archiviazione", + "calculating": "Calcolo in corso...", + "used": "{{percentage}}% utilizzato ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Questo tipo di file non può essere visualizzato in anteprima.", + "download_file": "Scarica file", + "zoom_in": "Ingrandisci", + "zoom_out": "Riduci", + "zoom_reset": "Reimposta zoom" + }, + "language_selector": { + "title": "Benvenuto!", + "subtitle": "Seleziona la tua lingua per continuare", + "continue": "Continua", + "languages": { + "en": "Inglese", + "es": "Spagnolo", + "zh": "Cinese", + "fa": "Persiano", + "fr": "Francese", + "de": "Tedesco", + "pt": "Portoghese", + "it": "Italiano", + "ar": "العربية", + "hi": "हिन्दी", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Ancora nessun preferito", + "empty_hint": "Aggiungi file o cartelle ai preferiti per inserirli qui", + "add": "Aggiungi ai preferiti", + "remove": "Rimuovi dai preferiti", + "added_title": "Aggiunto ai preferiti", + "added_msg": "aggiunto ai preferiti", + "removed_title": "Rimosso dai preferiti", + "removed_msg": "rimosso dai preferiti" + }, + "recent": { + "title": "Recenti", + "clear": "Cancella recenti", + "accessed": "Accesso", + "empty_state": "Nessun file recente", + "empty_hint": "I file che apri appariranno qui", + "loadMore": "Carica altri" + }, + "notifications": { + "file_renamed": "File rinominato", + "file_renamed_to": "File rinominato in \"{{name}}\"", + "folder_renamed": "Cartella rinominata", + "folder_renamed_to": "Cartella rinominata in \"{{name}}\"", + "file_uploaded": "File caricato", + "file_deleted": "File spostato nel cestino", + "folder_deleted": "Cartella spostata nel cestino", + "item_deleted_permanently": "Elemento eliminato definitivamente", + "trash_emptied": "Cestino svuotato con successo", + "empty": "No notifications", + "title": "Notifications", + "link_created": "Link creato", + "share_success": "Link di condivisione creato con successo", + "upload_files_section_title": "Caricamento non disponibile qui", + "upload_files_section_body": "Vai alla sezione File per caricare i file" + }, + "batch": { + "one_selected": "1 elemento selezionato", + "n_selected": "{{count}} elementi selezionati", + "confirm_delete": "Sei sicuro di voler spostare {{count}} elementi nel cestino?", + "move_title": "Sposta {{count}} elemento/i", + "add_favorites": "Aggiungi ai preferiti", + "move_copy": "Sposta o copia" + }, + "admin": { + "page_title": "Pannello di Amministrazione", + "back_to_app": "Torna a OxiCloud", + "loading": "Caricamento…", + "access_denied": "Accesso Negato", + "access_denied_desc": "Privilegi di amministratore necessari.", + "sign_in": "Accedi", + "tab_dashboard": "Dashboard", + "tab_users": "Utenti", + "tab_oidc": "SSO / OIDC", + "total_users": "Utenti Totali", + "active_users": "Utenti Attivi", + "admins": "Amministratori", + "version": "Versione", + "storage_overview": "Panoramica Archiviazione", + "used": "Usato", + "total_quota": "Quota Totale", + "usage_pct": "Utilizzo %", + "users_over_80": "Utenti >80% quota", + "users_over_quota": "Utenti oltre la quota", + "system": "Sistema", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quote", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "active": "Attivo", + "off": "Spento", + "allow_registration": "Consenti registrazione pubblica", + "registration_warning": "La registrazione pubblica è disabilitata. Solo gli admin possono creare utenti.", + "user_management": "Gestione Utenti", + "create_user": "Crea Utente", + "col_user": "Utente", + "col_role": "Ruolo", + "col_auth": "Auth", + "col_status": "Stato", + "col_storage": "Archiviazione", + "col_last_login": "Ultimo Accesso", + "col_actions": "Azioni", + "loading_users": "Caricamento utenti…", + "failed_load_users": "Impossibile caricare", + "no_users_found": "Nessun utente trovato", + "showing_users": "Mostrando {{from}}-{{to}} di {{total}}", + "prev": "Precedente", + "next": "Successivo", + "inactive": "Inattivo", + "you_badge": "(tu)", + "local": "Locale", + "never": "Mai", + "just_now": "Proprio adesso", + "minutes_ago": "{{n}}min fa", + "hours_ago": "{{n}}h fa", + "days_ago": "{{n}}g fa", + "edit_quota_title": "Modifica quota", + "reset_password_title": "Reimposta password", + "toggle_role_title": "Cambia ruolo", + "deactivate_title": "Disattiva", + "activate_title": "Attiva", + "delete_title": "Elimina", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "Abilita autenticazione SSO", + "provider_name": "Nome Provider", + "issuer_url": "URL Emittente", + "issuer_url_hint": "URL dell'emittente OpenID Connect", + "auto_discover": "Auto-scoperta", + "discovering": "Scoperta…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Lascia vuoto per mantenere il valore", + "secret_configured": "Un client secret è già configurato", + "callback_url": "URL di Callback", + "callback_url_hint": "(registra nel tuo IdP)", + "advanced_settings": "Impostazioni Avanzate", + "scopes": "Scopes", + "auto_provision": "Provisioning automatico degli utenti", + "admin_groups": "Gruppi Admin", + "admin_groups_hint": "Nomi di gruppi OIDC separati da virgola", + "disable_password": "Disabilita accesso con password (solo OIDC)", + "password_warning": "Questo impedirà TUTTI gli accessi tramite password!", + "test_btn": "Test", + "save_btn": "Salva", + "saving": "Salvataggio…", + "settings_saved": "Impostazioni salvate — OIDC ora è {{status}}", + "quota_modal_title": "Aggiorna Quota", + "quota_user_label": "Utente:", + "new_quota": "Nuova Quota", + "quota_unlimited_hint": "0 per illimitato", + "cancel": "Annulla", + "create_user_title": "Crea Nuovo Utente", + "username_label": "Nome utente", + "username_placeholder": "mariorossi", + "username_hint": "3–32 caratteri", + "password_label": "Password", + "password_placeholder": "Min 8 caratteri", + "email_label": "Email", + "email_optional": "(facoltativo)", + "email_placeholder": "utente@esempio.com (auto-generata se vuoto)", + "role_label": "Ruolo", + "role_user": "Utente", + "role_admin": "Admin", + "quota_label": "Quota", + "creating": "Creazione…", + "reset_pw_title": "Reimposta Password", + "new_password_label": "Nuova Password", + "resetting": "Reimpostazione…", + "reset_btn": "Reimposta", + "confirm_role_change": "Cambiare ruolo a {{role}}?", + "confirm_deactivate": "Sei sicuro di voler disattivare questo utente?", + "confirm_activate": "Sei sicuro di voler attivare questo utente?", + "confirm_delete_user": "ELIMINARE l'utente \"{{name}}\"? Azione irreversibile!", + "confirm_action": "Conferma Azione", + "confirm_yes": "Conferma", + "confirm_no": "Annulla", + "error_username_short": "Il nome utente deve avere almeno 3 caratteri", + "error_password_short": "La password deve avere almeno 8 caratteri", + "error_generic": "Fallito", + "error_network": "Errore di rete: {{message}}", + "error_create_user": "Impossibile creare l'utente", + "tab_storage": "Archiviazione", + "storage_title": "Configurazione archiviazione", + "storage_current_backend": "Backend corrente", + "storage_total_blobs": "Blob totali", + "storage_total_size": "Dimensione totale", + "storage_dedup_ratio": "Rapporto deduplicazione", + "storage_backend": "Backend", + "storage_local": "Locale", + "storage_s3": "Compatibile S3", + "storage_provider_preset": "Preset fornitore", + "storage_preset_custom": "Personalizzato", + "storage_endpoint_url": "URL endpoint", + "storage_endpoint_hint": "Lasciare vuoto per AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Regione", + "storage_access_key": "Chiave di accesso", + "storage_secret_key": "Chiave segreta", + "storage_secret_configured": "Chiave configurata", + "storage_key_placeholder": "Inserisci nuova chiave", + "storage_path_style": "Forza stile percorso", + "storage_path_style_hint": "Richiesto per MinIO e alcuni servizi compatibili S3", + "storage_test_connection": "Testa connessione", + "storage_test_success": "Connessione riuscita", + "storage_test_failure": "Connessione fallita", + "storage_save": "Salva configurazione", + "storage_saved": "Configurazione salvata", + "storage_migration": "Migrazione dati", + "storage_migration_coming_soon": "Strumenti di migrazione in arrivo", + "migration_status_label": "Stato migrazione", + "migration_start": "Avvia migrazione", + "migration_pause": "Pausa", + "migration_resume": "Riprendi", + "migration_verify": "Verifica", + "migration_complete": "Completa", + "migration_started": "Migrazione avviata", + "migration_paused_msg": "Migrazione in pausa", + "migration_resumed_msg": "Migrazione ripresa", + "migration_completed_msg": "Migrazione completata con successo", + "migration_verifying": "Verifica in corso...", + "migration_verify_passed": "Verifica superata", + "migration_verify_failed": "Verifica fallita", + "migration_failed_blobs": "Blob falliti", + "testing": "Test in corso...", + "smtp_disabled": "Disabilitato (host non impostato)", + "smtp_enabled": "Abilitato", + "smtp_enabled_label": "Stato", + "smtp_intro": "SMTP è configurato esclusivamente tramite variabili d'ambiente (OXICLOUD_SMTP_*). I valori sottostanti sono letti dal server in esecuzione — per modificarli, modifica l'ambiente e riavvia OxiCloud.", + "smtp_not_configured": "SMTP non è configurato su questo server.", + "smtp_send_failed": "Invio non riuscito.", + "smtp_send_test": "Invia email di prova", + "smtp_sending": "Invio in corso…", + "smtp_sent": "Email di prova inviata.", + "smtp_server_code": "Risposta del server", + "smtp_test_intro": "Invia un messaggio diagnostico predefinito al destinatario indicato sotto e riporta la risposta del server SMTP, così puoi correlarla con i log del tuo relay.", + "smtp_test_missing_to": "Inserisci un indirizzo destinatario.", + "smtp_test_title": "Invia un'email di prova", + "smtp_test_to": "Indirizzo destinatario", + "smtp_title": "Email in uscita (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Profilo", + "back_to_app": "Torna a OxiCloud", + "loading": "Caricamento…", + "not_authenticated": "Non Autenticato", + "not_authenticated_desc": "Accedi per visualizzare il tuo profilo.", + "sign_in": "Accedi", + "role_admin": "Amministratore", + "role_user": "Utente", + "account_details": "Dettagli Account", + "username": "Nome utente", + "email": "Email", + "role": "Ruolo", + "last_login": "Ultimo accesso", + "storage": "Archiviazione", + "used": "Usato", + "quota": "Quota", + "usage": "Utilizzo", + "unlimited": "Illimitato", + "app_passwords": "Password Applicazione", + "app_pw_desc": "Genera password per client WebDAV, CalDAV e CardDAV. Ogni password viene mostrata una sola volta.", + "app_pw_label_placeholder": "Etichetta (es. Thunderbird, macOS)", + "generate": "Genera", + "generating": "Generazione…", + "new_password_for": "Nuova password per", + "copy_warning": "Copia questa password ora. Non potrai rivederla.", + "copy_to_clipboard": "Copia negli appunti", + "col_label": "Etichetta", + "col_created": "Creato", + "col_last_used": "Ultimo utilizzo", + "col_status": "Stato", + "active": "Attiva", + "revoked": "Revocata", + "revoke_title": "Revoca", + "no_app_passwords": "Nessuna password applicazione ancora.", + "client_sessions": "Sessioni client", + "client_sessions_desc": "Generate automaticamente quando connetti un client compatibile Nextcloud.", + "col_client": "Client", + "never": "Mai", + "just_now": "Proprio adesso", + "minutes_ago": "{{n}} min fa", + "hours_ago": "{{n}}h fa", + "days_ago": "{{n}} giorni fa", + "edit_profile": "Modifica profilo", + "edit_oidc_managed": "Per modificare le tue informazioni (nome, cognome, foto profilo, …), aggiornale presso il tuo identity provider. Le modifiche compariranno al prossimo accesso.", + "username_claim_hint": "Da 2 a 64 caratteri, lettere / cifre / punto / trattino / sottolineatura. Una volta scelto, il nome utente non può essere modificato (i client DAV/NextCloud dipendono da esso).", + "username_already_claimed": "Nome utente impostato e non modificabile (i client DAV/NextCloud dipendono da esso).", + "given_name": "Nome", + "family_name": "Cognome", + "notify_on_share": "Avvisami via email quando qualcuno condivide con me", + "notify_on_share_hint": "Se deselezionato, le condivisioni continueranno ad apparire nel tuo account — semplicemente non riceverai un'email a riguardo.", + "save_profile": "Salva modifiche", + "profile_saved": "Profilo aggiornato", + "profile_no_changes": "Nessuna modifica da salvare.", + "profile_save_failed": "Salvataggio non riuscito", + "username_taken_error": "Questo nome utente è già in uso.", + "username_immutable_error": "Il tuo nome utente è già impostato e non può essere cambiato qui. Contatta un amministratore se desideri rinominarlo.", + "change_password": "Cambia Password", + "current_password": "Password Attuale", + "new_password": "Nuova Password", + "min_8_chars": "Almeno 8 caratteri", + "confirm_password": "Conferma Nuova Password", + "update_password": "Aggiorna Password", + "updating": "Aggiornamento…", + "password_updated": "Password aggiornata con successo", + "passwords_no_match": "Le password non corrispondono", + "password_too_short": "La password deve avere almeno 8 caratteri", + "password_change_failed": "Impossibile cambiare la password", + "error_network": "Errore di rete: {{message}}", + "error_label_required": "Inserisci un'etichetta", + "error_create_pw": "Impossibile creare la password", + "confirm_revoke": "Revocare la password \"{{label}}\"? I client che la usano smetteranno di funzionare.", + "error_revoke": "Revoca fallita", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Caricamento in corso...", + "files": "file", + "complete": "{{count}} / {{total}} caricati" + }, + "storage_quota_exceeded": "Quota di archiviazione superata", + "sharedwithme": { + "pageTitle": "Condiviso con me", + "pageDescription": "File e cartelle che altri utenti hanno condiviso con te", + "emptyStateTitle": "Niente è ancora condiviso con te", + "emptyStateDesc": "Gli elementi condivisi con te da altri utenti appariranno qui", + "loadMore": "Carica altri", + "sharedBy": "Condiviso da", + "colName": "Nome", + "colType": "Tipo", + "colSharedBy": "Condiviso da", + "colDate": "Data condivisione", + "colPermissions": "Permessi" + }, + "groupby": { + "none": "Nessuno", + "title": "Raggruppa per", + "owner": "Proprietario", + "shareDate": "Data condivisione", + "type": "Tipo", + "type.folders": "Cartelle", + "accessedAt": "Data di accesso", + "modifiedAt": "Data di modifica", + "createdAt": "Data di creazione", + "size": "Dimensione", + "favoriteDate": "Data preferito", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nuovo" + }, + "dateBucket": { + "today": "Oggi", + "last7days": "Ultimi 7 giorni", + "last30days": "Ultimi 30 giorni" + }, + "groups": { + "title": "Gestisci gruppi", + "create_button": "Crea gruppo", + "create_dialog_title": "Nuovo gruppo", + "edit_dialog_title": "Rinomina gruppo", + "name_label": "Nome", + "name_placeholder": "ingegneria", + "description_label": "Descrizione (opzionale)", + "members_section": "Membri", + "add_member_placeholder": "Aggiungi un utente o un gruppo…", + "no_members": "Nessun membro al momento.", + "remove_member": "Rimuovi", + "delete_group": "Elimina gruppo", + "delete_confirm": "Eliminare il gruppo \"{name}\"? Le autorizzazioni che fanno riferimento a questo gruppo saranno revocate.", + "empty_state": "Nessun gruppo al momento.", + "load_more": "Carica altro", + "back_to_list": "Indietro", + "loading": "Caricamento…", + "virtual_badge": "Sistema", + "member_count_zero": "Nessun membro", + "member_count_one": "1 membro", + "member_count_other": "{count} membri", + "delete_confirm_label": "Digita il nome del gruppo per confermare:", + "delete_confirm_mismatch": "Digita esattamente il nome del gruppo per confermare.", + "virtual_internal_name": "Interno", + "members_loading": "Caricamento membri…", + "members_empty": "Nessun membro", + "virtual_internal_explanation": "Ogni utente interno su questo server" + }, + "myshares": { + "copyLink": "Copia link", + "deleteLink": "Elimina link", + "notifyByEmail": "Notifica via email", + "notifyFailed": "Impossibile inviare la notifica.", + "notifyGroupMembers": "Notifica i membri del gruppo", + "notifyRateLimited": "Troppe notifiche per questo destinatario — riprova più tardi.", + "removeAccess": "Rimuovi accesso", + "resendInvitation": "Reinvia email di invito" + }, + "sort": { + "asc": "crescente", + "desc": "decrescente" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/ja.json b/frontend/static/locales/ja.json new file mode 100644 index 00000000..66edf237 --- /dev/null +++ b/frontend/static/locales/ja.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "このサインインリンクは無効になりました", + "expired_body": "リンクは期限切れか、すでに使用された可能性があります。新しいリンクをお送りできます — 数秒以内にメールが届きます。", + "resend_to": "{{email}} に新しいリンクを送信", + "generic_unavailable": "このサインインリンクは無効になりました。すでに使用されたか、期限が切れた可能性があります。ログインページから新しいリンクをリクエストしてください。", + "service_unavailable": "マジックリンクサインインは、このサーバーで有効になっていません。", + "internal_error": "サインイン中にエラーが発生しました。もう一度お試しください。", + "resend_failure": "リンクの送信中にエラーが発生しました。もう一度お試しください。", + "cross_browser_title": "このデバイスでサインインを続けますか?", + "cross_browser_body": "このサインインリンクを、リクエストしたものとは別のブラウザーまたはデバイスで開きました。", + "cross_browser_warning": "このリンクをあなたがリクエストしたのであれば、続行しても安全です。そうでない場合は、このページを閉じてください — 「続行」をクリックすると、他の人があなたのアカウントにサインインしてしまいます。", + "cross_browser_continue": "続行してサインイン", + "resend_confirmation_title": "受信トレイをご確認ください", + "resend_confirmation_body": "サインインリンクがアクティブなアカウントのものであれば、新しいリンクが今送信されました。受信トレイをご確認ください。", + "return_link": "OxiCloud に戻る" + }, + "email": { + "invitation": { + "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", + "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n以下のリンクをクリックして開いてください:\n{{link}}\n\nリンクは一度のみ有効で、{{ttl_hours}} 時間で期限切れになります。\nこの招待に心当たりがない場合は、このメッセージを無視していただいて結構です。\n\n— OxiCloud" + }, + "login": { + "subject": "OxiCloud にサインイン", + "body": "こんにちは、\n\n以下のリンクから OxiCloud にサインインしてください。リンクは一度のみ有効で、{{ttl_minutes}} 分で期限切れになります。リクエストしたものと同じデバイスで開いてください。\n\n{{link}}\n\nこのサインインリンクをリクエストしていない場合は、このメッセージを無視していただいて結構です — それ以上の操作は必要ありません。\n\n— OxiCloud" + }, + "kind_file": "ファイル", + "kind_folder": "フォルダー", + "english_fallback_divider": "--- 以下は英語版 ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", + "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n新しい共有を確認するには OxiCloud を開いてください:\n{{login_link}}\n\n{{inviter}} さんから他にも新しい共有があるかもしれません — サインインしてあなたと共有されたすべての項目を確認してください。\n\n— OxiCloud\n\nOxiCloud のアカウントをお持ちで、共有通知の設定が有効になっているため、このメッセージが届いています。プロフィールでオフにできます(誰かが共有したときにメールで通知する)。" + } + } + }, + "app": { + "title": "OxiCloud", + "description": "ミニマリストクラウドストレージシステム" + }, + "nav": { + "files": "ファイル", + "shared": "共有", + "recent": "最近", + "favorites": "お気に入り", + "photos": "写真", + "music": "音楽", + "trash": "ゴミ箱", + "sharedwithme": "自分と共有" + }, + "photos": { + "empty_state": "写真はまだありません", + "empty_hint": "画像や動画をアップロードするとここに表示されます", + "items_selected": "件選択中", + "view_daily": "日", + "view_monthly": "月", + "view_yearly": "年" + }, + "music": { + "create_playlist": "プレイリストを作成", + "playlists": "プレイリスト", + "no_playlists": "プレイリストがありません", + "select_playlist": "プレイリストを選択", + "select_hint": "サイドバーからプレイリストを選択するか、新しいものを作成してください", + "add_tracks": "トラックを追加", + "no_tracks": "このプレイリストにトラックがありません", + "unknown_artist": "不明なアーティスト", + "unknown_title": "不明", + "confirm_delete": "このプレイリストを削除しますか?", + "playlist_name": "プレイリスト名", + "create": "作成", + "delete": "削除", + "share": "共有", + "edit": "編集", + "play_all": "すべて再生", + "shuffle": "シャッフル", + "repeat": "リピート", + "repeat_one": "1曲リピート", + "queue": "キュー", + "queue_empty": "キューが空です", + "not_playing": "再生していません", + "play": "再生", + "pause": "一時停止", + "previous": "前へ", + "next": "次へ", + "volume": "音量", + "mute": "ミュート", + "unmute": "ミュート解除", + "title": "タイトル", + "artist": "アーティスト", + "album": "アルバム", + "tracks": "曲", + "add": "追加", + "added": "追加しました!", + "added_to_playlist": "プレイリストに追加しました", + "add_to_playlist": "プレイリストに追加", + "load_error": "プレイリストの読み込みエラー", + "add_error": "曲を追加できませんでした", + "no_playlists_yet": "プレイリストがありません。最初に作成してください!", + "selected_files": "選択中:", + "error": "エラー", + "search_audio": "オーディオファイルを検索…", + "no_audio_files": "オーディオファイルが見つかりません", + "selected": "件選択中", + "loading": "読み込み中…", + "search_error": "オーディオファイルを読み込めませんでした", + "adding": "追加中…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "ファイルを検索...", + "new_folder": "新しいフォルダ", + "upload": "アップロード", + "upload_files": "ファイルをアップロード", + "upload_folder": "フォルダをアップロード", + "upload.uploading": "アップロード中...", + "upload.complete": "{count} / {total} アップロード完了", + "upload.files": "ファイル", + "rename": "名前を変更", + "move": "移動先...", + "move_to": "移動先", + "delete": "削除", + "download": "ダウンロード", + "view": "表示", + "cancel": "キャンセル", + "confirm": "確認", + "share": "共有", + "favorite": "お気に入りに追加", + "unfavorite": "お気に入りから削除", + "copy": "コピー", + "notify": "通知", + "send": "送信", + "clear_recent": "最近をクリア", + "logout": "ログアウト", + "create": "作成", + "search_btn": "検索", + "close": "閉じる", + "delete_permanently": "完全に削除", + "empty_trash": "ゴミ箱を空にする", + "open_parent_folder": "親フォルダへ移動", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "外観", + "about": "OxiCloudについて", + "about_description": "RustとClean Architectureで構築されたクラウドストレージプラットフォーム。高速・安全・プライベート。", + "admin_panel": "管理パネル", + "profile": "マイプロフィール", + "role_user": "ユーザー", + "theme": { + "light": "ライト", + "dark": "ダーク", + "auto": "システムに合わせる" + }, + "manage_groups": "グループを管理" + }, + "share": { + "dialogTitle": "共有リンク", + "linkLabel": "共有リンク:", + "copyLink": "コピー", + "permissions": "権限:", + "permissionRead": "読み取り", + "permissionWrite": "書き込み", + "permissionReshare": "再共有", + "password": "パスワード保護:", + "generatePassword": "生成", + "expiration": "有効期限:", + "update": "共有を更新", + "remove": "共有を削除", + "notifyTitle": "通知を送信", + "notifyEmailLabel": "メールアドレス:", + "notifyMessageLabel": "メッセージ(任意):", + "notifySend": "通知を送信", + "shareWithOthers": "他のユーザーと共有", + "sharePublicly": "公開共有", + "shareSettings": "共有設定", + "shareCopied": "リンクがクリップボードにコピーされました", + "shareCreated": "共有リンクが正常に作成されました", + "shareUpdated": "共有設定が正常に更新されました", + "shareRemoved": "共有が正常に削除されました", + "inviteByEmail": "メールで招待 — 招待を送信します", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "共有リンク", + "share_linkLabel": "共有リンク:", + "share_copyLink": "コピー", + "share_permissions": "権限:", + "share_permissionRead": "読み取り", + "share_permissionWrite": "書き込み", + "share_permissionReshare": "再共有", + "share_password": "パスワード保護:", + "share_generatePassword": "生成", + "share_expiration": "有効期限:", + "share_update": "共有を更新", + "share_remove": "共有を削除", + "share_notifyTitle": "通知を送信", + "share_notifyEmailLabel": "メールアドレス:", + "share_notifyMessageLabel": "メッセージ(任意):", + "share_notifySend": "通知を送信", + "shared": { + "backToFiles": "ファイルに戻る", + "pageTitle": "共有リソース", + "pageDescription": "共有ファイルとフォルダの管理", + "filterType": "種類:", + "filterAll": "すべて", + "filterFiles": "ファイル", + "filterFolders": "フォルダ", + "sortBy": "並び替え:", + "sortByName": "名前", + "sortByDate": "共有日", + "sortByExpiration": "有効期限", + "search": "検索", + "colName": "名前", + "colType": "種類", + "colDateShared": "共有日", + "colExpiration": "有効期限", + "colPermissions": "権限", + "colPassword": "パスワード", + "colActions": "操作", + "emptyStateTitle": "共有リソースはまだありません", + "emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", + "goToFiles": "ファイルへ移動", + "typeFile": "ファイル", + "typeFolder": "フォルダ", + "noExpiration": "期限なし", + "hasPassword": "あり", + "noPassword": "なし", + "editShare": "共有を編集", + "notifyShare": "通知する", + "copyLink": "リンクをコピー", + "removeShare": "共有を削除", + "linkCopied": "リンクがクリップボードにコピーされました!", + "linkCopyFailed": "リンクのコピーに失敗しました", + "itemUpdated": "共有設定が正常に更新されました", + "itemRemoved": "共有が正常に削除されました", + "invalidEmail": "有効なメールアドレスを入力してください", + "notificationSent": "通知が正常に送信されました", + "notificationFailed": "通知の送信に失敗しました", + "shared_backToFiles": "ファイルに戻る", + "shared_pageTitle": "共有リソース", + "shared_pageDescription": "共有ファイルとフォルダの管理", + "shared_filterType": "種類:", + "shared_filterAll": "すべて", + "shared_filterFiles": "ファイル", + "shared_filterFolders": "フォルダ", + "shared_sortBy": "並び替え:", + "shared_sortByName": "名前", + "shared_sortByDate": "共有日", + "shared_sortByExpiration": "有効期限", + "shared_search": "検索", + "shared_colName": "名前", + "shared_colType": "種類", + "shared_colDateShared": "共有日", + "shared_colExpiration": "有効期限", + "shared_colPermissions": "権限", + "shared_colPassword": "パスワード", + "shared_colActions": "操作", + "shared_emptyStateTitle": "共有リソースはまだありません", + "shared_emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", + "shared_goToFiles": "ファイルへ移動", + "shared_typeFile": "ファイル", + "shared_typeFolder": "フォルダ", + "shared_noExpiration": "期限なし", + "shared_hasPassword": "あり", + "shared_noPassword": "なし", + "shared_editShare": "共有を編集", + "shared_notifyShare": "通知する", + "shared_copyLink": "リンクをコピー", + "shared_removeShare": "共有を削除", + "shared_linkCopied": "リンクがクリップボードにコピーされました!", + "shared_linkCopyFailed": "リンクのコピーに失敗しました", + "shared_itemUpdated": "共有設定が正常に更新されました", + "shared_itemRemoved": "共有が正常に削除されました", + "shared_invalidEmail": "有効なメールアドレスを入力してください", + "shared_notificationSent": "通知が正常に送信されました", + "shared_notificationFailed": "通知の送信に失敗しました" + }, + "files": { + "name": "名前", + "type": "種類", + "size": "サイズ", + "modified": "更新日", + "no_files": "このフォルダにファイルはありません", + "empty_hint": "ファイルをアップロードするかフォルダを作成して始めましょう", + "loading": "ファイルを読み込み中…", + "view_grid": "グリッド表示", + "view_list": "リスト表示", + "file_types": { + "document": "ドキュメント", + "image": "画像", + "video": "動画", + "audio": "音声", + "pdf": "PDF", + "text": "テキスト", + "folder": "フォルダ", + "spreadsheet": "スプレッドシート", + "presentation": "プレゼンテーション", + "archive": "アーカイブ", + "installer": "インストーラー", + "code": "コード" + }, + "owner": "オーナー" + }, + "dialogs": { + "rename_folder": "フォルダ名を変更", + "rename_file": "ファイル名を変更", + "new_name": "新しい名前", + "new_folder_title": "新しいフォルダ", + "folder_name": "フォルダ名", + "folder_placeholder": "マイフォルダ", + "rename_title": "名前を変更", + "move_file": "ファイルを移動", + "move_folder": "フォルダを移動", + "select_destination": "移動先フォルダを選択:", + "select_this_folder": "このフォルダを選択", + "go_to_parent": ".. (親フォルダ)", + "no_subfolders": "サブフォルダなし", + "root": "ルート", + "delete_confirmation": "本当に削除しますか", + "and_contents": "およびすべての内容", + "no_undo": "この操作は元に戻せません", + "confirm_title": "操作の確認", + "confirm_delete": "ゴミ箱に移動", + "confirm_delete_file": "ファイル「{{name}}」をゴミ箱に移動しますか?", + "confirm_delete_folder": "フォルダ「{{name}}」とそのすべての内容をゴミ箱に移動しますか?", + "confirm_permanent_delete": "完全に削除", + "confirm_permanent_delete_msg": "このアイテムを完全に削除しますか?この操作は元に戻せません。", + "confirm_empty_trash": "ゴミ箱を空にする", + "confirm_delete_share": "共有リンクを削除", + "confirm_delete_share_msg": "この共有リンクを削除しますか?", + "share_file": "ファイルを共有", + "share_folder": "フォルダを共有", + "existing_shares": "既存の共有", + "share_options": "共有オプション", + "password": "パスワード", + "expiration": "有効期限", + "permissions": "権限", + "generated_link": "生成されたリンク", + "notify": "通知を送信", + "recipient": "宛先", + "message": "メッセージ", + "move_to_home": "ホームフォルダへ移動" + }, + "dropzone": { + "drag_files": "ファイルをここにドラッグするか、クリックして選択", + "drop_files": "ファイルをドロップしてアップロード" + }, + "permissions": { + "read": "読み取り", + "write": "書き込み", + "reshare": "再共有" + }, + "errors": { + "file_not_found": "ファイルが見つかりません", + "folder_not_found": "フォルダが見つかりません", + "delete_error": "削除エラー", + "upload_error": "ファイルのアップロードエラー", + "rename_error": "名前変更エラー", + "move_error": "移動エラー", + "empty_name": "名前を空にすることはできません", + "name_exists": "同じ名前のファイルまたはフォルダが既に存在します", + "generic_error": "エラーが発生しました", + "group_name_invalid": "グループ名はメールプレフィックス形式に一致している必要があります(文字、数字、ドット、ダッシュ、アンダースコア;1~64文字)。", + "group_cycle": "このメンバーはグループ間で循環参照を作成します。", + "group_depth_exceeded": "ネストの深さが許容されている最大値(8)を超えています。", + "group_virtual_immutable": "「Internal」グループはシステム管理であり、変更できません。", + "group_not_found": "グループが見つかりません。", + "group_name_taken": "この名前のグループはすでに存在します。" + }, + "breadcrumb": { + "home": "ホーム" + }, + "trash": { + "empty_trash": "ゴミ箱を空にする", + "empty_state": "ゴミ箱は空です", + "original_location": "元の場所", + "deleted_date": "削除日", + "remaining": "残り", + "actions": "操作", + "restore": "復元", + "delete_permanently": "完全に削除", + "empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。", + "groupby": { + "remaining_days": "残り日数", + "trashed_time": "削除日時" + } + }, + "daysRemaining": { + "expired": "期限切れ", + "today": "今日", + "tomorrow": "明日", + "inDays": "{{count}}日" + }, + "expiryChip": { + "never": "期限なし", + "expired": "期限切れ", + "today": "今日で期限切れ", + "tomorrow": "明日で期限切れ", + "inDays": "{{count}}日後に期限切れ", + "onDate": "{{date}}に期限切れ" + }, + "auth": { + "login_title": "サインイン", + "username": "ユーザー名", + "username_placeholder": "ユーザー名を入力", + "login_identifier": "ユーザー名またはメールアドレス", + "login_identifier_placeholder": "ユーザー名またはメールアドレスを入力", + "password": "パスワード", + "password_placeholder": "パスワードを入力", + "login_button": "サインイン", + "no_account": "アカウントをお持ちでないですか?", + "register": "登録", + "admin_setup": "初回ですか?", + "setup": "管理者をセットアップ", + "register_title": "アカウント作成", + "email": "メール", + "email_placeholder": "メールアドレスを入力", + "confirm_password": "パスワードの確認", + "confirm_password_placeholder": "パスワードを再入力", + "register_button": "アカウント作成", + "have_account": "既にアカウントをお持ちですか?", + "login": "サインイン", + "setup_title": "初期設定", + "setup_step1": "管理者", + "setup_step2": "システム", + "setup_step3": "完了", + "admin_username": "管理者ユーザー名", + "admin_email": "管理者メール", + "admin_password": "管理者パスワード", + "create_admin": "管理者を作成", + "back_to_login": "設定済みですか?", + "admin_success": "管理者アカウントが正常に作成されました!サインインできます。", + "account_success": "アカウントが正常に作成されました!サインインできます。", + "passwords_mismatch": "パスワードが一致しません", + "admin_create_error": "管理者アカウントの作成エラー", + "or": "または", + "sso_login": "SSOでサインイン", + "sso_login_provider": "{{provider}}でサインイン", + "magicLinkHint": "パスワードをお持ちでない方は、メールアドレスを入力するとワンタイムサインインリンクをお送りします。", + "magicLinkEmailLabel": "メールアドレス", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "サインインリンクを送信", + "magicLinkSent": "そのメールアドレスのアカウントが存在する場合、サインインリンクが送信されました。受信トレイをご確認ください。", + "magicLinkUnavailable": "このサーバーではメールでのサインインは利用できません。", + "magicLinkNetworkError": "サーバーに接続できませんでした: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "ストレージ", + "calculating": "計算中...", + "used": "{{percentage}}% 使用中 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "このファイル形式はプレビューできません。", + "download_file": "ファイルをダウンロード", + "zoom_in": "拡大", + "zoom_out": "縮小", + "zoom_reset": "ズームリセット" + }, + "language_selector": { + "title": "ようこそ!", + "subtitle": "続行するには言語を選択してください", + "continue": "続行", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ja": "日本語", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "お気に入りはまだありません", + "empty_hint": "ファイルやフォルダにスターを付けてお気に入りに追加", + "add": "お気に入りに追加", + "remove": "お気に入りから削除", + "added_title": "お気に入りに追加しました", + "added_msg": "お気に入りに追加しました", + "removed_title": "お気に入りから削除しました", + "removed_msg": "お気に入りから削除しました" + }, + "recent": { + "title": "最近", + "clear": "最近をクリア", + "accessed": "アクセス日", + "empty_state": "最近のファイルはありません", + "empty_hint": "開いたファイルがここに表示されます", + "loadMore": "さらに読み込む" + }, + "notifications": { + "file_renamed": "ファイル名を変更しました", + "file_renamed_to": "ファイル名を「{{name}}」に変更しました", + "folder_renamed": "フォルダ名を変更しました", + "folder_renamed_to": "フォルダ名を「{{name}}」に変更しました", + "file_uploaded": "ファイルをアップロードしました", + "file_deleted": "ファイルをゴミ箱に移動しました", + "folder_deleted": "フォルダをゴミ箱に移動しました", + "item_deleted_permanently": "アイテムを完全に削除しました", + "trash_emptied": "ゴミ箱を正常に空にしました", + "title": "通知", + "empty": "通知はありません", + "link_created": "リンクを作成しました", + "share_success": "共有リンクを正常に作成しました", + "upload_files_section_title": "ここではアップロードできません", + "upload_files_section_body": "ファイルをアップロードするには「ファイル」セクションに移動してください" + }, + "batch": { + "one_selected": "1件選択中", + "n_selected": "{{count}}件選択中", + "confirm_delete": "{{count}}件のアイテムをゴミ箱に移動しますか?", + "move_title": "{{count}}件のアイテムを移動", + "add_favorites": "お気に入りに追加", + "move_copy": "移動またはコピー" + }, + "admin": { + "page_title": "管理パネル", + "back_to_app": "OxiCloudに戻る", + "loading": "読み込み中…", + "access_denied": "アクセス拒否", + "access_denied_desc": "管理者権限が必要です。", + "sign_in": "サインイン", + "tab_dashboard": "ダッシュボード", + "tab_users": "ユーザー", + "tab_oidc": "SSO / OIDC", + "total_users": "総ユーザー数", + "active_users": "アクティブ", + "admins": "管理者", + "version": "バージョン", + "storage_overview": "ストレージ概要", + "used": "使用済み", + "total_quota": "合計クォータ", + "usage_pct": "使用率", + "users_over_80": "クォータ80%超", + "users_over_quota": "クォータ超過", + "system": "システム", + "auth_label": "認証", + "oidc_label": "OIDC", + "quotas_label": "クォータ", + "enabled": "有効", + "disabled": "無効", + "active": "アクティブ", + "off": "オフ", + "allow_registration": "公開セルフ登録を許可", + "registration_warning": "公開登録は無効です。管理者のみがユーザーを作成できます。", + "user_management": "ユーザー管理", + "create_user": "ユーザー作成", + "col_user": "ユーザー", + "col_role": "役割", + "col_auth": "認証", + "col_status": "ステータス", + "col_storage": "ストレージ", + "col_last_login": "最終ログイン", + "col_actions": "操作", + "loading_users": "ユーザーを読み込み中…", + "failed_load_users": "読み込み失敗", + "no_users_found": "ユーザーなし", + "showing_users": "{{from}}-{{to}} / {{total}} を表示", + "prev": "前へ", + "next": "次へ", + "inactive": "非アクティブ", + "you_badge": "(あなた)", + "local": "ローカル", + "never": "未ログイン", + "just_now": "たった今", + "minutes_ago": "{{n}}分前", + "hours_ago": "{{n}}時間前", + "days_ago": "{{n}}日前", + "edit_quota_title": "クォータを編集", + "reset_password_title": "パスワードリセット", + "toggle_role_title": "役割を切替", + "deactivate_title": "無効化", + "activate_title": "有効化", + "delete_title": "削除", + "sso_title": "シングルサインオン (OIDC / SSO)", + "enable_sso": "SSO認証を有効化", + "provider_name": "プロバイダー名", + "issuer_url": "発行者URL", + "issuer_url_hint": "OpenID Connect発行者URL", + "auto_discover": "自動検出", + "discovering": "検出中…", + "client_id": "クライアントID", + "client_secret": "クライアントシークレット", + "client_secret_placeholder": "現在の値を維持するには空に", + "secret_configured": "クライアントシークレット設定済み", + "callback_url": "コールバックURL", + "callback_url_hint": "(IdPに登録)", + "advanced_settings": "詳細設定", + "scopes": "スコープ", + "auto_provision": "初回ログイン時に自動プロビジョニング", + "admin_groups": "管理者グループ", + "admin_groups_hint": "カンマ区切りのOIDCグループ名", + "disable_password": "パスワードログイン無効化(OIDCのみ)", + "password_warning": "すべてのパスワードログインが無効に!", + "test_btn": "テスト", + "save_btn": "保存", + "saving": "保存中…", + "settings_saved": "設定が保存されました — OIDC: {{status}}", + "quota_modal_title": "ストレージクォータ更新", + "quota_user_label": "ユーザー:", + "new_quota": "新しいクォータ", + "quota_unlimited_hint": "0で無制限", + "cancel": "キャンセル", + "create_user_title": "新規ユーザー作成", + "username_label": "ユーザー名", + "username_placeholder": "taro", + "username_hint": "3〜32文字", + "password_label": "パスワード", + "password_placeholder": "8文字以上", + "email_label": "メール", + "email_optional": "(任意)", + "email_placeholder": "user@example.com(空なら自動生成)", + "role_label": "役割", + "role_user": "ユーザー", + "role_admin": "管理者", + "quota_label": "クォータ", + "creating": "作成中…", + "reset_pw_title": "パスワードリセット", + "new_password_label": "新しいパスワード", + "resetting": "リセット中…", + "reset_btn": "リセット", + "confirm_role_change": "役割を{{role}}に変更?", + "confirm_deactivate": "このユーザーを無効化しますか?", + "confirm_activate": "このユーザーを有効化しますか?", + "confirm_delete_user": "ユーザー「{{name}}」を削除?取り消せません!", + "confirm_action": "操作の確認", + "confirm_yes": "確認", + "confirm_no": "キャンセル", + "error_username_short": "ユーザー名は3文字以上", + "error_password_short": "パスワードは8文字以上", + "error_generic": "失敗", + "error_network": "ネットワークエラー: {{message}}", + "error_create_user": "ユーザー作成失敗", + "tab_storage": "ストレージ", + "storage_title": "ストレージ設定", + "storage_current_backend": "現在のバックエンド", + "storage_total_blobs": "総ブロブ数", + "storage_total_size": "合計サイズ", + "storage_dedup_ratio": "重複排除率", + "storage_backend": "バックエンド", + "storage_local": "ローカル", + "storage_s3": "S3互換", + "storage_provider_preset": "プロバイダープリセット", + "storage_preset_custom": "カスタム", + "storage_endpoint_url": "エンドポイントURL", + "storage_endpoint_hint": "AWS S3の場合は空欄のまま", + "storage_bucket": "バケット", + "storage_region": "リージョン", + "storage_access_key": "アクセスキー", + "storage_secret_key": "シークレットキー", + "storage_secret_configured": "キーが設定済み", + "storage_key_placeholder": "新しいキーを入力", + "storage_path_style": "パススタイルを強制", + "storage_path_style_hint": "MinIOおよび一部のS3互換サービスに必要", + "storage_test_connection": "接続テスト", + "storage_test_success": "接続成功", + "storage_test_failure": "接続失敗", + "storage_save": "設定を保存", + "storage_saved": "設定を保存しました", + "storage_migration": "データ移行", + "storage_migration_coming_soon": "移行ツールは近日公開予定", + "migration_status_label": "移行状況", + "migration_start": "移行を開始", + "migration_pause": "一時停止", + "migration_resume": "再開", + "migration_verify": "検証", + "migration_complete": "完了", + "migration_started": "移行を開始しました", + "migration_paused_msg": "移行を一時停止しました", + "migration_resumed_msg": "移行を再開しました", + "migration_completed_msg": "移行が正常に完了しました", + "migration_verifying": "検証中...", + "migration_verify_passed": "検証に合格", + "migration_verify_failed": "検証に失敗", + "migration_failed_blobs": "失敗したブロブ", + "testing": "テスト中...", + "smtp_disabled": "無効 (ホスト未設定)", + "smtp_enabled": "有効", + "smtp_enabled_label": "ステータス", + "smtp_intro": "SMTP は環境変数 (OXICLOUD_SMTP_*) でのみ設定します。以下の値は稼働中のサーバーから読み取られます — 変更するには環境を編集して OxiCloud を再起動してください。", + "smtp_not_configured": "このサーバーでは SMTP が設定されていません。", + "smtp_send_failed": "送信に失敗しました。", + "smtp_send_test": "テストメールを送信", + "smtp_sending": "送信中…", + "smtp_sent": "テストメールを送信しました。", + "smtp_server_code": "サーバーの応答", + "smtp_test_intro": "あらかじめ定義された診断メッセージを下記の宛先に送信し、SMTP サーバーの応答を表示します。これを使ってリレーのログと突き合わせて確認できます。", + "smtp_test_missing_to": "宛先アドレスを入力してください。", + "smtp_test_title": "テストメールを送信", + "smtp_test_to": "宛先アドレス", + "smtp_title": "送信メール (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "プロフィール", + "back_to_app": "OxiCloudに戻る", + "loading": "読み込み中…", + "not_authenticated": "未認証", + "not_authenticated_desc": "プロフィールを表示するにはサインインしてください。", + "sign_in": "サインイン", + "role_admin": "管理者", + "role_user": "ユーザー", + "account_details": "アカウント詳細", + "username": "ユーザー名", + "email": "メール", + "role": "役割", + "last_login": "最終ログイン", + "storage": "ストレージ", + "used": "使用済み", + "quota": "クォータ", + "usage": "使用率", + "unlimited": "無制限", + "app_passwords": "アプリパスワード", + "app_pw_desc": "WebDAV、CalDAV、CardDAVクライアント用のパスワードを生成します。各パスワードは一度だけ表示されます。", + "app_pw_label_placeholder": "ラベル(例:Thunderbird、macOS)", + "generate": "生成", + "generating": "生成中…", + "new_password_for": "新しいパスワード:", + "copy_warning": "このパスワードを今コピーしてください。再度表示できません。", + "copy_to_clipboard": "クリップボードにコピー", + "col_label": "ラベル", + "col_created": "作成日", + "col_last_used": "最終使用", + "col_status": "ステータス", + "active": "アクティブ", + "revoked": "失効済み", + "revoke_title": "失効", + "no_app_passwords": "アプリパスワードはまだありません。", + "client_sessions": "クライアントセッション", + "client_sessions_desc": "Nextcloud互換クライアント接続時に自動生成されます。", + "col_client": "クライアント", + "never": "未ログイン", + "just_now": "たった今", + "minutes_ago": "{{n}}分前", + "hours_ago": "{{n}}時間前", + "days_ago": "{{n}}日前", + "edit_profile": "プロフィールを編集", + "edit_oidc_managed": "情報(姓、名、プロフィール写真など)を変更するには、IDプロバイダーで更新してください。次回サインイン時に反映されます。", + "username_claim_hint": "2〜64文字、英数字 / ドット / ハイフン / アンダースコア。一度選択すると、ユーザー名は変更できません(DAV/NextCloudクライアントが依存します)。", + "username_already_claimed": "ユーザー名は設定済みで変更できません(DAV/NextCloudクライアントが依存します)。", + "given_name": "名", + "family_name": "姓", + "notify_on_share": "誰かが共有したときにメールで通知する", + "notify_on_share_hint": "チェックを外しても、共有はアカウントに表示されますが、メールでの通知は届きません。", + "save_profile": "変更を保存", + "profile_saved": "プロフィールを更新しました", + "profile_no_changes": "保存する変更はありません。", + "profile_save_failed": "保存に失敗しました", + "username_taken_error": "このユーザー名はすでに使用されています。", + "username_immutable_error": "ユーザー名はすでに設定されており、ここでは変更できません。名前を変更したい場合は管理者にお問い合わせください。", + "change_password": "パスワード変更", + "current_password": "現在のパスワード", + "new_password": "新しいパスワード", + "min_8_chars": "8文字以上", + "confirm_password": "新しいパスワードの確認", + "update_password": "パスワードを更新", + "updating": "更新中…", + "password_updated": "パスワードが正常に更新されました", + "passwords_no_match": "パスワードが一致しません", + "password_too_short": "パスワードは8文字以上必要です", + "password_change_failed": "パスワードの変更に失敗しました", + "error_network": "ネットワークエラー: {{message}}", + "error_label_required": "ラベルを入力してください", + "error_create_pw": "アプリパスワードの作成に失敗しました", + "confirm_revoke": "アプリパスワード「{{label}}」を失効させますか?使用中のクライアントは動作しなくなります。", + "error_revoke": "失効に失敗しました", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "アップロード中...", + "files": "ファイル", + "complete": "{{count}} / {{total}} アップロード済み" + }, + "storage_quota_exceeded": "ストレージ容量を超過しました", + "sharedwithme": { + "pageTitle": "自分と共有", + "pageDescription": "他のユーザーがあなたと共有したファイルとフォルダー", + "emptyStateTitle": "まだ何も共有されていません", + "emptyStateDesc": "他のユーザーがあなたと共有したアイテムがここに表示されます", + "loadMore": "さらに読み込む", + "sharedBy": "共有者", + "colName": "名前", + "colType": "タイプ", + "colSharedBy": "共有者", + "colDate": "共有日", + "colPermissions": "権限" + }, + "groupby": { + "none": "なし", + "title": "グループ化", + "owner": "オーナー", + "shareDate": "共有日", + "type": "種類", + "type.folders": "フォルダー", + "accessedAt": "アクセス日", + "modifiedAt": "更新日", + "createdAt": "作成日", + "size": "サイズ", + "favoriteDate": "お気に入り登録日", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "新規" + }, + "dateBucket": { + "today": "今日", + "last7days": "過去7日間", + "last30days": "過去30日間" + }, + "groups": { + "title": "グループを管理", + "create_button": "グループを作成", + "create_dialog_title": "新規グループ", + "edit_dialog_title": "グループ名を変更", + "name_label": "名前", + "name_placeholder": "engineering", + "description_label": "説明(任意)", + "members_section": "メンバー", + "add_member_placeholder": "ユーザーまたはグループを追加…", + "no_members": "メンバーはまだいません。", + "remove_member": "削除", + "delete_group": "グループを削除", + "delete_confirm": "グループ「{name}」を削除しますか?このグループを参照しているすべての権限が取り消されます。", + "empty_state": "グループはまだありません。", + "load_more": "もっと読み込む", + "back_to_list": "戻る", + "loading": "読み込み中…", + "virtual_badge": "システム", + "member_count_zero": "メンバーなし", + "member_count_one": "1 メンバー", + "member_count_other": "{count} メンバー", + "delete_confirm_label": "確認のためにグループ名を入力してください:", + "delete_confirm_mismatch": "確認のためにグループ名を正確に入力してください。", + "virtual_internal_name": "内部", + "members_loading": "メンバーを読み込み中…", + "members_empty": "メンバーなし", + "virtual_internal_explanation": "このサーバー上のすべての内部ユーザー" + }, + "myshares": { + "copyLink": "リンクをコピー", + "deleteLink": "リンクを削除", + "notifyByEmail": "メールで通知", + "notifyFailed": "通知を送信できませんでした。", + "notifyGroupMembers": "グループメンバーに通知", + "notifyRateLimited": "この受信者への通知が多すぎます — しばらくしてから再試行してください。", + "removeAccess": "アクセスを削除", + "resendInvitation": "招待メールを再送信" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/ko.json b/frontend/static/locales/ko.json new file mode 100644 index 00000000..b8fa12c0 --- /dev/null +++ b/frontend/static/locales/ko.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "이 로그인 링크는 더 이상 유효하지 않습니다", + "expired_body": "링크가 만료되었거나 이미 사용되었을 수 있습니다. 새 링크를 보내드릴 수 있습니다 — 몇 초 안에 받은편지함에 도착합니다.", + "resend_to": "{{email}}로 새 링크 보내기", + "generic_unavailable": "이 로그인 링크는 더 이상 유효하지 않습니다. 이미 사용되었거나 만료되었을 수 있습니다. 로그인 페이지에서 새 링크를 요청하세요.", + "service_unavailable": "이 서버에서는 매직 링크 로그인이 활성화되어 있지 않습니다.", + "internal_error": "로그인 중 오류가 발생했습니다. 다시 시도해 주세요.", + "resend_failure": "링크 전송 중 오류가 발생했습니다. 다시 시도해 주세요.", + "cross_browser_title": "이 기기에서 로그인을 계속하시겠습니까?", + "cross_browser_body": "요청한 곳과 다른 브라우저나 기기에서 이 로그인 링크를 열었습니다.", + "cross_browser_warning": "이 링크를 본인이 요청했다면 안전하게 계속할 수 있습니다. 그렇지 않다면 이 페이지를 닫으세요 — 계속을 클릭하면 다른 사람이 당신의 계정에 로그인하게 됩니다.", + "cross_browser_continue": "계속하고 로그인", + "resend_confirmation_title": "받은편지함을 확인하세요", + "resend_confirmation_body": "로그인 링크가 활성 계정의 것이었다면 새 링크가 방금 전송되었습니다. 받은편지함을 확인하세요.", + "return_link": "OxiCloud로 돌아가기" + }, + "email": { + "invitation": { + "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", + "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n아래 링크를 클릭하여 여세요:\n{{link}}\n\n링크는 한 번만 사용 가능하며 {{ttl_hours}}시간 후에 만료됩니다.\n이 초대를 예상하지 못했다면 이 메시지를 무시하셔도 됩니다.\n\n— OxiCloud" + }, + "login": { + "subject": "OxiCloud 로그인", + "body": "안녕하세요,\n\n아래 링크를 사용하여 OxiCloud에 로그인하세요. 링크는 한 번만 사용 가능하며 {{ttl_minutes}}분 후에 만료됩니다. 요청한 것과 동일한 기기에서 여세요.\n\n{{link}}\n\n이 로그인 링크를 요청하지 않으셨다면 이 메시지를 무시하셔도 됩니다 — 추가 조치가 필요하지 않습니다.\n\n— OxiCloud" + }, + "kind_file": "파일", + "kind_folder": "폴더", + "english_fallback_divider": "--- 영어 버전은 아래 ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", + "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n새 공유 항목을 확인하려면 OxiCloud를 여세요:\n{{login_link}}\n\n{{inviter}}님이 추가로 공유한 항목이 있을 수 있습니다 — 로그인하여 공유받은 모든 항목을 확인하세요.\n\n— OxiCloud\n\nOxiCloud 계정이 있고 공유 알림 기본 설정이 켜져 있어 이 메시지를 받았습니다. 프로필에서 끌 수 있습니다(다른 사람이 나에게 공유할 때 이메일로 알림 받기)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "미니멀리스트 클라우드 스토리지 시스템" + }, + "nav": { + "files": "파일", + "shared": "공유", + "recent": "최근", + "favorites": "즐겨찾기", + "photos": "사진", + "music": "음악", + "trash": "휴지통", + "sharedwithme": "나와 공유됨" + }, + "photos": { + "empty_state": "아직 사진이 없습니다", + "empty_hint": "이미지나 동영상을 업로드하면 여기에 표시됩니다", + "items_selected": "개 선택됨", + "view_daily": "일", + "view_monthly": "월", + "view_yearly": "년" + }, + "music": { + "create_playlist": "재생목록 만들기", + "playlists": "재생목록", + "no_playlists": "아직 재생목록이 없습니다", + "select_playlist": "재생목록 선택", + "select_hint": "사이드바에서 재생목록을 선택하거나 새로 만드세요", + "add_tracks": "트랙 추가", + "no_tracks": "이 재생목록에 트랙이 없습니다", + "unknown_artist": "알 수 없는 아티스트", + "unknown_title": "알 수 없음", + "confirm_delete": "이 재생목록을 삭제하시겠습니까?", + "playlist_name": "재생목록 이름", + "create": "만들기", + "delete": "삭제", + "share": "공유", + "edit": "편집", + "play_all": "전체 재생", + "shuffle": "셔플", + "repeat": "반복", + "repeat_one": "한 곡 반복", + "queue": "대기열", + "queue_empty": "대기열이 비어 있습니다", + "not_playing": "재생 중이 아닙니다", + "play": "재생", + "pause": "일시정지", + "previous": "이전", + "next": "다음", + "volume": "볼륨", + "mute": "음소거", + "unmute": "음소거 해제", + "title": "제목", + "artist": "아티스트", + "album": "앨범", + "tracks": "개 트랙", + "add": "추가", + "added": "추가됨!", + "added_to_playlist": "플레이리스트에 추가됨", + "add_to_playlist": "플레이리스트에 추가", + "load_error": "플레이리스트 로드 오류", + "add_error": "트랙을 플레이리스트에 추가할 수 없습니다", + "no_playlists_yet": "플레이리스트가 없습니다. 먼저 하나를 만드세요!", + "selected_files": "선택됨:", + "error": "오류", + "search_audio": "오디오 파일 검색…", + "no_audio_files": "오디오 파일을 찾을 수 없습니다", + "selected": "선택됨", + "loading": "로딩 중…", + "search_error": "오디오 파일을 불러올 수 없습니다", + "adding": "추가 중…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "파일 검색...", + "new_folder": "새 폴더", + "upload": "업로드", + "upload_files": "파일 업로드", + "upload_folder": "폴더 업로드", + "upload.uploading": "업로드 중...", + "upload.complete": "{count} / {total} 업로드 완료", + "upload.files": "파일", + "rename": "이름 변경", + "move": "이동...", + "move_to": "이동 대상", + "delete": "삭제", + "download": "다운로드", + "view": "보기", + "cancel": "취소", + "confirm": "확인", + "share": "공유", + "favorite": "즐겨찾기 추가", + "unfavorite": "즐겨찾기 해제", + "copy": "복사", + "notify": "알림", + "send": "보내기", + "clear_recent": "최근 항목 지우기", + "logout": "로그아웃", + "create": "만들기", + "search_btn": "검색", + "close": "닫기", + "delete_permanently": "영구 삭제", + "empty_trash": "휴지통 비우기", + "open_parent_folder": "상위 폴더로 이동", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "외관", + "about": "OxiCloud 정보", + "about_description": "Rust와 Clean Architecture로 구축된 클라우드 스토리지 플랫폼. 빠르고, 안전하고, 프라이빗합니다.", + "admin_panel": "관리자 패널", + "profile": "내 프로필", + "role_user": "사용자", + "theme": { + "light": "라이트", + "dark": "다크", + "auto": "시스템과 동일" + }, + "manage_groups": "그룹 관리" + }, + "share": { + "dialogTitle": "공유 링크", + "linkLabel": "공유 링크:", + "copyLink": "복사", + "permissions": "권한:", + "permissionRead": "읽기", + "permissionWrite": "쓰기", + "permissionReshare": "재공유", + "password": "비밀번호 보호:", + "generatePassword": "생성", + "expiration": "만료일:", + "update": "공유 업데이트", + "remove": "공유 삭제", + "notifyTitle": "알림 보내기", + "notifyEmailLabel": "이메일 주소:", + "notifyMessageLabel": "메시지 (선택사항):", + "notifySend": "알림 보내기", + "shareWithOthers": "다른 사용자와 공유", + "sharePublicly": "공개 공유", + "shareSettings": "공유 설정", + "shareCopied": "링크가 클립보드에 복사되었습니다", + "shareCreated": "공유 링크가 성공적으로 생성되었습니다", + "shareUpdated": "공유 설정이 성공적으로 업데이트되었습니다", + "shareRemoved": "공유가 성공적으로 삭제되었습니다", + "inviteByEmail": "이메일로 초대 — 초대장이 전송됩니다", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "공유 링크", + "share_linkLabel": "공유 링크:", + "share_copyLink": "복사", + "share_permissions": "권한:", + "share_permissionRead": "읽기", + "share_permissionWrite": "쓰기", + "share_permissionReshare": "재공유", + "share_password": "비밀번호 보호:", + "share_generatePassword": "생성", + "share_expiration": "만료일:", + "share_update": "공유 업데이트", + "share_remove": "공유 삭제", + "share_notifyTitle": "알림 보내기", + "share_notifyEmailLabel": "이메일 주소:", + "share_notifyMessageLabel": "메시지 (선택사항):", + "share_notifySend": "알림 보내기", + "shared": { + "backToFiles": "파일로 돌아가기", + "pageTitle": "공유 리소스", + "pageDescription": "공유 파일 및 폴더 관리", + "filterType": "유형:", + "filterAll": "전체", + "filterFiles": "파일", + "filterFolders": "폴더", + "sortBy": "정렬:", + "sortByName": "이름", + "sortByDate": "공유일", + "sortByExpiration": "만료일", + "search": "검색", + "colName": "이름", + "colType": "유형", + "colDateShared": "공유일", + "colExpiration": "만료일", + "colPermissions": "권한", + "colPassword": "비밀번호", + "colActions": "작업", + "emptyStateTitle": "아직 공유된 리소스가 없습니다", + "emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", + "goToFiles": "파일로 이동", + "typeFile": "파일", + "typeFolder": "폴더", + "noExpiration": "만료 없음", + "hasPassword": "있음", + "noPassword": "없음", + "editShare": "공유 편집", + "notifyShare": "알림", + "copyLink": "링크 복사", + "removeShare": "공유 삭제", + "linkCopied": "링크가 클립보드에 복사되었습니다!", + "linkCopyFailed": "링크 복사에 실패했습니다", + "itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", + "itemRemoved": "공유가 성공적으로 삭제되었습니다", + "invalidEmail": "유효한 이메일 주소를 입력하세요", + "notificationSent": "알림이 성공적으로 전송되었습니다", + "notificationFailed": "알림 전송에 실패했습니다", + "shared_backToFiles": "파일로 돌아가기", + "shared_pageTitle": "공유 리소스", + "shared_pageDescription": "공유 파일 및 폴더 관리", + "shared_filterType": "유형:", + "shared_filterAll": "전체", + "shared_filterFiles": "파일", + "shared_filterFolders": "폴더", + "shared_sortBy": "정렬:", + "shared_sortByName": "이름", + "shared_sortByDate": "공유일", + "shared_sortByExpiration": "만료일", + "shared_search": "검색", + "shared_colName": "이름", + "shared_colType": "유형", + "shared_colDateShared": "공유일", + "shared_colExpiration": "만료일", + "shared_colPermissions": "권한", + "shared_colPassword": "비밀번호", + "shared_colActions": "작업", + "shared_emptyStateTitle": "아직 공유된 리소스가 없습니다", + "shared_emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", + "shared_goToFiles": "파일로 이동", + "shared_typeFile": "파일", + "shared_typeFolder": "폴더", + "shared_noExpiration": "만료 없음", + "shared_hasPassword": "있음", + "shared_noPassword": "없음", + "shared_editShare": "공유 편집", + "shared_notifyShare": "알림", + "shared_copyLink": "링크 복사", + "shared_removeShare": "공유 삭제", + "shared_linkCopied": "링크가 클립보드에 복사되었습니다!", + "shared_linkCopyFailed": "링크 복사에 실패했습니다", + "shared_itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", + "shared_itemRemoved": "공유가 성공적으로 삭제되었습니다", + "shared_invalidEmail": "유효한 이메일 주소를 입력하세요", + "shared_notificationSent": "알림이 성공적으로 전송되었습니다", + "shared_notificationFailed": "알림 전송에 실패했습니다" + }, + "files": { + "name": "이름", + "type": "유형", + "size": "크기", + "modified": "수정일", + "no_files": "이 폴더에 파일이 없습니다", + "empty_hint": "파일을 업로드하거나 폴더를 만들어 시작하세요", + "loading": "파일 로딩 중…", + "view_grid": "그리드 보기", + "view_list": "목록 보기", + "file_types": { + "document": "문서", + "image": "이미지", + "video": "동영상", + "audio": "오디오", + "pdf": "PDF", + "text": "텍스트", + "folder": "폴더", + "spreadsheet": "스프레드시트", + "presentation": "프레젠테이션", + "archive": "아카이브", + "installer": "설치 프로그램", + "code": "코드" + }, + "owner": "소유자" + }, + "dialogs": { + "rename_folder": "폴더 이름 변경", + "rename_file": "파일 이름 변경", + "new_name": "새 이름", + "new_folder_title": "새 폴더", + "folder_name": "폴더 이름", + "folder_placeholder": "내 폴더", + "rename_title": "이름 변경", + "move_file": "파일 이동", + "move_folder": "폴더 이동", + "select_destination": "대상 폴더를 선택하세요:", + "select_this_folder": "이 폴더 선택", + "go_to_parent": ".. (상위 폴더)", + "no_subfolders": "하위 폴더 없음", + "root": "루트", + "delete_confirmation": "정말 삭제하시겠습니까", + "and_contents": "및 모든 내용", + "no_undo": "이 작업은 되돌릴 수 없습니다", + "confirm_title": "작업 확인", + "confirm_delete": "휴지통으로 이동", + "confirm_delete_file": "파일 «{{name}}»을(를) 휴지통으로 이동하시겠습니까?", + "confirm_delete_folder": "폴더 «{{name}}» 및 모든 내용을 휴지통으로 이동하시겠습니까?", + "confirm_permanent_delete": "영구 삭제", + "confirm_permanent_delete_msg": "이 항목을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "confirm_empty_trash": "휴지통 비우기", + "confirm_delete_share": "공유 링크 삭제", + "confirm_delete_share_msg": "이 공유 링크를 삭제하시겠습니까?", + "share_file": "파일 공유", + "share_folder": "폴��� 공유", + "existing_shares": "기존 공유", + "share_options": "공유 옵션", + "password": "비밀번호", + "expiration": "만료일", + "permissions": "권한", + "generated_link": "생성된 링크", + "notify": "알림 보내기", + "recipient": "수신자", + "message": "메시지", + "move_to_home": "홈 폴더로 이동" + }, + "dropzone": { + "drag_files": "여기에 파일을 드래그하거나 클릭하여 선택하세요", + "drop_files": "파일을 놓아 업로드하세요" + }, + "permissions": { + "read": "읽기", + "write": "쓰기", + "reshare": "재공유" + }, + "errors": { + "file_not_found": "파일을 찾을 수 없습니다", + "folder_not_found": "폴더를 찾을 수 없습니다", + "delete_error": "삭제 오류", + "upload_error": "파일 업로드 오류", + "rename_error": "이름 변경 오류", + "move_error": "이동 오류", + "empty_name": "이름은 비워둘 수 없습니다", + "name_exists": "같은 이름의 파일 또는 폴더가 이미 존재합니다", + "generic_error": "오류가 발생했습니다", + "group_name_invalid": "그룹 이름은 이메일 접두사 형식과 일치해야 합니다(문자, 숫자, 점, 대시, 밑줄; 1–64자).", + "group_cycle": "이 구성원은 그룹 간 순환 참조를 만들 것입니다.", + "group_depth_exceeded": "중첩 깊이가 허용 최대값(8)을 초과합니다.", + "group_virtual_immutable": "«Internal» 그룹은 시스템이 관리하며 수정할 수 없습니다.", + "group_not_found": "그룹을 찾을 수 없습니다.", + "group_name_taken": "이 이름의 그룹이 이미 존재합니다." + }, + "breadcrumb": { + "home": "홈" + }, + "trash": { + "empty_trash": "휴지통 비우기", + "empty_state": "휴지통이 비어 있습니다", + "original_location": "원래 위치", + "deleted_date": "삭제일", + "remaining": "남음", + "actions": "작업", + "restore": "복원", + "delete_permanently": "영구 삭제", + "empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다.", + "groupby": { + "remaining_days": "남은 일수", + "trashed_time": "삭제 시간" + } + }, + "daysRemaining": { + "expired": "만료됨", + "today": "오늘", + "tomorrow": "내일", + "inDays": "{{count}}일" + }, + "expiryChip": { + "never": "만료되지 않음", + "expired": "만료됨", + "today": "오늘 만료", + "tomorrow": "내일 만료", + "inDays": "{{count}}일 후 만료", + "onDate": "{{date}}에 만료" + }, + "auth": { + "login_title": "로그인", + "username": "사용자 이름", + "username_placeholder": "사용자 이름을 입력하세요", + "login_identifier": "사용자 이름 또는 이메일", + "login_identifier_placeholder": "사용자 이름 또는 이메일을 입력하세요", + "password": "비밀번호", + "password_placeholder": "비밀번호를 입력하세요", + "login_button": "로그인", + "no_account": "계정이 없으신가요?", + "register": "가입하기", + "admin_setup": "처음이신가요?", + "setup": "관리자 설정", + "register_title": "계정 만들기", + "email": "이메일", + "email_placeholder": "이메일 주소를 입력하세요", + "confirm_password": "비밀번호 확인", + "confirm_password_placeholder": "비밀번호를 다시 입력하세요", + "register_button": "계정 만들기", + "have_account": "이미 계정이 있으신가요?", + "login": "로그인", + "setup_title": "초기 설정", + "setup_step1": "관리자", + "setup_step2": "시스템", + "setup_step3": "완료", + "admin_username": "관리자 사용자 이름", + "admin_email": "관리자 이메일", + "admin_password": "관리자 비밀번호", + "create_admin": "관리자 생성", + "back_to_login": "이미 설정하셨나요?", + "admin_success": "관리자 계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", + "account_success": "계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", + "passwords_mismatch": "비밀번호가 일치하지 않습니다", + "admin_create_error": "관리자 계정 생성 오류", + "or": "또는", + "sso_login": "SSO로 로그인", + "sso_login_provider": "{{provider}}(으)로 로그인", + "magicLinkHint": "비밀번호가 없으신가요? 이메일을 입력하시면 일회용 로그인 링크를 보내드립니다.", + "magicLinkEmailLabel": "이메일 주소", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "로그인 링크 보내기", + "magicLinkSent": "해당 이메일에 대한 계정이 있는 경우 로그인 링크가 전송되었습니다. 받은편지함을 확인하세요.", + "magicLinkUnavailable": "이 서버에서는 이메일 로그인을 사용할 수 없습니다.", + "magicLinkNetworkError": "서버에 연결할 수 없습니다: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "저장소", + "calculating": "계산 중...", + "used": "{{percentage}}% 사용 중 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "이 파일 형식은 미리보기를 지원하지 않습니다.", + "download_file": "파일 다운로드", + "zoom_in": "확대", + "zoom_out": "축소", + "zoom_reset": "줌 초기화" + }, + "language_selector": { + "title": "환영합니다!", + "subtitle": "계속하려면 언어를 선택하세요", + "continue": "계속", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ko": "한국어", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "아직 즐겨찾기가 없습니다", + "empty_hint": "파일이나 폴더에 별표를 눌러 즐겨찾기에 추가하세요", + "add": "즐겨찾기 추가", + "remove": "즐겨찾기 해제", + "added_title": "즐겨찾기에 추가됨", + "added_msg": "즐겨찾기에 추가되었습니다", + "removed_title": "즐겨찾기에서 삭제됨", + "removed_msg": "즐겨찾기에서 삭제되었습니다" + }, + "recent": { + "title": "최근", + "clear": "최근 항목 지우기", + "accessed": "접근일", + "empty_state": "최근 파일이 없습니다", + "empty_hint": "열어본 파일이 여기에 표시됩니다", + "loadMore": "더 불러오기" + }, + "notifications": { + "file_renamed": "파일 이름이 변경되었습니다", + "file_renamed_to": "파일 이름이 «{{name}}»(으)로 변경되었습니다", + "folder_renamed": "폴더 이름이 변경되었습니다", + "folder_renamed_to": "폴더 이름이 «{{name}}»(으)로 변경되었습니다", + "file_uploaded": "파일이 업로드되었습니다", + "file_deleted": "파일이 휴지통으로 이동되었습니다", + "folder_deleted": "폴더가 휴지통으로 이동되었습니다", + "item_deleted_permanently": "항목이 영구적으로 삭제되었습니다", + "trash_emptied": "휴지통이 성공적으로 비워졌습니다", + "title": "알림", + "empty": "알림이 없습니다", + "link_created": "링크 생성됨", + "share_success": "공유 링크가 성공적으로 생성되었습니다", + "upload_files_section_title": "여기서는 업로드할 수 없습니다", + "upload_files_section_body": "파일을 업로드하려면 파일 섹션으로 이동하세요" + }, + "batch": { + "one_selected": "1개 선택됨", + "n_selected": "{{count}}개 선택됨", + "confirm_delete": "{{count}}개 항목을 휴지통으로 이동하시겠습니까?", + "move_title": "{{count}}개 항목 이동", + "add_favorites": "즐겨찾기에 추가", + "move_copy": "이동 또는 복사" + }, + "admin": { + "page_title": "관리자 패널", + "back_to_app": "OxiCloud로 돌아가기", + "loading": "로딩 중…", + "access_denied": "접근 거부", + "access_denied_desc": "관리자 권한이 필요합니다.", + "sign_in": "로그인", + "tab_dashboard": "대시보드", + "tab_users": "사용자", + "tab_oidc": "SSO / OIDC", + "total_users": "전체 사용자", + "active_users": "활성 사용자", + "admins": "관리자", + "version": "버전", + "storage_overview": "스토리지 개요", + "used": "사용됨", + "total_quota": "총 할당량", + "usage_pct": "사용률", + "users_over_80": "할당량 80% 초과", + "users_over_quota": "할당량 초과", + "system": "시스템", + "auth_label": "인증", + "oidc_label": "OIDC", + "quotas_label": "할당량", + "enabled": "활성화됨", + "disabled": "비활성화됨", + "active": "활성", + "off": "꺼짐", + "allow_registration": "공개 자가 등록 허용", + "registration_warning": "공개 등록이 비활성화되어 있습니다. 관리자만 사용자를 만들 수 있습니다.", + "user_management": "사용자 관리", + "create_user": "사용자 생성", + "col_user": "사용자", + "col_role": "역할", + "col_auth": "인증", + "col_status": "상태", + "col_storage": "스토리지", + "col_last_login": "마지막 로그인", + "col_actions": "작업", + "loading_users": "사용자 로딩 중…", + "failed_load_users": "로드 실패", + "no_users_found": "사용자 없음", + "showing_users": "{{from}}-{{to}} / {{total}} 표시", + "prev": "이전", + "next": "다음", + "inactive": "비활성", + "you_badge": "(나)", + "local": "로컬", + "never": "없음", + "just_now": "방금", + "minutes_ago": "{{n}}분 전", + "hours_ago": "{{n}}시간 전", + "days_ago": "{{n}}일 전", + "edit_quota_title": "할당량 편집", + "reset_password_title": "비밀번호 재설정", + "toggle_role_title": "역할 전환", + "deactivate_title": "비활성화", + "activate_title": "활성화", + "delete_title": "삭제", + "sso_title": "싱글 사인온 (OIDC / SSO)", + "enable_sso": "SSO 인증 활성화", + "provider_name": "제공자 이름", + "issuer_url": "발급자 URL", + "issuer_url_hint": "OpenID Connect 발급자 URL", + "auto_discover": "자동 검색", + "discovering": "검색 중…", + "client_id": "클라이언트 ID", + "client_secret": "클라이언트 시크릿", + "client_secret_placeholder": "현재 값 유지하려면 비워두세요", + "secret_configured": "클라이언트 시크릿 구성됨", + "callback_url": "콜백 URL", + "callback_url_hint": "(IdP에 등록)", + "advanced_settings": "고급 설정", + "scopes": "스코프", + "auto_provision": "첫 로그인 시 자동 프로비저닝", + "admin_groups": "관리자 그룹", + "admin_groups_hint": "쉼표로 구분된 OIDC 그룹 이름", + "disable_password": "비밀번호 로그인 비활성화 (OIDC만)", + "password_warning": "모든 비밀번호 로그인이 차단됩니다!", + "test_btn": "테스트", + "save_btn": "저장", + "saving": "저장 중…", + "settings_saved": "설정 저장됨 — OIDC: {{status}}", + "quota_modal_title": "스토리지 할당량 업데이트", + "quota_user_label": "사용자:", + "new_quota": "새 할당량", + "quota_unlimited_hint": "무제한은 0", + "cancel": "취소", + "create_user_title": "새 사용자 생성", + "username_label": "사용자 이름", + "username_placeholder": "username", + "username_hint": "3–32자", + "password_label": "비밀번호", + "password_placeholder": "최소 8자", + "email_label": "이메일", + "email_optional": "(선택사항)", + "email_placeholder": "user@example.com (비어있으면 자동 생성)", + "role_label": "역할", + "role_user": "사용자", + "role_admin": "관리자", + "quota_label": "할당량", + "creating": "생성 중…", + "reset_pw_title": "비밀번호 재설정", + "new_password_label": "새 비밀번호", + "resetting": "재설정 중…", + "reset_btn": "재설정", + "confirm_role_change": "역할을 {{role}}(으)로 변경?", + "confirm_deactivate": "이 사용자를 비활성화하시겠습니까?", + "confirm_activate": "이 사용자를 활성화하시겠습니까?", + "confirm_delete_user": "사용자 \"{{name}}\" 삭제? 되돌릴 수 없습니다!", + "confirm_action": "작업 확인", + "confirm_yes": "확인", + "confirm_no": "취소", + "error_username_short": "사용자 이름 최소 3자", + "error_password_short": "비밀번호 최소 8자", + "error_generic": "실패", + "error_network": "네트워크 오류: {{message}}", + "error_create_user": "사용자 생성 실패", + "tab_storage": "저장소", + "storage_title": "저장소 구성", + "storage_current_backend": "현재 백엔드", + "storage_total_blobs": "총 블롭 수", + "storage_total_size": "총 크기", + "storage_dedup_ratio": "중복 제거 비율", + "storage_backend": "백엔드", + "storage_local": "로컬", + "storage_s3": "S3 호환", + "storage_provider_preset": "공급자 프리셋", + "storage_preset_custom": "사용자 지정", + "storage_endpoint_url": "엔드포인트 URL", + "storage_endpoint_hint": "AWS S3의 경우 비워두세요", + "storage_bucket": "버킷", + "storage_region": "지역", + "storage_access_key": "액세스 키", + "storage_secret_key": "시크릿 키", + "storage_secret_configured": "키 구성됨", + "storage_key_placeholder": "새 키 입력", + "storage_path_style": "경로 스타일 강제", + "storage_path_style_hint": "MinIO 및 일부 S3 호환 서비스에 필요", + "storage_test_connection": "연결 테스트", + "storage_test_success": "연결 성공", + "storage_test_failure": "연결 실패", + "storage_save": "구성 저장", + "storage_saved": "구성이 저장되었습니다", + "storage_migration": "데이터 마이그레이션", + "storage_migration_coming_soon": "마이그레이션 도구 곧 출시", + "migration_status_label": "마이그레이션 상태", + "migration_start": "마이그레이션 시작", + "migration_pause": "일시 중지", + "migration_resume": "재개", + "migration_verify": "확인", + "migration_complete": "완료", + "migration_started": "마이그레이션 시작됨", + "migration_paused_msg": "마이그레이션 일시 중지됨", + "migration_resumed_msg": "마이그레이션 재개됨", + "migration_completed_msg": "마이그레이션이 성공적으로 완료되었습니다", + "migration_verifying": "확인 중...", + "migration_verify_passed": "확인 통과", + "migration_verify_failed": "확인 실패", + "migration_failed_blobs": "실패한 블롭", + "testing": "테스트 중...", + "smtp_disabled": "비활성화됨 (호스트 미설정)", + "smtp_enabled": "활성화됨", + "smtp_enabled_label": "상태", + "smtp_intro": "SMTP는 환경 변수(OXICLOUD_SMTP_*)로만 구성됩니다. 아래 값들은 실행 중인 서버에서 읽어옵니다 — 변경하려면 환경을 수정하고 OxiCloud를 다시 시작하세요.", + "smtp_not_configured": "이 서버에는 SMTP가 구성되어 있지 않습니다.", + "smtp_send_failed": "전송 실패.", + "smtp_send_test": "테스트 이메일 보내기", + "smtp_sending": "보내는 중…", + "smtp_sent": "테스트 이메일을 보냈습니다.", + "smtp_server_code": "서버 응답", + "smtp_test_intro": "아래 수신자에게 미리 정의된 진단 메시지를 보내고 SMTP 서버의 응답을 표시합니다. 이를 통해 릴레이 로그와 대조하여 확인할 수 있습니다.", + "smtp_test_missing_to": "수신자 주소를 입력하세요.", + "smtp_test_title": "테스트 이메일 보내기", + "smtp_test_to": "수신자 주소", + "smtp_title": "발신 이메일 (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "프로필", + "back_to_app": "OxiCloud로 돌아가기", + "loading": "로딩 중…", + "not_authenticated": "인증되지 않음", + "not_authenticated_desc": "프로필을 보려면 로그인하세요.", + "sign_in": "로그인", + "role_admin": "관리자", + "role_user": "사용자", + "account_details": "계정 정보", + "username": "사용자 이름", + "email": "이메일", + "role": "역할", + "last_login": "마지막 로그인", + "storage": "스토리지", + "used": "사용됨", + "quota": "할당량", + "usage": "사용률", + "unlimited": "무제한", + "app_passwords": "앱 비밀번호", + "app_pw_desc": "WebDAV, CalDAV, CardDAV 클라이언트용 비밀번호를 생성합니다. 각 비밀번호는 한 번만 표시됩니다.", + "app_pw_label_placeholder": "라벨 (예: Thunderbird, macOS)", + "generate": "생성", + "generating": "생성 중…", + "new_password_for": "새 비밀번호:", + "copy_warning": "지금 이 비밀번호를 복사하세요. 다시 볼 수 없습니다.", + "copy_to_clipboard": "클립보드에 복사", + "col_label": "라벨", + "col_created": "생성일", + "col_last_used": "마지막 사용", + "col_status": "상태", + "active": "활성", + "revoked": "취소됨", + "revoke_title": "취소", + "no_app_passwords": "앱 비밀번호가 아직 없습니다.", + "client_sessions": "클라이언트 세션", + "client_sessions_desc": "Nextcloud 호환 클라이언트 연결 시 자동 생성됩니다.", + "col_client": "클라이언트", + "never": "없음", + "just_now": "방금", + "minutes_ago": "{{n}}분 전", + "hours_ago": "{{n}}시간 전", + "days_ago": "{{n}}일 전", + "edit_profile": "프로필 편집", + "edit_oidc_managed": "정보(이름, 성, 프로필 사진 등)를 변경하려면 ID 공급자에서 업데이트하세요. 변경 사항은 다음 로그인 시 반영됩니다.", + "username_claim_hint": "2~64자, 영문자 / 숫자 / 점 / 하이픈 / 밑줄. 선택한 후에는 사용자 이름을 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", + "username_already_claimed": "사용자 이름이 설정되어 있어 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", + "given_name": "이름", + "family_name": "성", + "notify_on_share": "다른 사람이 나에게 공유할 때 이메일로 알림 받기", + "notify_on_share_hint": "선택을 해제해도 공유 항목은 계정에 계속 표시되지만, 이메일 알림은 받지 않습니다.", + "save_profile": "변경 사항 저장", + "profile_saved": "프로필이 업데이트되었습니다", + "profile_no_changes": "저장할 변경 사항이 없습니다.", + "profile_save_failed": "저장 실패", + "username_taken_error": "이미 사용 중인 사용자 이름입니다.", + "username_immutable_error": "사용자 이름이 이미 설정되어 있어 여기서 변경할 수 없습니다. 이름을 변경하려면 관리자에게 문의하세요.", + "change_password": "비밀번호 변경", + "current_password": "현재 비밀번호", + "new_password": "새 비밀번호", + "min_8_chars": "최소 8자", + "confirm_password": "새 비밀번호 확인", + "update_password": "비밀번호 업데이트", + "updating": "업데이트 중…", + "password_updated": "비밀번호가 성공적으로 업데이트되었습니다", + "passwords_no_match": "비밀번호가 일치하지 않습니다", + "password_too_short": "비밀번호는 최소 8자여야 합니다", + "password_change_failed": "비밀번호 변경 실패", + "error_network": "네트워크 오류: {{message}}", + "error_label_required": "라벨을 입력하세요", + "error_create_pw": "앱 비밀번호 생성 실패", + "confirm_revoke": "앱 비밀번호 \"{{label}}\"을(를) 취소하시겠습니까? 이 비밀번호를 사용하는 클라이언트가 작동하지 않게 됩니다.", + "error_revoke": "취소 실패", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "업로드 중...", + "files": "파일", + "complete": "{{count}} / {{total}} 업로드됨" + }, + "storage_quota_exceeded": "저장 공간 할당량 초과", + "sharedwithme": { + "pageTitle": "나와 공유됨", + "pageDescription": "다른 사용자가 나와 공유한 파일 및 폴더", + "emptyStateTitle": "아직 공유된 항목이 없습니다", + "emptyStateDesc": "다른 사용자가 공유한 항목이 여기에 표시됩니다", + "loadMore": "더 불러오기", + "sharedBy": "공유한 사람", + "colName": "이름", + "colType": "유형", + "colSharedBy": "공유한 사람", + "colDate": "공유 날짜", + "colPermissions": "권한" + }, + "groupby": { + "none": "없음", + "title": "그룹화 기준", + "owner": "소유자", + "shareDate": "공유 날짜", + "type": "유형", + "type.folders": "폴더", + "accessedAt": "접근 날짜", + "modifiedAt": "수정 날짜", + "createdAt": "생성 날짜", + "size": "크기", + "favoriteDate": "즐겨찾기 날짜", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "새 항목" + }, + "dateBucket": { + "today": "오늘", + "last7days": "최근 7일", + "last30days": "최근 30일" + }, + "groups": { + "title": "그룹 관리", + "create_button": "그룹 생성", + "create_dialog_title": "새 그룹", + "edit_dialog_title": "그룹 이름 변경", + "name_label": "이름", + "name_placeholder": "engineering", + "description_label": "설명(선택사항)", + "members_section": "구성원", + "add_member_placeholder": "사용자 또는 그룹 추가…", + "no_members": "아직 구성원이 없습니다.", + "remove_member": "제거", + "delete_group": "그룹 삭제", + "delete_confirm": "\"{name}\" 그룹을 삭제하시겠습니까? 이 그룹을 참조하는 모든 권한이 해제됩니다.", + "empty_state": "아직 그룹이 없습니다.", + "load_more": "더 보기", + "back_to_list": "뒤로", + "loading": "로딩 중…", + "virtual_badge": "시스템", + "member_count_zero": "구성원 없음", + "member_count_one": "구성원 1명", + "member_count_other": "구성원 {count}명", + "delete_confirm_label": "확인을 위해 그룹 이름을 입력하세요:", + "delete_confirm_mismatch": "확인을 위해 그룹 이름을 정확히 입력하세요.", + "virtual_internal_name": "내부", + "members_loading": "구성원 로딩 중…", + "members_empty": "구성원 없음", + "virtual_internal_explanation": "이 서버의 모든 내부 사용자" + }, + "myshares": { + "copyLink": "링크 복사", + "deleteLink": "링크 삭제", + "notifyByEmail": "이메일로 알림", + "notifyFailed": "알림을 보낼 수 없습니다.", + "notifyGroupMembers": "그룹 구성원에게 알림", + "notifyRateLimited": "이 수신자에게 알림이 너무 많습니다 — 나중에 다시 시도하세요.", + "removeAccess": "액세스 제거", + "resendInvitation": "초대 이메일 다시 보내기" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/nl.json b/frontend/static/locales/nl.json new file mode 100644 index 00000000..468c93fd --- /dev/null +++ b/frontend/static/locales/nl.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Deze aanmeldlink is niet meer geldig", + "expired_body": "De link is mogelijk verlopen of al gebruikt. We kunnen je een nieuwe sturen — die komt binnen enkele seconden in je inbox.", + "resend_to": "Stuur een nieuwe link naar {{email}}", + "generic_unavailable": "Deze aanmeldlink is niet meer geldig. Hij is mogelijk al gebruikt of verlopen. Vraag een nieuwe link aan via de aanmeldpagina.", + "service_unavailable": "Aanmelden via magic link is niet ingeschakeld op deze server.", + "internal_error": "Er is iets misgegaan bij het aanmelden. Probeer het opnieuw.", + "resend_failure": "Er is iets misgegaan bij het versturen van de link. Probeer het opnieuw.", + "cross_browser_title": "Doorgaan met aanmelden op dit apparaat?", + "cross_browser_body": "Je hebt deze aanmeldlink geopend in een andere browser of op een ander apparaat dan waar je hem hebt aangevraagd.", + "cross_browser_warning": "Als jij deze link hebt aangevraagd, kun je veilig doorgaan. Zo niet, sluit deze pagina — op Doorgaan klikken zou iemand anders bij je account aanmelden.", + "cross_browser_continue": "Doorgaan en aanmelden", + "resend_confirmation_title": "Controleer je inbox", + "resend_confirmation_body": "Als de aanmeldlink bij een actief account hoorde, is er zojuist een nieuwe link verstuurd. Controleer je inbox.", + "return_link": "Terug naar OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", + "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen het door op de onderstaande link te klikken:\n{{link}}\n\nDe link werkt eenmalig en verloopt over {{ttl_hours}} uur.\nAls je deze uitnodiging niet verwacht, kun je dit bericht negeren.\n\n— OxiCloud" + }, + "login": { + "subject": "Aanmelden bij OxiCloud", + "body": "Hallo,\n\nGebruik de onderstaande link om je aan te melden bij OxiCloud. De link werkt eenmalig en verloopt over {{ttl_minutes}} minuten. Open hem op hetzelfde apparaat waarop je hem hebt aangevraagd.\n\n{{link}}\n\nAls je deze aanmeldlink niet hebt aangevraagd, kun je dit bericht negeren — er is geen verdere actie nodig.\n\n— OxiCloud" + }, + "kind_file": "bestand", + "kind_folder": "map", + "english_fallback_divider": "--- Engelse versie hieronder ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", + "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen OxiCloud om je nieuwe gedeelde item te bekijken:\n{{login_link}}\n\nMisschien heb je nog meer nieuwe gedeelde items van {{inviter}} — meld je aan om al je gedeelde items te zien.\n\n— OxiCloud\n\nJe ontvangt dit bericht omdat je een OxiCloud-account hebt en je voorkeur voor deelmeldingen aanstaat. Je kunt het uitzetten in je profiel (Stuur me een e-mail wanneer iemand iets met mij deelt)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalistisch cloudopslagsysteem" + }, + "nav": { + "files": "Bestanden", + "shared": "Gedeeld", + "recent": "Recente", + "favorites": "Favorieten", + "photos": "Foto's", + "music": "Muziek", + "trash": "Prullenbak", + "sharedwithme": "Gedeeld met mij" + }, + "photos": { + "empty_state": "Nog geen foto's", + "empty_hint": "Upload afbeeldingen of video's om ze hier te zien", + "items_selected": "geselecteerd", + "view_daily": "Dag", + "view_monthly": "Maand", + "view_yearly": "Jaar" + }, + "music": { + "create_playlist": "Afspeellijst Maken", + "playlists": "Afspeellijsten", + "no_playlists": "Nog geen afspeellijsten", + "select_playlist": "Selecteer een afspeellijst", + "select_hint": "Kies een afspeellijst uit de zijbalk of maak een nieuwe", + "add_tracks": "Tracks Toevoegen", + "no_tracks": "Geen tracks in deze afspeellijst", + "unknown_artist": "Onbekende Artiest", + "unknown_title": "Onbekend", + "confirm_delete": "Deze afspeellijst verwijderen?", + "playlist_name": "Naam afspeellijst", + "create": "Maken", + "delete": "Verwijderen", + "share": "Delen", + "edit": "Bewerken", + "play_all": "Alles Afspelen", + "shuffle": "Shuffle", + "repeat": "Herhalen", + "repeat_one": "Een Herhalen", + "queue": "Wachtrij", + "queue_empty": "Wachtrij is leeg", + "not_playing": "Niet afspelend", + "play": "Afspelen", + "pause": "Pauzeren", + "previous": "Vorige", + "next": "Volgende", + "volume": "Volume", + "mute": "Dempen", + "unmute": "Geluid aan", + "title": "Titel", + "artist": "Artiest", + "album": "Album", + "tracks": "tracks", + "add": "Toevoegen", + "added": "Toegevoegd!", + "added_to_playlist": "toegevoegd aan playlist", + "add_to_playlist": "Aan playlist toevoegen", + "load_error": "Fout bij laden van playlists", + "add_error": "Kon tracks niet toevoegen aan playlist", + "no_playlists_yet": "Nog geen playlists. Maak er eerst een!", + "selected_files": "Geselecteerd:", + "error": "Fout", + "search_audio": "Audiobestanden zoeken…", + "no_audio_files": "Geen audiobestanden gevonden", + "selected": "geselecteerd", + "loading": "Laden…", + "search_error": "Kan audiobestanden niet laden", + "adding": "Toevoegen…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "Zoek bestanden...", + "new_folder": "Nieuwe map", + "upload": "Uploaden", + "upload_files": "Bestanden uploaden", + "upload_folder": "Map uploaden", + "upload.uploading": "Uploaden...", + "upload.complete": "{count} / {total} geüpload", + "upload.files": "bestanden", + "rename": "Hernoemen", + "move": "Verplaatsen naar...", + "move_to": "Verplaatsen naar", + "delete": "Verwijderen", + "download": "Downloaden", + "view": "Bekijken", + "cancel": "Annuleren", + "confirm": "Bevestigen", + "share": "Delen", + "favorite": "Toevoegen aan favorieten", + "unfavorite": "Verwijderen uit favorieten", + "copy": "Kopiëren", + "notify": "Melden", + "send": "Verzenden", + "clear_recent": "Recente wissen", + "logout": "Uitloggen", + "create": "Maken", + "search_btn": "Zoeken", + "close": "Sluiten", + "delete_permanently": "Permanent verwijderen", + "empty_trash": "Prullenbak legen", + "open_parent_folder": "Naar bovenliggende map", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Weergave", + "about": "Over OxiCloud", + "about_description": "Cloudopslagplatform gebouwd met Rust & Clean Architecture. Snel, veilig en privé.", + "admin_panel": "Beheerpaneel", + "profile": "Mijn profiel", + "role_user": "Gebruiker", + "theme": { + "light": "Licht", + "dark": "Donker", + "auto": "Zoals systeem" + }, + "manage_groups": "Groepen beheren" + }, + "share": { + "dialogTitle": "Deellink", + "linkLabel": "Deellink:", + "copyLink": "Kopiëren", + "permissions": "Rechten:", + "permissionRead": "Lezen", + "permissionWrite": "Schrijven", + "permissionReshare": "Opnieuw delen", + "password": "Wachtwoordbeveiliging:", + "generatePassword": "Genereren", + "expiration": "Verloopdatum:", + "update": "Delen bijwerken", + "remove": "Delen verwijderen", + "notifyTitle": "Notificatie verzenden", + "notifyEmailLabel": "E-mailadres:", + "notifyMessageLabel": "Bericht (optioneel):", + "notifySend": "Notificatie verzenden", + "shareWithOthers": "Met anderen delen", + "sharePublicly": "Openbaar delen", + "shareSettings": "Deelinstellingen", + "shareCopied": "Link gekopieerd naar klembord", + "shareCreated": "Deellink succesvol aangemaakt", + "shareUpdated": "Deelinstellingen bijgewerkt", + "shareRemoved": "Delen verwijderd", + "inviteByEmail": "Uitnodigen via e-mail — uitnodiging wordt verzonden", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Deellink", + "share_linkLabel": "Deellink:", + "share_copyLink": "Kopiëren", + "share_permissions": "Rechten:", + "share_permissionRead": "Lezen", + "share_permissionWrite": "Schrijven", + "share_permissionReshare": "Opnieuw delen", + "share_password": "Wachtwoordbeveiliging:", + "share_generatePassword": "Genereren", + "share_expiration": "Verloopdatum:", + "share_update": "Share bijwerken", + "share_remove": "Share verwijderen", + "share_notifyTitle": "Notificatie verzenden", + "share_notifyEmailLabel": "E-mailadres:", + "share_notifyMessageLabel": "Bericht (optioneel):", + "share_notifySend": "Notificatie verzenden", + "shared": { + "backToFiles": "Terug naar Bestanden", + "pageTitle": "Gedeelde items", + "pageDescription": "Beheer je gedeelde bestanden en mappen", + "filterType": "Type:", + "filterAll": "Alles", + "filterFiles": "Bestanden", + "filterFolders": "Mappen", + "sortBy": "Sorteren op:", + "sortByName": "Naam", + "sortByDate": "Datum gedeeld", + "sortByExpiration": "Verloop", + "search": "Zoeken", + "colName": "Naam", + "colType": "Type", + "colDateShared": "Gedeeld op", + "colExpiration": "Verloop", + "colPermissions": "Rechten", + "colPassword": "Wachtwoord", + "colActions": "Acties", + "emptyStateTitle": "Nog geen gedeelde items", + "emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", + "goToFiles": "Ga naar Bestanden", + "typeFile": "Bestand", + "typeFolder": "Map", + "noExpiration": "Geen verloop", + "hasPassword": "Ja", + "noPassword": "Nee", + "editShare": "Delen bewerken", + "notifyShare": "Iemand informeren", + "copyLink": "Link kopiëren", + "removeShare": "Delen verwijderen", + "linkCopied": "Link gekopieerd naar klembord!", + "linkCopyFailed": "Kopiëren van link mislukt", + "itemUpdated": "Deelinstellingen bijgewerkt", + "itemRemoved": "Delen verwijderd", + "invalidEmail": "Voer een geldig e-mailadres in", + "notificationSent": "Notificatie verzonden", + "notificationFailed": "Notificatie verzenden mislukt", + "shared_backToFiles": "Terug naar Bestanden", + "shared_pageTitle": "Gedeelde items", + "shared_pageDescription": "Beheer je gedeelde bestanden en mappen", + "shared_filterType": "Type:", + "shared_filterAll": "Alles", + "shared_filterFiles": "Bestanden", + "shared_filterFolders": "Mappen", + "shared_sortBy": "Sorteren op:", + "shared_sortByName": "Naam", + "shared_sortByDate": "Datum gedeeld", + "shared_sortByExpiration": "Verloop", + "shared_search": "Zoeken", + "shared_colName": "Naam", + "shared_colType": "Type", + "shared_colDateShared": "Gedeeld op", + "shared_colExpiration": "Verloop", + "shared_colPermissions": "Rechten", + "shared_colPassword": "Wachtwoord", + "shared_colActions": "Acties", + "shared_emptyStateTitle": "Nog geen gedeelde items", + "shared_emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", + "shared_goToFiles": "Ga naar Bestanden", + "shared_typeFile": "Bestand", + "shared_typeFolder": "Map", + "shared_noExpiration": "Geen verloop", + "shared_hasPassword": "Ja", + "shared_noPassword": "Nee", + "shared_editShare": "Delen bewerken", + "shared_notifyShare": "Iemand informeren", + "shared_copyLink": "Link kopiëren", + "shared_removeShare": "Delen verwijderen", + "shared_linkCopied": "Link gekopieerd naar klembord!", + "shared_linkCopyFailed": "Kopiëren van link mislukt", + "shared_itemUpdated": "Deelinstellingen bijgewerkt", + "shared_itemRemoved": "Delen verwijderd", + "shared_invalidEmail": "Voer een geldig e-mailadres in", + "shared_notificationSent": "Notificatie verzonden", + "shared_notificationFailed": "Notificatie verzenden mislukt" + }, + "files": { + "name": "Naam", + "type": "Type", + "size": "Grootte", + "modified": "Gewijzigd", + "no_files": "Geen bestanden in deze map", + "empty_hint": "Upload bestanden of maak mappen aan om te beginnen", + "loading": "Bestanden laden…", + "view_grid": "Rasterweergave", + "view_list": "Lijstweergave", + "file_types": { + "document": "Document", + "image": "Afbeelding", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Tekst", + "folder": "Map", + "spreadsheet": "Spreadsheet", + "presentation": "Presentatie", + "archive": "Archief", + "installer": "Installatiebestand", + "code": "Code" + }, + "owner": "Eigenaar" + }, + "dialogs": { + "rename_folder": "Map hernoemen", + "rename_file": "Bestand hernoemen", + "new_name": "Nieuwe naam", + "new_folder_title": "Nieuwe map", + "folder_name": "Mapnaam", + "folder_placeholder": "Mijn map", + "rename_title": "Hernoemen", + "move_file": "Bestand verplaatsen", + "move_folder": "Map verplaatsen", + "select_destination": "Selecteer doelmap:", + "root": "Hoofdmap", + "delete_confirmation": "Weet je zeker dat je wilt verwijderen", + "and_contents": "en alle inhoud", + "no_undo": "Deze actie kan niet ongedaan gemaakt worden", + "confirm_title": "Actie bevestigen", + "confirm_delete": "Verplaatsen naar prullenbak", + "confirm_delete_file": "Weet je zeker dat je het bestand \"{{name}}\" naar de prullenbak wilt verplaatsen?", + "confirm_delete_folder": "Weet je zeker dat je de map \"{{name}}\" en alle inhoud naar de prullenbak wilt verplaatsen?", + "confirm_permanent_delete": "Permanent verwijderen", + "confirm_permanent_delete_msg": "Weet je zeker dat je dit item permanent wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden.", + "confirm_empty_trash": "Prullenbak legen", + "confirm_delete_share": "Deellink verwijderen", + "confirm_delete_share_msg": "Weet je zeker dat je deze deellink wilt verwijderen?", + "share_file": "Bestand delen", + "share_folder": "Map delen", + "existing_shares": "Bestaande delingen", + "share_options": "Deelopties", + "password": "Wachtwoord", + "expiration": "Verloop", + "permissions": "Rechten", + "generated_link": "Gegenereerde link", + "notify": "Notificatie verzenden", + "recipient": "Ontvanger", + "message": "Bericht", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Verplaatsen naar de thuismap" + }, + "dropzone": { + "drag_files": "Sleep bestanden hierheen of klik om te selecteren", + "drop_files": "Laat bestanden vallen om te uploaden" + }, + "permissions": { + "read": "Lezen", + "write": "Schrijven", + "reshare": "Opnieuw delen" + }, + "errors": { + "file_not_found": "Bestand niet gevonden", + "folder_not_found": "Map niet gevonden", + "delete_error": "Fout bij verwijderen", + "upload_error": "Fout bij uploaden van bestand", + "rename_error": "Fout bij hernoemen", + "move_error": "Fout bij verplaatsen", + "empty_name": "Naam mag niet leeg zijn", + "name_exists": "Een bestand of map met deze naam bestaat al", + "generic_error": "Er is een fout opgetreden", + "group_name_invalid": "De groepsnaam moet voldoen aan het e-mailprefix-formaat (letters, cijfers, punt, streepje, underscore; 1–64 tekens).", + "group_cycle": "Dit lid zou een circulaire groepsverwijzing veroorzaken.", + "group_depth_exceeded": "Deze nestdiepte overschrijdt het maximum (8).", + "group_virtual_immutable": "De groep 'Internal' wordt door het systeem beheerd en kan niet worden gewijzigd.", + "group_not_found": "Groep niet gevonden.", + "group_name_taken": "Er bestaat al een groep met deze naam." + }, + "breadcrumb": { + "home": "Start" + }, + "trash": { + "empty_trash": "Prullenbak legen", + "empty_state": "Prullenbak is leeg", + "original_location": "Oorspronkelijke locatie", + "deleted_date": "Verwijderdatum", + "remaining": "Resterend", + "actions": "Acties", + "restore": "Herstellen", + "delete_permanently": "Permanent verwijderen", + "empty_confirm": "Weet je zeker dat je de prullenbak wilt legen? Dit verwijdert alle items permanent.", + "groupby": { + "remaining_days": "Resterende dagen", + "trashed_time": "Verwijderd op" + } + }, + "daysRemaining": { + "expired": "Verlopen", + "today": "Vandaag", + "tomorrow": "Morgen", + "inDays": "{{count}} dagen" + }, + "expiryChip": { + "never": "Verloopt nooit", + "expired": "Verlopen", + "today": "Verloopt vandaag", + "tomorrow": "Verloopt morgen", + "inDays": "Verloopt over {{count}} dagen", + "onDate": "Verloopt op {{date}}" + }, + "auth": { + "login_title": "Inloggen", + "username": "Gebruikersnaam", + "username_placeholder": "Voer je gebruikersnaam in", + "login_identifier": "Gebruikersnaam of e-mail", + "login_identifier_placeholder": "Voer uw gebruikersnaam of e-mailadres in", + "password": "Wachtwoord", + "password_placeholder": "Voer je wachtwoord in", + "login_button": "Inloggen", + "no_account": "Nog geen account?", + "register": "Aanmelden", + "admin_setup": "Eerste keer?", + "setup": "Administrator instellen", + "register_title": "Account aanmaken", + "email": "E-mailadres", + "email_placeholder": "Voer je e-mailadres in", + "confirm_password": "Bevestig wachtwoord", + "confirm_password_placeholder": "Bevestig je wachtwoord", + "register_button": "Account aanmaken", + "have_account": "Heb je al een account?", + "login": "Inloggen", + "setup_title": "Eerste setup", + "setup_step1": "Admin", + "setup_step2": "Systeem", + "setup_step3": "Voltooid", + "admin_username": "Admin gebruikersnaam", + "admin_email": "Admin e-mailadres", + "admin_password": "Admin wachtwoord", + "create_admin": "Administrator aanmaken", + "back_to_login": "Al ingesteld?", + "admin_success": "Administrator account succesvol aangemaakt! Je kunt nu inloggen.", + "account_success": "Account succesvol aangemaakt! Je kunt nu inloggen.", + "passwords_mismatch": "Wachtwoorden komen niet overeen", + "admin_create_error": "Fout bij het aanmaken van het administratoraccount", + "or": "of", + "sso_login": "Inloggen met SSO", + "sso_login_provider": "Inloggen met {{provider}}", + "magicLinkHint": "Geen wachtwoord? Voer uw e-mailadres in en we sturen u een eenmalige aanmeldlink.", + "magicLinkEmailLabel": "E-mailadres", + "magicLinkEmailPlaceholder": "jij@voorbeeld.nl", + "magicLinkSubmit": "Aanmeldlink versturen", + "magicLinkSent": "Als er een account bestaat voor dat e-mailadres, is een aanmeldlink verzonden. Controleer uw inbox.", + "magicLinkUnavailable": "Aanmelden per e-mail is niet beschikbaar op deze server.", + "magicLinkNetworkError": "Kan de server niet bereiken: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "Opslag", + "calculating": "Bezig met berekenen...", + "used": "{{percentage}}% gebruikt ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Dit bestandstype kan niet bekeken worden.", + "download_file": "Bestand downloaden", + "zoom_in": "Inzoomen", + "zoom_out": "Uitzoomen", + "zoom_reset": "Zoom terugzetten" + }, + "language_selector": { + "title": "Welkom!", + "subtitle": "Selecteer je taal om door te gaan", + "continue": "Doorgaan", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Nog geen favorieten", + "empty_hint": "Markeer bestanden of mappen om ze aan je favorieten toe te voegen", + "add": "Toevoegen aan favorieten", + "remove": "Verwijderen uit favorieten", + "added_title": "Toegevoegd aan favorieten", + "added_msg": "toegevoegd aan favorieten", + "removed_title": "Verwijderd uit favorieten", + "removed_msg": "verwijderd uit favorieten" + }, + "recent": { + "title": "Recent", + "clear": "Recente wissen", + "accessed": "Geopend", + "empty_state": "Geen recente bestanden", + "empty_hint": "Bestanden die je opent verschijnen hier", + "loadMore": "Meer laden" + }, + "notifications": { + "file_renamed": "Bestand hernoemd", + "file_renamed_to": "Bestand hernoemd naar \"{{name}}\"", + "folder_renamed": "Map hernoemd", + "folder_renamed_to": "Map hernoemd naar \"{{name}}\"", + "file_uploaded": "Bestand geüpload", + "file_deleted": "Bestand verplaatst naar prullenbak", + "folder_deleted": "Map verplaatst naar prullenbak", + "item_deleted_permanently": "Item permanent verwijderd", + "trash_emptied": "Prullenbak succesvol geleegd", + "title": "Notificaties", + "empty": "Geen notificaties", + "link_created": "Link aangemaakt", + "share_success": "Deellink succesvol aangemaakt", + "upload_files_section_title": "Uploaden hier niet beschikbaar", + "upload_files_section_body": "Ga naar de sectie Bestanden om bestanden te uploaden" + }, + "batch": { + "one_selected": "1 item geselecteerd", + "n_selected": "{{count}} items geselecteerd", + "confirm_delete": "Weet je zeker dat je {{count}} items naar de prullenbak wilt verplaatsen?", + "move_title": "Verplaats {{count}} item(s)", + "add_favorites": "Toevoegen aan favorieten", + "move_copy": "Verplaatsen of kopiëren" + }, + "admin": { + "page_title": "Beheerderspaneel", + "back_to_app": "Terug naar OxiCloud", + "loading": "Laden…", + "access_denied": "Toegang geweigerd", + "access_denied_desc": "Beheerdersrechten vereist.", + "sign_in": "Inloggen", + "tab_dashboard": "Dashboard", + "tab_users": "Gebruikers", + "tab_oidc": "SSO / OIDC", + "total_users": "Totaal gebruikers", + "active_users": "Actieve gebruikers", + "admins": "Beheerders", + "version": "Versie", + "storage_overview": "Opslagoverzicht", + "used": "Gebruikt", + "total_quota": "Totaal quotum", + "usage_pct": "Gebruik %", + "users_over_80": "Gebruikers >80% quotum", + "users_over_quota": "Gebruikers boven quotum", + "system": "Systeem", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quota", + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "active": "Actief", + "off": "Uit", + "allow_registration": "Openbare zelfregistratie toestaan", + "registration_warning": "Openbare registratie is uitgeschakeld. Alleen beheerders kunnen gebruikers aanmaken.", + "user_management": "Gebruikersbeheer", + "create_user": "Gebruiker aanmaken", + "col_user": "Gebruiker", + "col_role": "Rol", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Opslag", + "col_last_login": "Laatste login", + "col_actions": "Acties", + "loading_users": "Gebruikers laden…", + "failed_load_users": "Laden mislukt", + "no_users_found": "Geen gebruikers gevonden", + "showing_users": "Toont {{from}}-{{to}} van {{total}}", + "prev": "Vorige", + "next": "Volgende", + "inactive": "Inactief", + "you_badge": "(jij)", + "local": "Lokaal", + "never": "Nooit", + "just_now": "Zojuist", + "minutes_ago": "{{n}}min geleden", + "hours_ago": "{{n}}u geleden", + "days_ago": "{{n}}d geleden", + "edit_quota_title": "Quotum bewerken", + "reset_password_title": "Wachtwoord resetten", + "toggle_role_title": "Rol wisselen", + "deactivate_title": "Deactiveren", + "activate_title": "Activeren", + "delete_title": "Verwijderen", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "SSO-authenticatie inschakelen", + "provider_name": "Providernaam", + "issuer_url": "Uitgever-URL", + "issuer_url_hint": "OpenID Connect uitgever-URL", + "auto_discover": "Auto-ontdekking", + "discovering": "Ontdekken…", + "client_id": "Client-ID", + "client_secret": "Client-secret", + "client_secret_placeholder": "Laat leeg om huidige waarde te behouden", + "secret_configured": "Een client-secret is al geconfigureerd", + "callback_url": "Callback-URL", + "callback_url_hint": "(registreer bij uw IdP)", + "advanced_settings": "Geavanceerde instellingen", + "scopes": "Scopes", + "auto_provision": "Gebruikers automatisch aanmaken bij eerste login", + "admin_groups": "Beheergroepen", + "admin_groups_hint": "Kommagescheiden OIDC-groepsnamen", + "disable_password": "Wachtwoord-login uitschakelen (alleen OIDC)", + "password_warning": "Dit voorkomt ALLE logins op basis van wachtwoord!", + "test_btn": "Testen", + "save_btn": "Opslaan", + "saving": "Opslaan…", + "settings_saved": "Instellingen opgeslagen — OIDC is nu {{status}}", + "quota_modal_title": "Opslagquotum bijwerken", + "quota_user_label": "Gebruiker:", + "new_quota": "Nieuw quotum", + "quota_unlimited_hint": "0 voor onbeperkt", + "cancel": "Annuleren", + "create_user_title": "Nieuwe gebruiker aanmaken", + "username_label": "Gebruikersnaam", + "username_placeholder": "jandevries", + "username_hint": "3–32 tekens", + "password_label": "Wachtwoord", + "password_placeholder": "Min 8 tekens", + "email_label": "E-mail", + "email_optional": "(optioneel)", + "email_placeholder": "gebruiker@voorbeeld.nl (automatisch indien leeg)", + "role_label": "Rol", + "role_user": "Gebruiker", + "role_admin": "Beheerder", + "quota_label": "Quotum", + "creating": "Aanmaken…", + "reset_pw_title": "Wachtwoord resetten", + "new_password_label": "Nieuw wachtwoord", + "resetting": "Resetten…", + "reset_btn": "Resetten", + "confirm_role_change": "Rol wijzigen naar {{role}}?", + "confirm_deactivate": "Weet u zeker dat u deze gebruiker wilt deactiveren?", + "confirm_activate": "Weet u zeker dat u deze gebruiker wilt activeren?", + "confirm_delete_user": "Gebruiker \"{{name}}\" VERWIJDEREN? Kan niet ongedaan worden gemaakt!", + "confirm_action": "Actie bevestigen", + "confirm_yes": "Bevestigen", + "confirm_no": "Annuleren", + "error_username_short": "Gebruikersnaam moet minimaal 3 tekens bevatten", + "error_password_short": "Wachtwoord moet minimaal 8 tekens bevatten", + "error_generic": "Mislukt", + "error_network": "Netwerkfout: {{message}}", + "error_create_user": "Kan gebruiker niet aanmaken", + "tab_storage": "Opslag", + "storage_title": "Opslagconfiguratie", + "storage_current_backend": "Huidig backend", + "storage_total_blobs": "Totaal blobs", + "storage_total_size": "Totale grootte", + "storage_dedup_ratio": "Deduplicatieverhouding", + "storage_backend": "Backend", + "storage_local": "Lokaal", + "storage_s3": "S3-compatibel", + "storage_provider_preset": "Providerinstelling", + "storage_preset_custom": "Aangepast", + "storage_endpoint_url": "Eindpunt-URL", + "storage_endpoint_hint": "Leeg laten voor AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Regio", + "storage_access_key": "Toegangssleutel", + "storage_secret_key": "Geheime sleutel", + "storage_secret_configured": "Sleutel geconfigureerd", + "storage_key_placeholder": "Nieuwe sleutel invoeren", + "storage_path_style": "Padstijl forceren", + "storage_path_style_hint": "Vereist voor MinIO en sommige S3-compatibele diensten", + "storage_test_connection": "Verbinding testen", + "storage_test_success": "Verbinding geslaagd", + "storage_test_failure": "Verbinding mislukt", + "storage_save": "Configuratie opslaan", + "storage_saved": "Configuratie opgeslagen", + "storage_migration": "Gegevensmigratie", + "storage_migration_coming_soon": "Migratietools binnenkort beschikbaar", + "migration_status_label": "Migratiestatus", + "migration_start": "Migratie starten", + "migration_pause": "Pauzeren", + "migration_resume": "Hervatten", + "migration_verify": "Verifiëren", + "migration_complete": "Voltooien", + "migration_started": "Migratie gestart", + "migration_paused_msg": "Migratie gepauzeerd", + "migration_resumed_msg": "Migratie hervat", + "migration_completed_msg": "Migratie succesvol voltooid", + "migration_verifying": "Bezig met verifiëren...", + "migration_verify_passed": "Verificatie geslaagd", + "migration_verify_failed": "Verificatie mislukt", + "migration_failed_blobs": "Mislukte blobs", + "testing": "Bezig met testen...", + "smtp_disabled": "Uitgeschakeld (host niet ingesteld)", + "smtp_enabled": "Ingeschakeld", + "smtp_enabled_label": "Status", + "smtp_intro": "SMTP wordt uitsluitend geconfigureerd via omgevingsvariabelen (OXICLOUD_SMTP_*). De onderstaande waarden worden gelezen uit de actieve server — om ze te wijzigen, bewerk de omgeving en herstart OxiCloud.", + "smtp_not_configured": "SMTP is niet geconfigureerd op deze server.", + "smtp_send_failed": "Verzenden mislukt.", + "smtp_send_test": "Test-e-mail verzenden", + "smtp_sending": "Bezig met verzenden…", + "smtp_sent": "Test-e-mail verzonden.", + "smtp_server_code": "Serverantwoord", + "smtp_test_intro": "Verzendt een vooraf gedefinieerd diagnostisch bericht naar de onderstaande ontvanger en rapporteert het antwoord van de SMTP-server, zodat je het kunt correleren met je relay-logboeken.", + "smtp_test_missing_to": "Voer een ontvangeradres in.", + "smtp_test_title": "Test-e-mail verzenden", + "smtp_test_to": "Ontvangeradres", + "smtp_title": "Uitgaande e-mail (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Profiel", + "back_to_app": "Terug naar OxiCloud", + "loading": "Laden…", + "not_authenticated": "Niet geauthenticeerd", + "not_authenticated_desc": "Log in om uw profiel te bekijken.", + "sign_in": "Inloggen", + "role_admin": "Beheerder", + "role_user": "Gebruiker", + "account_details": "Accountgegevens", + "username": "Gebruikersnaam", + "email": "E-mail", + "role": "Rol", + "last_login": "Laatste login", + "storage": "Opslag", + "used": "Gebruikt", + "quota": "Quotum", + "usage": "Gebruik", + "unlimited": "Onbeperkt", + "app_passwords": "App-wachtwoorden", + "app_pw_desc": "Genereer wachtwoorden voor WebDAV-, CalDAV- en CardDAV-clients. Elk wachtwoord wordt slechts één keer getoond.", + "app_pw_label_placeholder": "Label (bijv. Thunderbird, macOS)", + "generate": "Genereren", + "generating": "Genereren…", + "new_password_for": "Nieuw wachtwoord voor", + "copy_warning": "Kopieer dit wachtwoord nu. U kunt het niet opnieuw bekijken.", + "copy_to_clipboard": "Kopiëren naar klembord", + "col_label": "Label", + "col_created": "Aangemaakt", + "col_last_used": "Laatst gebruikt", + "col_status": "Status", + "active": "Actief", + "revoked": "Ingetrokken", + "revoke_title": "Intrekken", + "no_app_passwords": "Nog geen app-wachtwoorden.", + "client_sessions": "Clientsessies", + "client_sessions_desc": "Automatisch gegenereerd bij het verbinden van een Nextcloud-compatibele client.", + "col_client": "Client", + "never": "Nooit", + "just_now": "Zojuist", + "minutes_ago": "{{n}} min geleden", + "hours_ago": "{{n}}u geleden", + "days_ago": "{{n}} dagen geleden", + "edit_profile": "Profiel bewerken", + "edit_oidc_managed": "Om uw gegevens (naam, voornaam, profielfoto, …) te wijzigen, werk ze bij bij uw identity provider. De wijzigingen verschijnen bij uw volgende aanmelding.", + "username_claim_hint": "2–64 tekens, letters / cijfers / punt / streepje / underscore. Eenmaal gekozen kan de gebruikersnaam niet meer worden gewijzigd (DAV/NextCloud-clients zijn ervan afhankelijk).", + "username_already_claimed": "Gebruikersnaam ingesteld en niet wijzigbaar (DAV/NextCloud-clients zijn ervan afhankelijk).", + "given_name": "Voornaam", + "family_name": "Achternaam", + "notify_on_share": "Stuur me een e-mail wanneer iemand iets met mij deelt", + "notify_on_share_hint": "Wanneer uitgevinkt, verschijnen gedeelde items nog steeds in je account — je krijgt er alleen geen e-mail over.", + "save_profile": "Wijzigingen opslaan", + "profile_saved": "Profiel bijgewerkt", + "profile_no_changes": "Geen wijzigingen om op te slaan.", + "profile_save_failed": "Opslaan mislukt", + "username_taken_error": "Die gebruikersnaam is al in gebruik.", + "username_immutable_error": "Uw gebruikersnaam is al ingesteld en kan hier niet worden gewijzigd. Neem contact op met een beheerder als u wilt hernoemen.", + "change_password": "Wachtwoord wijzigen", + "current_password": "Huidig wachtwoord", + "new_password": "Nieuw wachtwoord", + "min_8_chars": "Minimaal 8 tekens", + "confirm_password": "Bevestig nieuw wachtwoord", + "update_password": "Wachtwoord bijwerken", + "updating": "Bijwerken…", + "password_updated": "Wachtwoord succesvol bijgewerkt", + "passwords_no_match": "Wachtwoorden komen niet overeen", + "password_too_short": "Wachtwoord moet minimaal 8 tekens bevatten", + "password_change_failed": "Wachtwoord wijzigen mislukt", + "error_network": "Netwerkfout: {{message}}", + "error_label_required": "Voer een label in", + "error_create_pw": "App-wachtwoord aanmaken mislukt", + "confirm_revoke": "App-wachtwoord \"{{label}}\" intrekken? Clients die dit wachtwoord gebruiken zullen stoppen.", + "error_revoke": "Intrekken mislukt", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Bezig met uploaden...", + "files": "bestanden", + "complete": "{{count}} / {{total}} geüpload" + }, + "storage_quota_exceeded": "Opslagquotum overschreden", + "sharedwithme": { + "pageTitle": "Gedeeld met mij", + "pageDescription": "Bestanden en mappen die andere gebruikers met u hebben gedeeld", + "emptyStateTitle": "Er is nog niets met u gedeeld", + "emptyStateDesc": "Items die andere gebruikers met u delen, verschijnen hier", + "loadMore": "Meer laden", + "sharedBy": "Gedeeld door", + "colName": "Naam", + "colType": "Type", + "colSharedBy": "Gedeeld door", + "colDate": "Datum gedeeld", + "colPermissions": "Machtigingen" + }, + "groupby": { + "none": "Geen", + "title": "Groeperen op", + "owner": "Eigenaar", + "shareDate": "Deeldatum", + "type": "Type", + "type.folders": "Mappen", + "accessedAt": "Toegangsdatum", + "modifiedAt": "Wijzigingsdatum", + "createdAt": "Aanmaakdatum", + "size": "Grootte", + "favoriteDate": "Favoritendatum", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nieuw" + }, + "dateBucket": { + "today": "Vandaag", + "last7days": "Afgelopen 7 dagen", + "last30days": "Afgelopen 30 dagen" + }, + "groups": { + "title": "Groepen beheren", + "create_button": "Groep maken", + "create_dialog_title": "Nieuwe groep", + "edit_dialog_title": "Groep hernoemen", + "name_label": "Naam", + "name_placeholder": "engineering", + "description_label": "Beschrijving (optioneel)", + "members_section": "Leden", + "add_member_placeholder": "Een gebruiker of groep toevoegen…", + "no_members": "Nog geen leden.", + "remove_member": "Verwijderen", + "delete_group": "Groep verwijderen", + "delete_confirm": "De groep \"{name}\" verwijderen? Aan deze groep gekoppelde rechten worden ingetrokken.", + "empty_state": "Nog geen groepen.", + "load_more": "Meer laden", + "back_to_list": "Terug", + "loading": "Bezig met laden…", + "virtual_badge": "Systeem", + "member_count_zero": "Geen leden", + "member_count_one": "1 lid", + "member_count_other": "{count} leden", + "delete_confirm_label": "Typ de groepsnaam ter bevestiging:", + "delete_confirm_mismatch": "Typ de groepsnaam exact om te bevestigen.", + "virtual_internal_name": "Intern", + "members_loading": "Leden laden…", + "members_empty": "Geen leden", + "virtual_internal_explanation": "Iedere interne gebruiker op deze server" + }, + "myshares": { + "copyLink": "Link kopiëren", + "deleteLink": "Link verwijderen", + "notifyByEmail": "Per e-mail notificeren", + "notifyFailed": "Notificatie kon niet worden verzonden.", + "notifyGroupMembers": "Groepsleden notificeren", + "notifyRateLimited": "Te veel notificaties voor deze ontvanger — probeer het later opnieuw.", + "removeAccess": "Toegang verwijderen", + "resendInvitation": "Uitnodigingsmail opnieuw verzenden" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/pl.json b/frontend/static/locales/pl.json new file mode 100644 index 00000000..320e34b8 --- /dev/null +++ b/frontend/static/locales/pl.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Ten link logowania nie jest już ważny", + "expired_body": "Link mógł wygasnąć lub został już użyty. Możemy wysłać Ci nowy — dotrze do Twojej skrzynki odbiorczej w ciągu kilku sekund.", + "resend_to": "Wyślij nowy link do {{email}}", + "generic_unavailable": "Ten link logowania nie jest już ważny. Mógł zostać już użyty lub wygasł. Poproś o nowy link na stronie logowania.", + "service_unavailable": "Logowanie magic link nie jest włączone na tym serwerze.", + "internal_error": "Coś poszło nie tak podczas logowania. Spróbuj ponownie.", + "resend_failure": "Coś poszło nie tak podczas wysyłania linku. Spróbuj ponownie.", + "cross_browser_title": "Kontynuować logowanie na tym urządzeniu?", + "cross_browser_body": "Otworzyłeś ten link logowania w innej przeglądarce lub urządzeniu niż to, z którego został zażądany.", + "cross_browser_warning": "Jeśli to Ty zażądałeś tego linku, możesz bezpiecznie kontynuować. W przeciwnym razie zamknij tę stronę — kliknięcie Kontynuuj zaloguje kogoś innego na Twoje konto.", + "cross_browser_continue": "Kontynuuj i zaloguj się", + "resend_confirmation_title": "Sprawdź swoją skrzynkę", + "resend_confirmation_body": "Jeśli link logowania należał do aktywnego konta, nowy link właśnie został wysłany. Sprawdź swoją skrzynkę.", + "return_link": "Powrót do OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", + "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz, klikając poniższy link:\n{{link}}\n\nLink działa raz i wygasa za {{ttl_hours}} godzin.\nJeśli nie spodziewałeś się tego zaproszenia, możesz zignorować tę wiadomość.\n\n— OxiCloud" + }, + "login": { + "subject": "Zaloguj się do OxiCloud", + "body": "Cześć,\n\nUżyj poniższego linku, aby zalogować się do OxiCloud. Link działa raz i wygasa za {{ttl_minutes}} minut. Otwórz go na tym samym urządzeniu, z którego został zażądany.\n\n{{link}}\n\nJeśli nie żądałeś tego linku logowania, możesz zignorować tę wiadomość — nie jest wymagane żadne dalsze działanie.\n\n— OxiCloud" + }, + "kind_file": "plik", + "kind_folder": "folder", + "english_fallback_divider": "--- Wersja angielska poniżej ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", + "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz OxiCloud, aby zobaczyć nowe udostępnienie:\n{{login_link}}\n\nMożesz mieć dodatkowe nowe udostępnienia od {{inviter}} — zaloguj się, aby zobaczyć wszystkie udostępnione Ci elementy.\n\n— OxiCloud\n\nOtrzymujesz tę wiadomość, ponieważ masz konto OxiCloud i preferencja powiadomień o udostępnieniach jest włączona. Możesz ją wyłączyć w swoim profilu (Wyślij mi e-mail, gdy ktoś coś mi udostępni)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalistyczny cloud storage" + }, + "nav": { + "files": "Pliki", + "shared": "Udostępnione", + "recent": "Ostatnie", + "favorites": "Ulubione", + "photos": "Zdjęcia", + "music": "Muzyka", + "trash": "Kosz", + "sharedwithme": "Udostępnione dla mnie" + }, + "photos": { + "empty_state": "Brak zdjęć", + "empty_hint": "Prześlij obrazy lub filmy, aby zobaczyć je tutaj", + "items_selected": "wybrane", + "view_daily": "Dzień", + "view_monthly": "Miesiąc", + "view_yearly": "Rok" + }, + "music": { + "create_playlist": "Utwórz playlistę", + "playlists": "Playlisty", + "no_playlists": "Brak playlist", + "empty_hint": "Utwórz pierwszą playlistę, aby zacząć organizować swoją muzykę", + "select_playlist": "Wybierz playlistę", + "select_hint": "Wybierz playlistę z paska bocznego lub utwórz nową", + "add_tracks": "Dodaj utwory", + "add_to_playlist": "Dodaj do playlisty", + "add": "Dodaj", + "added": "Dodano!", + "added_to_playlist": "dodano do playlisty", + "load_error": "Błąd ładowania playlist", + "add_error": "Nie można dodać utworów do playlisty", + "no_playlists_yet": "Brak playlist. Utwórz pierwszą!", + "selected_files": "Wybrane:", + "no_tracks": "Brak utworów na tej playliście", + "unknown_artist": "Nieznany wykonawca", + "unknown_title": "Nieznany", + "confirm_delete": "Usunąć tę playlistę?", + "playlist_name": "Nazwa playlisty", + "create": "Utwórz", + "delete": "Usuń", + "share": "Udostępnij", + "edit": "Edytuj", + "play_all": "Odtwórz wszystkie", + "shuffle": "Losowo", + "repeat": "Powtarzaj", + "repeat_one": "Powtarzaj jeden", + "queue": "Kolejka", + "queue_empty": "Kolejka jest pusta", + "not_playing": "Nic nie jest odtwarzane", + "play": "Odtwórz", + "pause": "Pauza", + "previous": "Poprzedni", + "next": "Następny", + "volume": "Głośność", + "mute": "Wycisz", + "unmute": "Włącz dźwięk", + "title": "Tytuł", + "artist": "Wykonawca", + "album": "Album", + "tracks": "utwory", + "share_with_user": "ID użytkownika lub e-mail", + "playback_error": "Odtwarzanie nie powiodło się", + "error": "Błąd", + "remove": "Usuń", + "track_removed": "Utwór usunięty", + "manage_shares": "Zarządzaj udostępnieniami", + "no_shares": "Brak udostępnień", + "remove_share": "Usuń udostępnienie", + "can_write": "Może edytować", + "read_only": "Tylko do odczytu", + "public": "Publiczny", + "private": "Prywatny", + "toggle_public": "Widoczność", + "make_public": "Ustaw jako publiczny", + "make_private": "Ustaw jako prywatny", + "set_cover": "Ustaw okładkę", + "cover_updated": "Okładka zaktualizowana", + "search_audio": "Szukaj plików audio…", + "no_audio_files": "Nie znaleziono plików audio", + "selected": "wybrane", + "loading": "Ładowanie…", + "search_error": "Nie można załadować plików audio", + "adding": "Dodawanie…" + }, + "actions": { + "search": "Szukaj plików...", + "new_folder": "Nowy folder", + "upload": "Prześlij", + "upload_files": "Prześlij pliki", + "upload_folder": "Prześlij folder", + "upload.uploading": "Przesyłanie...", + "upload.complete": "{count} / {total} przesłano", + "upload.files": "plików", + "rename": "Zmień nazwę", + "move": "Przenieś do...", + "move_to": "Przenieś do", + "delete": "Usuń", + "download": "Pobierz", + "view": "Pokaż", + "cancel": "Anuluj", + "confirm": "Potwierdź", + "share": "Udostępnij", + "favorite": "Dodaj do ulubionych", + "unfavorite": "Usuń z ulubionych", + "copy": "Kopiuj", + "notify": "Powiadom", + "send": "Wyślij", + "clear_recent": "Wyczyść ostatnie", + "logout": "Wyloguj się", + "create": "Utwórz", + "search_btn": "Szukaj", + "close": "Zamknij", + "delete_permanently": "Usuń trwale", + "empty_trash": "Opróżnij kosz", + "open_parent_folder": "Przejdź do folderu nadrzędnego", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Wygląd", + "about": "O OxiCloud", + "about_description": "Platforma pamięci masowej w chmurze zbudowana w oparciu o Rust & Clean Architecture. Szybka, bezpieczna i prywatna.", + "admin_panel": "Panel administratora", + "profile": "Mój profil", + "role_user": "Użytkownik", + "theme": { + "light": "Jasny", + "dark": "Ciemny", + "auto": "Jak system" + }, + "manage_groups": "Zarządzaj grupami" + }, + "share": { + "dialogTitle": "Link udostępniania", + "linkLabel": "Link udostępniania:", + "copyLink": "Kopiuj", + "permissions": "Uprawnienia:", + "permissionRead": "Odczyt", + "permissionWrite": "Zapis", + "permissionReshare": "Dalsze udostępnianie", + "password": "Ochrona hasłem:", + "generatePassword": "Wygeneruj", + "expiration": "Data wygaśnięcia:", + "update": "Zaktualizuj udostępnienie", + "remove": "Usuń udostępnienie", + "notifyTitle": "Wyślij powiadomienie", + "notifyEmailLabel": "Adres e-mail:", + "notifyMessageLabel": "Wiadomość (opcjonalnie):", + "notifySend": "Wyślij powiadomienie", + "shareWithOthers": "Udostępnij innym", + "sharePublicly": "Udostępnij publicznie", + "shareSettings": "Ustawienia udostępniania", + "shareCopied": "Link skopiowany do schowka", + "shareCreated": "Link udostępniania utworzony pomyślnie", + "shareUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", + "shareRemoved": "Udostępnienie usunięte pomyślnie", + "inviteByEmail": "Zaproś przez e-mail — zaproszenie zostanie wysłane", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Link udostępniania", + "share_linkLabel": "Link udostępniania:", + "share_copyLink": "Kopiuj", + "share_permissions": "Uprawnienia:", + "share_permissionRead": "Odczyt", + "share_permissionWrite": "Zapis", + "share_permissionReshare": "Dalsze udostępnianie", + "share_password": "Ochrona hasłem:", + "share_generatePassword": "Wygeneruj", + "share_expiration": "Data wygaśnięcia:", + "share_update": "Zaktualizuj udostępnienie", + "share_remove": "Usuń udostępnienie", + "share_notifyTitle": "Wyślij powiadomienie", + "share_notifyEmailLabel": "Adres e-mail:", + "share_notifyMessageLabel": "Wiadomość (opcjonalnie):", + "share_notifySend": "Wyślij powiadomienie", + "shared": { + "backToFiles": "Powrót do plików", + "pageTitle": "Udostępnione zasoby", + "pageDescription": "Zarządzaj udostępnionymi plikami i folderami", + "filterType": "Typ:", + "filterAll": "Wszystkie", + "filterFiles": "Pliki", + "filterFolders": "Foldery", + "sortBy": "Sortuj według:", + "sortByName": "Nazwa", + "sortByDate": "Data udostępnienia", + "sortByExpiration": "Wygaśnięcie", + "search": "Szukaj", + "colName": "Nazwa", + "colType": "Typ", + "colDateShared": "Data udostępnienia", + "colExpiration": "Wygaśnięcie", + "colPermissions": "Uprawnienia", + "colPassword": "Hasło", + "colActions": "Akcje", + "emptyStateTitle": "Brak udostępnionych zasobów", + "emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", + "goToFiles": "Przejdź do plików", + "typeFile": "Plik", + "typeFolder": "Folder", + "noExpiration": "Bez wygaśnięcia", + "hasPassword": "Tak", + "noPassword": "Nie", + "editShare": "Edytuj udostępnienie", + "notifyShare": "Powiadom kogoś", + "copyLink": "Kopiuj link", + "removeShare": "Usuń udostępnienie", + "linkCopied": "Link skopiowany do schowka!", + "linkCopyFailed": "Nie udało się skopiować linku", + "itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", + "itemRemoved": "Udostępnienie usunięte pomyślnie", + "invalidEmail": "Wprowadź prawidłowy adres e-mail", + "notificationSent": "Powiadomienie wysłane pomyślnie", + "notificationFailed": "Nie udało się wysłać powiadomienia", + "shared_backToFiles": "Powrót do plików", + "shared_pageTitle": "Udostępnione zasoby", + "shared_pageDescription": "Zarządzaj udostępnionymi plikami i folderami", + "shared_filterType": "Typ:", + "shared_filterAll": "Wszystkie", + "shared_filterFiles": "Pliki", + "shared_filterFolders": "Foldery", + "shared_sortBy": "Sortuj według:", + "shared_sortByName": "Nazwa", + "shared_sortByDate": "Data udostępnienia", + "shared_sortByExpiration": "Wygaśnięcie", + "shared_search": "Szukaj", + "shared_colName": "Nazwa", + "shared_colType": "Typ", + "shared_colDateShared": "Data udostępnienia", + "shared_colExpiration": "Wygaśnięcie", + "shared_colPermissions": "Uprawnienia", + "shared_colPassword": "Hasło", + "shared_colActions": "Akcje", + "shared_emptyStateTitle": "Brak udostępnionych zasobów", + "shared_emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", + "shared_goToFiles": "Przejdź do plików", + "shared_typeFile": "Plik", + "shared_typeFolder": "Folder", + "shared_noExpiration": "Bez wygaśnięcia", + "shared_hasPassword": "Tak", + "shared_noPassword": "Nie", + "shared_editShare": "Edytuj udostępnienie", + "shared_notifyShare": "Powiadom kogoś", + "shared_copyLink": "Kopiuj link", + "shared_removeShare": "Usuń udostępnienie", + "shared_linkCopied": "Link skopiowany do schowka!", + "shared_linkCopyFailed": "Nie udało się skopiować linku", + "shared_itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", + "shared_itemRemoved": "Udostępnienie usunięte pomyślnie", + "shared_invalidEmail": "Wprowadź prawidłowy adres e-mail", + "shared_notificationSent": "Powiadomienie wysłane pomyślnie", + "shared_notificationFailed": "Nie udało się wysłać powiadomienia" + }, + "files": { + "name": "Nazwa", + "type": "Typ", + "size": "Rozmiar", + "modified": "Zmodyfikowano", + "no_files": "Brak plików w tym folderze", + "empty_hint": "Prześlij pliki lub utwórz foldery, aby rozpocząć", + "loading": "Ładowanie plików…", + "view_grid": "Widok siatki", + "view_list": "Widok listy", + "file_types": { + "document": "Dokument", + "image": "Obraz", + "video": "Wideo", + "audio": "Audio", + "pdf": "PDF", + "text": "Tekst", + "folder": "Folder", + "spreadsheet": "Arkusz kalkulacyjny", + "presentation": "Prezentacja", + "archive": "Archiwum", + "installer": "Instalator", + "code": "Kod" + }, + "owner": "Właściciel" + }, + "dialogs": { + "rename_folder": "Zmień nazwę folderu", + "rename_file": "Zmień nazwę pliku", + "new_name": "Nowa nazwa", + "new_folder_title": "Nowy folder", + "folder_name": "Nazwa folderu", + "folder_placeholder": "Mój folder", + "rename_title": "Zmień nazwę", + "move_file": "Przenieś plik", + "move_folder": "Przenieś folder", + "select_destination": "Wybierz folder docelowy:", + "select_this_folder": "Wybierz ten folder", + "go_to_parent": ".. (folder nadrzędny)", + "no_subfolders": "Brak podfolderów", + "root": "Główny", + "delete_confirmation": "Czy na pewno chcesz usunąć", + "and_contents": "i całą jego zawartość", + "no_undo": "Tej akcji nie można cofnąć", + "confirm_title": "Potwierdź akcję", + "confirm_delete": "Przenieś do kosza", + "confirm_delete_file": "Czy na pewno chcesz przenieść plik \"{{name}}\" do kosza?", + "confirm_delete_folder": "Czy na pewno chcesz przenieść folder \"{{name}}\" i całą jego zawartość do kosza?", + "confirm_permanent_delete": "Usuń trwale", + "confirm_permanent_delete_msg": "Czy na pewno chcesz trwale usunąć ten element? Tej akcji nie można cofnąć.", + "confirm_empty_trash": "Opróżnij kosz", + "confirm_delete_share": "Usuń link udostępniania", + "confirm_delete_share_msg": "Czy na pewno chcesz usunąć ten link udostępniania?", + "share_file": "Udostępnij plik", + "share_folder": "Udostępnij folder", + "existing_shares": "Istniejące udostępnienia", + "share_options": "Opcje udostępniania", + "password": "Hasło", + "expiration": "Wygaśnięcie", + "permissions": "Uprawnienia", + "generated_link": "Wygenerowany link", + "notify": "Wyślij powiadomienie", + "recipient": "Odbiorca", + "message": "Wiadomość", + "move_to_home": "Przenieś do folderu domowego" + }, + "dropzone": { + "drag_files": "Przeciągnij pliki tutaj lub kliknij, aby wybrać", + "drop_files": "Upuść pliki, aby przesłać" + }, + "permissions": { + "read": "Odczyt", + "write": "Zapis", + "reshare": "Dalsze udostępnianie" + }, + "errors": { + "file_not_found": "Plik nie został znaleziony", + "folder_not_found": "Folder nie został znaleziony", + "delete_error": "Błąd podczas usuwania", + "upload_error": "Błąd podczas przesyłania pliku", + "rename_error": "Błąd podczas zmiany nazwy", + "move_error": "Błąd podczas przenoszenia", + "empty_name": "Nazwa nie może być pusta", + "name_exists": "Plik lub folder o tej nazwie już istnieje", + "generic_error": "Wystąpił błąd", + "group_name_invalid": "Nazwa grupy musi spełniać format prefiksu e-mail (litery, cyfry, kropka, myślnik, podkreślnik; 1–64 znaków).", + "group_cycle": "Ten członek utworzyłby cykliczne odwołanie między grupami.", + "group_depth_exceeded": "Ta głębokość zagnieżdżenia przekracza maksymalną dozwoloną (8).", + "group_virtual_immutable": "Grupa „Internal” jest zarządzana przez system i nie może być modyfikowana.", + "group_not_found": "Grupa nie znaleziona.", + "group_name_taken": "Grupa o tej nazwie już istnieje." + }, + "breadcrumb": { + "home": "Strona główna" + }, + "trash": { + "empty_trash": "Opróżnij kosz", + "empty_state": "Kosz jest pusty", + "original_location": "Pierwotna lokalizacja", + "deleted_date": "Data usunięcia", + "remaining": "Pozostało", + "actions": "Akcje", + "restore": "Przywróć", + "delete_permanently": "Usuń trwale", + "empty_confirm": "Czy na pewno chcesz opróżnić kosz? Wszystkie elementy zostaną trwale usunięte.", + "groupby": { + "remaining_days": "Pozostałe dni", + "trashed_time": "Czas usunięcia" + } + }, + "daysRemaining": { + "expired": "Wygasł", + "today": "Dziś", + "tomorrow": "Jutro", + "inDays": "{{count}} dni" + }, + "expiryChip": { + "never": "Nigdy nie wygasa", + "expired": "Wygasł", + "today": "Wygasa dziś", + "tomorrow": "Wygasa jutro", + "inDays": "Wygasa za {{count}} dni", + "onDate": "Wygasa {{date}}" + }, + "auth": { + "login_title": "Zaloguj się", + "username": "Nazwa użytkownika", + "username_placeholder": "Wprowadź nazwę użytkownika", + "login_identifier": "Nazwa użytkownika lub e-mail", + "login_identifier_placeholder": "Wpisz nazwę użytkownika lub e-mail", + "password": "Hasło", + "password_placeholder": "Wprowadź hasło", + "login_button": "Zaloguj się", + "no_account": "Nie masz konta?", + "register": "Zarejestruj się", + "admin_setup": "Pierwszy raz?", + "setup": "Konfiguracja administratora", + "register_title": "Utwórz konto", + "email": "E-mail", + "email_placeholder": "Wprowadź adres e-mail", + "confirm_password": "Potwierdź hasło", + "confirm_password_placeholder": "Potwierdź hasło", + "register_button": "Utwórz konto", + "have_account": "Masz już konto?", + "login": "Zaloguj się", + "setup_title": "Konfiguracja początkowa", + "setup_step1": "Administrator", + "setup_step2": "System", + "setup_step3": "Gotowe", + "admin_username": "Nazwa administratora", + "admin_email": "E-mail administratora", + "admin_password": "Hasło administratora", + "create_admin": "Utwórz administratora", + "back_to_login": "Już skonfigurowane?", + "admin_success": "Konto administratora zostało utworzone! Możesz się teraz zalogować.", + "account_success": "Konto utworzone pomyślnie! Możesz się teraz zalogować.", + "passwords_mismatch": "Hasła nie są zgodne", + "admin_create_error": "Błąd podczas tworzenia konta administratora", + "or": "lub", + "sso_login": "Zaloguj się przez SSO", + "sso_login_provider": "Zaloguj się przez {{provider}}", + "magicLinkHint": "Brak hasła? Wpisz swój adres e-mail, a wyślemy Ci jednorazowy link do logowania.", + "magicLinkEmailLabel": "Adres e-mail", + "magicLinkEmailPlaceholder": "ty@przyklad.pl", + "magicLinkSubmit": "Wyślij link do logowania", + "magicLinkSent": "Jeśli konto dla tego adresu istnieje, link do logowania został wysłany. Sprawdź swoją skrzynkę odbiorczą.", + "magicLinkUnavailable": "Logowanie e-mailem nie jest dostępne na tym serwerze.", + "magicLinkNetworkError": "Nie udało się połączyć z serwerem: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "Pamięć masowa", + "calculating": "Obliczanie...", + "used": "{{percentage}}% wykorzystane ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Tego typu pliku nie można wyświetlić.", + "download_file": "Pobierz plik", + "zoom_in": "Przybliż", + "zoom_out": "Oddal", + "zoom_reset": "Resetuj powiększenie" + }, + "language_selector": { + "title": "Witaj!", + "subtitle": "Wybierz język, aby kontynuować", + "continue": "Kontynuuj", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Brak ulubionych", + "empty_hint": "Oznacz pliki lub foldery gwiazdką, aby dodać je do ulubionych", + "add": "Dodaj do ulubionych", + "remove": "Usuń z ulubionych", + "added_title": "Dodano do ulubionych", + "added_msg": "dodano do ulubionych", + "removed_title": "Usunięto z ulubionych", + "removed_msg": "usunięto z ulubionych" + }, + "recent": { + "title": "Ostatnie", + "clear": "Wyczyść ostatnie", + "accessed": "Otwarte", + "empty_state": "Brak ostatnich plików", + "empty_hint": "Otwarte pliki pojawią się tutaj", + "loadMore": "Załaduj więcej" + }, + "notifications": { + "file_renamed": "Zmieniono nazwę pliku", + "file_renamed_to": "Nazwa pliku zmieniona na \"{{name}}\"", + "folder_renamed": "Zmieniono nazwę folderu", + "folder_renamed_to": "Nazwa folderu zmieniona na \"{{name}}\"", + "file_uploaded": "Plik przesłany", + "file_deleted": "Plik przeniesiony do kosza", + "folder_deleted": "Folder przeniesiony do kosza", + "item_deleted_permanently": "Element trwale usunięty", + "trash_emptied": "Kosz został opróżniony", + "title": "Powiadomienia", + "empty": "Brak powiadomień", + "link_created": "Link utworzony", + "share_success": "Link udostępniania utworzony pomyślnie", + "upload_files_section_title": "Przesyłanie niedostępne tutaj", + "upload_files_section_body": "Przejdź do sekcji Pliki, aby przesłać pliki" + }, + "batch": { + "one_selected": "Wybrano 1 element", + "n_selected": "Wybrano {{count}} elementów", + "confirm_delete": "Czy na pewno chcesz przenieść {{count}} elementów do kosza?", + "move_title": "Przenieś {{count}} element(ów)", + "add_favorites": "Dodaj do ulubionych", + "move_copy": "Przenieś lub kopiuj" + }, + "admin": { + "page_title": "Panel administratora", + "back_to_app": "Powrót do OxiCloud", + "loading": "Ładowanie…", + "access_denied": "Dostęp zabroniony", + "access_denied_desc": "Aby uzyskać dostęp do tego panelu, wymagane są uprawnienia administratora.", + "sign_in": "Zaloguj się", + "tab_dashboard": "Panel", + "tab_users": "Użytkownicy", + "tab_oidc": "SSO / OIDC", + "total_users": "Wszyscy użytkownicy", + "active_users": "Aktywni użytkownicy", + "admins": "Administratorzy", + "version": "Wersja", + "storage_overview": "Przegląd pamięci masowej", + "used": "Wykorzystane", + "total_quota": "Łączny przydział", + "usage_pct": "Wykorzystanie %", + "users_over_80": "Użytkownicy >80% przydziału", + "users_over_quota": "Użytkownicy powyżej przydziału", + "system": "System", + "auth_label": "Uwierzytelnianie", + "oidc_label": "OIDC", + "quotas_label": "Przydziały", + "enabled": "Włączone", + "disabled": "Wyłączone", + "active": "Aktywne", + "off": "Wyłączone", + "allow_registration": "Zezwalaj na publiczną samodzielną rejestrację", + "registration_warning": "Publiczna rejestracja jest wyłączona. Tylko administratorzy mogą tworzyć nowych użytkowników.", + "user_management": "Zarządzanie użytkownikami", + "create_user": "Utwórz użytkownika", + "col_user": "Użytkownik", + "col_role": "Rola", + "col_auth": "Uwierzytelnianie", + "col_status": "Status", + "col_storage": "Pamięć masowa", + "col_last_login": "Ostatnie logowanie", + "col_actions": "Akcje", + "loading_users": "Ładowanie użytkowników…", + "failed_load_users": "Nie udało się załadować użytkowników", + "no_users_found": "Nie znaleziono użytkowników", + "showing_users": "Wyświetlanie {{from}}-{{to}} z {{total}}", + "prev": "Poprzednia", + "next": "Następna", + "inactive": "Nieaktywny", + "you_badge": "(ty)", + "local": "Lokalny", + "never": "Nigdy", + "just_now": "Przed chwilą", + "minutes_ago": "{{n}} min temu", + "hours_ago": "{{n}} godz. temu", + "days_ago": "{{n}} dni temu", + "edit_quota_title": "Edytuj przydział", + "reset_password_title": "Resetuj hasło", + "toggle_role_title": "Przełącz rolę", + "deactivate_title": "Dezaktywuj", + "activate_title": "Aktywuj", + "delete_title": "Usuń", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "Włącz uwierzytelnianie SSO", + "provider_name": "Nazwa dostawcy", + "issuer_url": "URL wystawcy", + "issuer_url_hint": "URL wystawcy OpenID Connect Twojego dostawcy tożsamości", + "auto_discover": "Automatyczne wykrywanie", + "discovering": "Wykrywanie…", + "client_id": "ID klienta", + "client_secret": "Sekret klienta", + "client_secret_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", + "secret_configured": "Sekret klienta jest już skonfigurowany", + "callback_url": "URL zwrotny", + "callback_url_hint": "(zarejestruj w swoim IdP)", + "advanced_settings": "Ustawienia zaawansowane", + "scopes": "Zakresy", + "auto_provision": "Automatycznie twórz użytkowników przy pierwszym logowaniu", + "admin_groups": "Grupy administratorów", + "admin_groups_hint": "Lista nazw grup OIDC oddzielonych przecinkami, mapowanych na rolę administratora", + "disable_password": "Wyłącz logowanie hasłem (tylko OIDC)", + "password_warning": "To uniemożliwi WSZYSTKIE logowania hasłem!", + "test_btn": "Testuj", + "save_btn": "Zapisz", + "saving": "Zapisywanie…", + "settings_saved": "Ustawienia zapisane — OIDC jest teraz {{status}}", + "quota_modal_title": "Aktualizuj przydział pamięci masowej", + "quota_user_label": "Użytkownik:", + "new_quota": "Nowy przydział", + "quota_unlimited_hint": "Ustaw 0 dla nieograniczonego", + "cancel": "Anuluj", + "create_user_title": "Utwórz nowego użytkownika", + "username_label": "Nazwa użytkownika", + "username_placeholder": "jankowalski", + "username_hint": "3–32 znaków", + "password_label": "Hasło", + "password_placeholder": "Min. 8 znaków", + "email_label": "E-mail", + "email_optional": "(opcjonalnie)", + "email_placeholder": "uzytkownik@example.com (generowany automatycznie, jeśli puste)", + "role_label": "Rola", + "role_user": "Użytkownik", + "role_admin": "Administrator", + "quota_label": "Przydział", + "creating": "Tworzenie…", + "reset_pw_title": "Resetuj hasło", + "new_password_label": "Nowe hasło", + "resetting": "Resetowanie…", + "reset_btn": "Resetuj", + "confirm_role_change": "Zmienić rolę na {{role}}?", + "confirm_deactivate": "Czy na pewno chcesz dezaktywować tego użytkownika?", + "confirm_activate": "Czy na pewno chcesz aktywować tego użytkownika?", + "confirm_delete_user": "USUNĄĆ użytkownika \"{{name}}\"? Tej akcji nie można cofnąć!", + "confirm_action": "Potwierdź akcję", + "confirm_yes": "Potwierdź", + "confirm_no": "Anuluj", + "error_username_short": "Nazwa użytkownika musi mieć co najmniej 3 znaki", + "error_password_short": "Hasło musi mieć co najmniej 8 znaków", + "error_generic": "Niepowodzenie", + "error_network": "Błąd sieci: {{message}}", + "error_create_user": "Nie udało się utworzyć użytkownika", + "tab_storage": "Pamięć masowa", + "storage_title": "Backend pamięci masowej", + "storage_current_backend": "Aktywny backend", + "storage_total_blobs": "Liczba blobów", + "storage_total_size": "Łączny rozmiar", + "storage_dedup_ratio": "Współczynnik deduplikacji", + "storage_backend": "Typ backendu", + "storage_local": "Lokalny system plików", + "storage_s3": "Kompatybilny z S3", + "storage_provider_preset": "Ustawienia dostawcy", + "storage_preset_custom": "Niestandardowy", + "storage_endpoint_url": "URL endpointu", + "storage_endpoint_hint": "Pozostaw puste dla domyślnego Amazon S3", + "storage_bucket": "Bucket", + "storage_region": "Region", + "storage_access_key": "Access Key ID", + "storage_secret_key": "Secret Access Key", + "storage_secret_configured": "Klucz tajny jest już skonfigurowany", + "storage_key_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", + "storage_path_style": "Wymuś styl ścieżki", + "storage_path_style_hint": "Wymagane dla MinIO i niektórych dostawców kompatybilnych z S3", + "storage_test_connection": "Testuj połączenie", + "storage_test_success": "Połączenie udane", + "storage_test_failure": "Połączenie nieudane", + "storage_save": "Zapisz", + "storage_saved": "Ustawienia pamięci masowej zapisane pomyślnie", + "storage_migration": "Migracja backendu", + "storage_migration_coming_soon": "Migracja backendu będzie dostępna w przyszłej aktualizacji.", + "migration_status_label": "Status:", + "migration_start": "Rozpocznij migrację", + "migration_pause": "Wstrzymaj", + "migration_resume": "Wznów", + "migration_verify": "Sprawdź integralność", + "migration_complete": "Sfinalizuj", + "migration_started": "Migracja rozpoczęta", + "migration_paused_msg": "Migracja wstrzymana", + "migration_resumed_msg": "Migracja wznowiona", + "migration_completed_msg": "Migracja sfinalizowana. Uruchom ponownie serwer, aby użyć nowego backendu.", + "migration_verifying": "Weryfikowanie…", + "migration_verify_passed": "Weryfikacja zaliczona", + "migration_verify_failed": "Weryfikacja nieudana", + "migration_failed_blobs": "nieudane bloby", + "testing": "Testowanie…", + "smtp_disabled": "Wyłączone (host nieustawiony)", + "smtp_enabled": "Włączone", + "smtp_enabled_label": "Status", + "smtp_intro": "SMTP jest konfigurowany wyłącznie przez zmienne środowiskowe (OXICLOUD_SMTP_*). Poniższe wartości są odczytywane z działającego serwera — aby je zmienić, zmodyfikuj środowisko i uruchom ponownie OxiCloud.", + "smtp_not_configured": "SMTP nie jest skonfigurowany na tym serwerze.", + "smtp_send_failed": "Wysłanie nie powiodło się.", + "smtp_send_test": "Wyślij e-mail testowy", + "smtp_sending": "Wysyłanie…", + "smtp_sent": "E-mail testowy wysłany.", + "smtp_server_code": "Odpowiedź serwera", + "smtp_test_intro": "Wysyła wstępnie zdefiniowaną wiadomość diagnostyczną do podanego poniżej odbiorcy i raportuje odpowiedź serwera SMTP, abyś mógł skorelować ją z logami swojego przekaźnika.", + "smtp_test_missing_to": "Wprowadź adres odbiorcy.", + "smtp_test_title": "Wyślij e-mail testowy", + "smtp_test_to": "Adres odbiorcy", + "smtp_title": "Poczta wychodząca (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Profil", + "back_to_app": "Powrót do OxiCloud", + "loading": "Ładowanie…", + "not_authenticated": "Nieuwierzytelniony", + "not_authenticated_desc": "Zaloguj się, aby zobaczyć swój profil.", + "sign_in": "Zaloguj się", + "role_admin": "Administrator", + "role_user": "Użytkownik", + "account_details": "Szczegóły konta", + "username": "Nazwa użytkownika", + "email": "E-mail", + "role": "Rola", + "last_login": "Ostatnie logowanie", + "storage": "Pamięć masowa", + "used": "Wykorzystane", + "quota": "Przydział", + "usage": "Wykorzystanie", + "unlimited": "Nieograniczone", + "app_passwords": "Hasła aplikacji", + "app_pw_desc": "Generuj hasła dla klientów WebDAV, CalDAV i CardDAV. Każde hasło jest wyświetlane tylko raz.", + "app_pw_label_placeholder": "Etykieta (np. Thunderbird, macOS)", + "generate": "Wygeneruj", + "generating": "Generowanie…", + "new_password_for": "Nowe hasło dla", + "copy_warning": "Skopiuj to hasło teraz. Nie zobaczysz go ponownie.", + "copy_to_clipboard": "Kopiuj do schowka", + "col_label": "Etykieta", + "col_created": "Utworzone", + "col_last_used": "Ostatnio używane", + "col_status": "Status", + "active": "Aktywne", + "revoked": "Unieważnione", + "revoke_title": "Unieważnij", + "no_app_passwords": "Brak haseł aplikacji.", + "client_sessions": "Sesje klientów", + "client_sessions_desc": "Generowane automatycznie po połączeniu z klientem kompatybilnym z Nextcloud.", + "col_client": "Klient", + "never": "Nigdy", + "just_now": "Przed chwilą", + "minutes_ago": "{{n}} min temu", + "hours_ago": "{{n}} godz. temu", + "days_ago": "{{n}} dni temu", + "edit_profile": "Edytuj profil", + "edit_oidc_managed": "Aby zmienić swoje dane (nazwisko, imię, zdjęcie profilowe, …), zaktualizuj je u swojego dostawcy tożsamości. Twoje zmiany pojawią się przy następnym logowaniu.", + "username_claim_hint": "2–64 znaki, litery / cyfry / kropka / myślnik / podkreślenie. Po wybraniu nazwy użytkownika nie można jej zmienić (klienty DAV/NextCloud są od niej zależne).", + "username_already_claimed": "Nazwa użytkownika jest ustawiona i nie może być zmieniona (klienty DAV/NextCloud są od niej zależne).", + "given_name": "Imię", + "family_name": "Nazwisko", + "notify_on_share": "Wyślij mi e-mail, gdy ktoś coś mi udostępni", + "notify_on_share_hint": "Gdy odznaczone, udostępnienia nadal pojawiają się na Twoim koncie — po prostu nie otrzymasz o nich e-maila.", + "save_profile": "Zapisz zmiany", + "profile_saved": "Profil zaktualizowany", + "profile_no_changes": "Brak zmian do zapisania.", + "profile_save_failed": "Zapis nie powiódł się", + "username_taken_error": "Ta nazwa użytkownika jest już zajęta.", + "username_immutable_error": "Twoja nazwa użytkownika jest już ustawiona i nie można jej tutaj zmienić. Skontaktuj się z administratorem, jeśli chcesz ją zmienić.", + "change_password": "Zmień hasło", + "current_password": "Bieżące hasło", + "new_password": "Nowe hasło", + "min_8_chars": "Co najmniej 8 znaków", + "confirm_password": "Potwierdź nowe hasło", + "update_password": "Aktualizuj hasło", + "updating": "Aktualizowanie…", + "password_updated": "Hasło zaktualizowane pomyślnie", + "passwords_no_match": "Hasła nie są zgodne", + "password_too_short": "Hasło musi mieć co najmniej 8 znaków", + "password_change_failed": "Nie udało się zmienić hasła", + "error_network": "Błąd sieci: {{message}}", + "error_label_required": "Wprowadź etykietę", + "error_create_pw": "Nie udało się utworzyć hasła aplikacji", + "confirm_revoke": "Unieważnić hasło aplikacji \"{{label}}\"? Klienci używający tego hasła przestaną działać.", + "error_revoke": "Nie udało się unieważnić hasła aplikacji", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Przesyłanie...", + "files": "plików", + "complete": "{{count}} / {{total}} przesłano" + }, + "storage_quota_exceeded": "Przekroczono limit pamięci masowej", + "sharedwithme": { + "pageTitle": "Udostępnione dla mnie", + "pageDescription": "Pliki i foldery, które inni użytkownicy udostępnili Ci", + "emptyStateTitle": "Nic nie zostało Ci jeszcze udostępnione", + "emptyStateDesc": "Elementy udostępnione Ci przez innych użytkowników pojawią się tutaj", + "loadMore": "Załaduj więcej", + "sharedBy": "Udostępnione przez", + "colName": "Nazwa", + "colType": "Typ", + "colSharedBy": "Udostępnione przez", + "colDate": "Data udostępnienia", + "colPermissions": "Uprawnienia" + }, + "groupby": { + "none": "Brak", + "title": "Grupuj według", + "owner": "Właściciel", + "shareDate": "Data udostępnienia", + "type": "Typ", + "type.folders": "Foldery", + "accessedAt": "Data dostępu", + "modifiedAt": "Data modyfikacji", + "createdAt": "Data utworzenia", + "size": "Rozmiar", + "favoriteDate": "Data dodania do ulubionych", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nowe" + }, + "dateBucket": { + "today": "Dzisiaj", + "last7days": "Ostatnie 7 dni", + "last30days": "Ostatnie 30 dni" + }, + "groups": { + "title": "Zarządzaj grupami", + "create_button": "Utwórz grupę", + "create_dialog_title": "Nowa grupa", + "edit_dialog_title": "Zmień nazwę grupy", + "name_label": "Nazwa", + "name_placeholder": "inzynieria", + "description_label": "Opis (opcjonalny)", + "members_section": "Członkowie", + "add_member_placeholder": "Dodaj użytkownika lub grupę…", + "no_members": "Brak członków.", + "remove_member": "Usuń", + "delete_group": "Usuń grupę", + "delete_confirm": "Usunąć grupę „{name}\"? Uprawnienia odwołujące się do tej grupy zostaną cofnięte.", + "empty_state": "Brak grup.", + "load_more": "Załaduj więcej", + "back_to_list": "Wstecz", + "loading": "Ładowanie…", + "virtual_badge": "System", + "member_count_zero": "Brak członków", + "member_count_one": "1 członek", + "member_count_other": "{count} członków", + "delete_confirm_label": "Wpisz nazwę grupy, aby potwierdzić:", + "delete_confirm_mismatch": "Wpisz nazwę grupy dokładnie, aby potwierdzić.", + "virtual_internal_name": "Wewnętrzni", + "members_loading": "Ładowanie członków…", + "members_empty": "Brak członków", + "virtual_internal_explanation": "Każdy użytkownik wewnętrzny na tym serwerze" + }, + "myshares": { + "copyLink": "Skopiuj link", + "deleteLink": "Usuń link", + "notifyByEmail": "Powiadom e-mailem", + "notifyFailed": "Nie udało się wysłać powiadomienia.", + "notifyGroupMembers": "Powiadom członków grupy", + "notifyRateLimited": "Zbyt wiele powiadomień dla tego odbiorcy — spróbuj ponownie później.", + "removeAccess": "Usuń dostęp", + "resendInvitation": "Wyślij ponownie e-mail z zaproszeniem" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/pt.json b/frontend/static/locales/pt.json new file mode 100644 index 00000000..1e8b3657 --- /dev/null +++ b/frontend/static/locales/pt.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Este link de início de sessão já não é válido", + "expired_body": "O link pode ter expirado ou já ter sido usado. Podemos enviar-lhe um novo — chegará à sua caixa de entrada em alguns segundos.", + "resend_to": "Enviar um novo link para {{email}}", + "generic_unavailable": "Este link de início de sessão já não é válido. Pode já ter sido usado ou ter expirado. Solicite um novo link na página de início de sessão.", + "service_unavailable": "O início de sessão por magic link não está ativado neste servidor.", + "internal_error": "Ocorreu um erro ao iniciar sessão. Por favor, tente novamente.", + "resend_failure": "Ocorreu um erro ao enviar o link. Por favor, tente novamente.", + "cross_browser_title": "Continuar o início de sessão neste dispositivo?", + "cross_browser_body": "Abriu este link de início de sessão num navegador ou dispositivo diferente daquele em que o solicitou.", + "cross_browser_warning": "Se foi você quem solicitou este link, é seguro continuar. Caso contrário, feche esta página — clicar em Continuar iniciaria sessão de outra pessoa na sua conta.", + "cross_browser_continue": "Continuar e iniciar sessão", + "resend_confirmation_title": "Verifique a sua caixa de entrada", + "resend_confirmation_body": "Se o link de início de sessão pertencia a uma conta ativa, um novo link acaba de ser enviado. Por favor, verifique a sua caixa de entrada.", + "return_link": "Voltar ao OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", + "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra-o clicando no link abaixo:\n{{link}}\n\nO link é de uso único e expira em {{ttl_hours}} horas.\nSe não esperava este convite, pode ignorar esta mensagem.\n\n— OxiCloud" + }, + "login": { + "subject": "Iniciar sessão no OxiCloud", + "body": "Olá,\n\nUse o link abaixo para iniciar sessão no OxiCloud. O link é de uso único e expira em {{ttl_minutes}} minutos. Abra-o no mesmo dispositivo em que o solicitou.\n\n{{link}}\n\nSe não solicitou este link de início de sessão, pode ignorar esta mensagem — não é necessária qualquer outra ação.\n\n— OxiCloud" + }, + "kind_file": "ficheiro", + "kind_folder": "pasta", + "english_fallback_divider": "--- Versão em inglês abaixo ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", + "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra o OxiCloud para ver a sua nova partilha:\n{{login_link}}\n\nPode ter mais partilhas novas de {{inviter}} — inicie sessão para ver todos os itens partilhados consigo.\n\n— OxiCloud\n\nRecebeu esta mensagem porque tem uma conta OxiCloud e a preferência de notificação de partilhas está ativada. Pode desativá-la no seu perfil (Avisar-me por e-mail quando alguém compartilhar comigo)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Sistema de armazenamento em nuvem minimalista" + }, + "nav": { + "files": "Arquivos", + "shared": "Compartilhamentos", + "recent": "Recentes", + "favorites": "Favoritos", + "photos": "Fotos", + "music": "Música", + "trash": "Lixeira", + "sharedwithme": "Compartilhados comigo" + }, + "photos": { + "empty_state": "Nenhuma foto ainda", + "empty_hint": "Envie imagens ou vídeos para vê-los aqui", + "items_selected": "selecionados", + "view_daily": "Dia", + "view_monthly": "Mês", + "view_yearly": "Ano" + }, + "music": { + "create_playlist": "Criar Playlist", + "playlists": "Playlists", + "no_playlists": "Nenhuma playlist ainda", + "select_playlist": "Selecione uma playlist", + "select_hint": "Escolha uma playlist na barra lateral ou crie uma nova", + "add_tracks": "Adicionar Faixas", + "no_tracks": "Nenhuma faixa nesta playlist", + "unknown_artist": "Artista Desconhecido", + "unknown_title": "Desconhecido", + "confirm_delete": "Excluir esta playlist?", + "playlist_name": "Nome da playlist", + "create": "Criar", + "delete": "Excluir", + "share": "Compartilhar", + "edit": "Editar", + "play_all": "Reproduzir Tudo", + "shuffle": "Aleatório", + "repeat": "Repetir", + "repeat_one": "Repetir Uma", + "queue": "Fila", + "queue_empty": "Fila vazia", + "not_playing": "Não reproduzindo", + "play": "Reproduzir", + "pause": "Pausar", + "previous": "Anterior", + "next": "Próximo", + "volume": "Volume", + "mute": "Mudo", + "unmute": "Ativar som", + "title": "Título", + "artist": "Artista", + "album": "Álbum", + "tracks": "faixas", + "add": "Adicionar", + "added": "Adicionado!", + "added_to_playlist": "adicionado à playlist", + "add_to_playlist": "Adicionar à playlist", + "load_error": "Erro ao carregar playlists", + "add_error": "Não foi possível adicionar as faixas", + "no_playlists_yet": "Nenhuma playlist ainda. Crie uma primeiro!", + "selected_files": "Selecionados:", + "error": "Erro", + "search_audio": "Pesquisar ficheiros de áudio…", + "no_audio_files": "Nenhum ficheiro de áudio encontrado", + "selected": "selecionados", + "loading": "A carregar…", + "search_error": "Não foi possível carregar os ficheiros de áudio", + "adding": "A adicionar…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "Pesquisar arquivos...", + "new_folder": "Nova pasta", + "upload": "Enviar", + "upload_files": "Enviar arquivos", + "upload_folder": "Enviar pasta", + "upload.uploading": "Enviando...", + "upload.complete": "{count} / {total} enviados", + "upload.files": "arquivos", + "rename": "Renomear", + "move": "Mover para...", + "move_to": "Mover para", + "delete": "Excluir", + "download": "Baixar", + "view": "Visualizar", + "cancel": "Cancelar", + "confirm": "Confirmar", + "share": "Compartilhar", + "favorite": "Adicionar aos favoritos", + "unfavorite": "Remover dos favoritos", + "copy": "Copiar", + "notify": "Notificar", + "send": "Enviar", + "clear_recent": "Limpar recentes", + "logout": "Sair", + "create": "Criar", + "search_btn": "Pesquisar", + "close": "Fechar", + "delete_permanently": "Excluir permanentemente", + "empty_trash": "Esvaziar lixeira", + "open_parent_folder": "Ir para a pasta pai", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Aparência", + "about": "Sobre o OxiCloud", + "about_description": "Plataforma de armazenamento em nuvem construída com Rust e Arquitetura Limpa. Rápida, segura e privada.", + "admin_panel": "Painel de administração", + "profile": "Meu perfil", + "role_user": "Usuário", + "theme": { + "light": "Claro", + "dark": "Escuro", + "auto": "Como o sistema" + }, + "manage_groups": "Gerenciar grupos" + }, + "share": { + "dialogTitle": "Link de compartilhamento", + "linkLabel": "Link compartilhado:", + "copyLink": "Copiar", + "permissions": "Permissões:", + "permissionRead": "Leitura", + "permissionWrite": "Escrita", + "permissionReshare": "Recompartilhar", + "password": "Proteção por senha:", + "generatePassword": "Gerar", + "expiration": "Data de expiração:", + "update": "Atualizar compartilhamento", + "remove": "Remover compartilhamento", + "notifyTitle": "Enviar notificação", + "notifyEmailLabel": "Endereço de e-mail:", + "notifyMessageLabel": "Mensagem (opcional):", + "notifySend": "Enviar notificação", + "shareWithOthers": "Compartilhar com outros", + "sharePublicly": "Compartilhar publicamente", + "shareSettings": "Configurações de compartilhamento", + "shareCopied": "Link copiado para a área de transferência", + "shareCreated": "Link de compartilhamento criado com sucesso", + "shareUpdated": "Configurações de compartilhamento atualizadas", + "shareRemoved": "Compartilhamento removido com sucesso", + "inviteByEmail": "Convidar por e-mail — o convite será enviado", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Link de compartilhamento", + "share_linkLabel": "Link compartilhado:", + "share_copyLink": "Copiar", + "share_permissions": "Permissões:", + "share_permissionRead": "Leitura", + "share_permissionWrite": "Escrita", + "share_permissionReshare": "Recompartilhar", + "share_password": "Proteção por senha:", + "share_generatePassword": "Gerar", + "share_expiration": "Data de expiração:", + "share_update": "Atualizar compartilhamento", + "share_remove": "Remover compartilhamento", + "share_notifyTitle": "Enviar notificação", + "share_notifyEmailLabel": "Endereço de e-mail:", + "share_notifyMessageLabel": "Mensagem (opcional):", + "share_notifySend": "Enviar notificação", + "shared": { + "backToFiles": "Voltar aos arquivos", + "pageTitle": "Recursos compartilhados", + "pageDescription": "Gerencie seus arquivos e pastas compartilhados", + "filterType": "Tipo:", + "filterAll": "Todos", + "filterFiles": "Arquivos", + "filterFolders": "Pastas", + "sortBy": "Ordenar por:", + "sortByName": "Nome", + "sortByDate": "Data de compartilhamento", + "sortByExpiration": "Expiração", + "search": "Pesquisar", + "colName": "Nome", + "colType": "Tipo", + "colDateShared": "Data de compartilhamento", + "colExpiration": "Expiração", + "colPermissions": "Permissões", + "colPassword": "Senha", + "colActions": "Ações", + "emptyStateTitle": "Nenhum recurso compartilhado ainda", + "emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", + "goToFiles": "Ir para arquivos", + "typeFile": "Arquivo", + "typeFolder": "Pasta", + "noExpiration": "Sem expiração", + "hasPassword": "Sim", + "noPassword": "Não", + "editShare": "Editar compartilhamento", + "notifyShare": "Notificar alguém", + "copyLink": "Copiar link", + "removeShare": "Remover compartilhamento", + "linkCopied": "Link copiado para a área de transferência!", + "linkCopyFailed": "Falha ao copiar o link", + "itemUpdated": "Configurações de compartilhamento atualizadas", + "itemRemoved": "Compartilhamento removido com sucesso", + "invalidEmail": "Por favor, insira um endereço de e-mail válido", + "notificationSent": "Notificação enviada com sucesso", + "notificationFailed": "Falha ao enviar a notificação", + "shared_backToFiles": "Voltar aos arquivos", + "shared_pageTitle": "Recursos compartilhados", + "shared_pageDescription": "Gerencie seus arquivos e pastas compartilhados", + "shared_filterType": "Tipo:", + "shared_filterAll": "Todos", + "shared_filterFiles": "Arquivos", + "shared_filterFolders": "Pastas", + "shared_sortBy": "Ordenar por:", + "shared_sortByName": "Nome", + "shared_sortByDate": "Data de compartilhamento", + "shared_sortByExpiration": "Expiração", + "shared_search": "Pesquisar", + "shared_colName": "Nome", + "shared_colType": "Tipo", + "shared_colDateShared": "Data de compartilhamento", + "shared_colExpiration": "Expiração", + "shared_colPermissions": "Permissões", + "shared_colPassword": "Senha", + "shared_colActions": "Ações", + "shared_emptyStateTitle": "Nenhum recurso compartilhado ainda", + "shared_emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", + "shared_goToFiles": "Ir para arquivos", + "shared_typeFile": "Arquivo", + "shared_typeFolder": "Pasta", + "shared_noExpiration": "Sem expiração", + "shared_hasPassword": "Sim", + "shared_noPassword": "Não", + "shared_editShare": "Editar compartilhamento", + "shared_notifyShare": "Notificar alguém", + "shared_copyLink": "Copiar link", + "shared_removeShare": "Remover compartilhamento", + "shared_linkCopied": "Link copiado para a área de transferência!", + "shared_linkCopyFailed": "Falha ao copiar o link", + "shared_itemUpdated": "Configurações de compartilhamento atualizadas", + "shared_itemRemoved": "Compartilhamento removido com sucesso", + "shared_invalidEmail": "Por favor, insira um endereço de e-mail válido", + "shared_notificationSent": "Notificação enviada com sucesso", + "shared_notificationFailed": "Falha ao enviar a notificação" + }, + "files": { + "name": "Nome", + "type": "Tipo", + "size": "Tamanho", + "modified": "Modificado", + "no_files": "Nenhum arquivo nesta pasta", + "empty_hint": "Envie arquivos ou crie pastas para começar", + "loading": "Carregando arquivos…", + "view_grid": "Visualização em grade", + "view_list": "Visualização em lista", + "file_types": { + "document": "Documento", + "image": "Imagem", + "video": "Vídeo", + "audio": "Áudio", + "pdf": "PDF", + "text": "Texto", + "folder": "Pasta", + "spreadsheet": "Planilha", + "presentation": "Apresentação", + "archive": "Arquivo compactado", + "installer": "Instalador", + "code": "Código" + }, + "owner": "Proprietário" + }, + "dialogs": { + "rename_folder": "Renomear pasta", + "rename_file": "Renomear arquivo", + "new_name": "Novo nome", + "new_folder_title": "Nova pasta", + "folder_name": "Nome da pasta", + "folder_placeholder": "Minha pasta", + "rename_title": "Renomear", + "move_file": "Mover arquivo", + "move_folder": "Mover pasta", + "select_destination": "Selecione a pasta de destino:", + "root": "Raiz", + "delete_confirmation": "Tem certeza de que deseja excluir", + "and_contents": "e todo o seu conteúdo", + "no_undo": "Esta ação não pode ser desfeita", + "confirm_title": "Confirmar ação", + "confirm_delete": "Mover para a lixeira", + "confirm_delete_file": "Tem certeza de que deseja mover o arquivo \"{{name}}\" para a lixeira?", + "confirm_delete_folder": "Tem certeza de que deseja mover a pasta \"{{name}}\" e todo o seu conteúdo para a lixeira?", + "confirm_permanent_delete": "Excluir permanentemente", + "confirm_permanent_delete_msg": "Tem certeza de que deseja excluir permanentemente este item? Esta ação não pode ser desfeita.", + "confirm_empty_trash": "Esvaziar lixeira", + "confirm_delete_share": "Excluir link de compartilhamento", + "confirm_delete_share_msg": "Tem certeza de que deseja excluir este link de compartilhamento?", + "share_file": "Compartilhar arquivo", + "share_folder": "Compartilhar pasta", + "existing_shares": "Compartilhamentos existentes", + "share_options": "Opções de compartilhamento", + "password": "Senha", + "expiration": "Expiração", + "permissions": "Permissões", + "generated_link": "Link gerado", + "notify": "Enviar notificação", + "recipient": "Destinatário", + "message": "Mensagem", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Mover para a pasta inicial" + }, + "dropzone": { + "drag_files": "Arraste arquivos aqui ou clique para selecionar", + "drop_files": "Solte os arquivos para enviar" + }, + "permissions": { + "read": "Leitura", + "write": "Escrita", + "reshare": "Recompartilhar" + }, + "errors": { + "file_not_found": "Arquivo não encontrado", + "folder_not_found": "Pasta não encontrada", + "delete_error": "Erro ao excluir", + "upload_error": "Erro ao enviar o arquivo", + "rename_error": "Erro ao renomear", + "move_error": "Erro ao mover", + "empty_name": "O nome não pode estar vazio", + "name_exists": "Já existe um arquivo ou pasta com esse nome", + "generic_error": "Ocorreu um erro", + "group_name_invalid": "O nome do grupo deve seguir o formato de prefixo de e-mail (letras, dígitos, ponto, hífen, sublinhado; 1–64 caracteres).", + "group_cycle": "Este membro criaria uma referência circular entre grupos.", + "group_depth_exceeded": "Esta profundidade de aninhamento excede o máximo permitido (8).", + "group_virtual_immutable": "O grupo «Internal» é gerenciado pelo sistema e não pode ser modificado.", + "group_not_found": "Grupo não encontrado.", + "group_name_taken": "Já existe um grupo com este nome." + }, + "breadcrumb": { + "home": "Início" + }, + "trash": { + "empty_trash": "Esvaziar lixeira", + "empty_state": "A lixeira está vazia", + "original_location": "Local original", + "deleted_date": "Data de exclusão", + "remaining": "Restante", + "actions": "Ações", + "restore": "Restaurar", + "delete_permanently": "Excluir permanentemente", + "empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente.", + "groupby": { + "remaining_days": "Dias restantes", + "trashed_time": "Data de exclusão" + } + }, + "daysRemaining": { + "expired": "Expirado", + "today": "Hoje", + "tomorrow": "Amanhã", + "inDays": "{{count}} dias" + }, + "expiryChip": { + "never": "Nunca expira", + "expired": "Expirado", + "today": "Expira hoje", + "tomorrow": "Expira amanhã", + "inDays": "Expira em {{count}} dias", + "onDate": "Expira em {{date}}" + }, + "auth": { + "login_title": "Entrar", + "username": "Usuário", + "username_placeholder": "Digite seu nome de usuário", + "login_identifier": "Usuário ou e-mail", + "login_identifier_placeholder": "Digite seu usuário ou e-mail", + "password": "Senha", + "password_placeholder": "Digite sua senha", + "login_button": "Entrar", + "no_account": "Não tem uma conta?", + "register": "Cadastre-se", + "admin_setup": "Primeira vez?", + "setup": "Configurar administrador", + "register_title": "Criar conta", + "email": "E-mail", + "email_placeholder": "Digite seu e-mail", + "confirm_password": "Confirmar senha", + "confirm_password_placeholder": "Confirme sua senha", + "register_button": "Criar conta", + "have_account": "Já tem uma conta?", + "login": "Entrar", + "setup_title": "Configuração inicial", + "setup_step1": "Admin", + "setup_step2": "Sistema", + "setup_step3": "Concluído", + "admin_username": "Usuário administrador", + "admin_email": "E-mail do administrador", + "admin_password": "Senha do administrador", + "create_admin": "Criar administrador", + "back_to_login": "Já configurado?", + "admin_success": "Conta de administrador criada com sucesso! Agora você pode entrar.", + "account_success": "Conta criada com sucesso! Agora você pode entrar.", + "passwords_mismatch": "As senhas não coincidem", + "admin_create_error": "Erro ao criar conta de administrador", + "or": "ou", + "sso_login": "Entrar com SSO", + "sso_login_provider": "Entrar com {{provider}}", + "magicLinkHint": "Sem senha? Digite seu e-mail e enviaremos um link de acesso único.", + "magicLinkEmailLabel": "Endereço de e-mail", + "magicLinkEmailPlaceholder": "voce@exemplo.com", + "magicLinkSubmit": "Enviar link de acesso", + "magicLinkSent": "Se existir uma conta para este e-mail, um link de acesso foi enviado. Verifique sua caixa de entrada.", + "magicLinkUnavailable": "O acesso por e-mail não está disponível neste servidor.", + "magicLinkNetworkError": "Não foi possível conectar ao servidor: {{message}}", + "magicLinkToggle": "Sem palavra-passe? Receba um link por e-mail", + "passwordsMatch": "As palavras-passe coincidem", + "capsLock": "Caps Lock ativado" + }, + "storage": { + "title": "Armazenamento", + "calculating": "Calculando...", + "used": "{{percentage}}% usado ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Este tipo de arquivo não pode ser visualizado.", + "download_file": "Baixar arquivo", + "zoom_in": "Ampliar", + "zoom_out": "Reduzir", + "zoom_reset": "Redefinir zoom" + }, + "language_selector": { + "title": "Bem-vindo!", + "subtitle": "Selecione seu idioma para continuar", + "continue": "Continuar", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Nenhum favorito ainda", + "empty_hint": "Marque arquivos ou pastas com estrela para adicioná-los aos seus favoritos", + "add": "Adicionar aos favoritos", + "remove": "Remover dos favoritos", + "added_title": "Adicionado aos favoritos", + "added_msg": "adicionado aos favoritos", + "removed_title": "Removido dos favoritos", + "removed_msg": "removido dos favoritos" + }, + "recent": { + "title": "Recentes", + "clear": "Limpar recentes", + "accessed": "Acessado", + "empty_state": "Nenhum arquivo recente", + "empty_hint": "Os arquivos que você abrir aparecerão aqui", + "loadMore": "Carregar mais" + }, + "notifications": { + "file_renamed": "Arquivo renomeado", + "file_renamed_to": "Arquivo renomeado para \"{{name}}\"", + "folder_renamed": "Pasta renomeada", + "folder_renamed_to": "Pasta renomeada para \"{{name}}\"", + "file_uploaded": "Arquivo enviado", + "file_deleted": "Arquivo movido para a lixeira", + "folder_deleted": "Pasta movida para a lixeira", + "item_deleted_permanently": "Item excluído permanentemente", + "trash_emptied": "Lixeira esvaziada com sucesso", + "empty": "No notifications", + "title": "Notifications", + "link_created": "Link criado", + "share_success": "Link de partilha criado com sucesso", + "upload_files_section_title": "Upload não disponível aqui", + "upload_files_section_body": "Vá para a seção Arquivos para enviar arquivos" + }, + "batch": { + "one_selected": "1 item selecionado", + "n_selected": "{{count}} itens selecionados", + "confirm_delete": "Tem certeza de que deseja mover {{count}} itens para a lixeira?", + "move_title": "Mover {{count}} item(ns)", + "add_favorites": "Adicionar aos favoritos", + "move_copy": "Mover ou copiar" + }, + "admin": { + "page_title": "Painel de Administração", + "back_to_app": "Voltar ao OxiCloud", + "loading": "Carregando…", + "access_denied": "Acesso Negado", + "access_denied_desc": "Privilégios de administrador necessários.", + "sign_in": "Entrar", + "tab_dashboard": "Painel", + "tab_users": "Usuários", + "tab_oidc": "SSO / OIDC", + "total_users": "Total de Usuários", + "active_users": "Usuários Ativos", + "admins": "Admins", + "version": "Versão", + "storage_overview": "Visão do Armazenamento", + "used": "Usado", + "total_quota": "Cota Total", + "usage_pct": "Uso %", + "users_over_80": "Usuários >80% cota", + "users_over_quota": "Usuários acima da cota", + "system": "Sistema", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Cotas", + "enabled": "Habilitado", + "disabled": "Desabilitado", + "active": "Ativo", + "off": "Inativo", + "allow_registration": "Permitir registro público", + "registration_warning": "O registro público está desabilitado. Apenas administradores podem criar novos usuários.", + "user_management": "Gerenciamento de Usuários", + "create_user": "Criar Usuário", + "col_user": "Usuário", + "col_role": "Função", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Armazenamento", + "col_last_login": "Último Login", + "col_actions": "Ações", + "loading_users": "Carregando usuários…", + "failed_load_users": "Falha ao carregar", + "no_users_found": "Nenhum usuário encontrado", + "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", + "prev": "Anterior", + "next": "Próximo", + "inactive": "Inativo", + "you_badge": "(você)", + "local": "Local", + "never": "Nunca", + "just_now": "Agora mesmo", + "minutes_ago": "{{n}}min atrás", + "hours_ago": "{{n}}h atrás", + "days_ago": "{{n}}d atrás", + "edit_quota_title": "Editar cota", + "reset_password_title": "Redefinir senha", + "toggle_role_title": "Alternar função", + "deactivate_title": "Desativar", + "activate_title": "Ativar", + "delete_title": "Excluir", + "sso_title": "Login Único (OIDC / SSO)", + "enable_sso": "Habilitar autenticação SSO", + "provider_name": "Nome do Provedor", + "issuer_url": "URL do Emissor", + "issuer_url_hint": "URL do emissor OpenID Connect", + "auto_discover": "Auto-descoberta", + "discovering": "Descobrindo…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Deixe vazio para manter o valor atual", + "secret_configured": "Um client secret já está configurado", + "callback_url": "URL de Callback", + "callback_url_hint": "(registrar no seu IdP)", + "advanced_settings": "Configurações Avançadas", + "scopes": "Scopes", + "auto_provision": "Provisionar usuários automaticamente", + "admin_groups": "Grupos de Admin", + "admin_groups_hint": "Nomes de grupos OIDC separados por vírgula", + "disable_password": "Desabilitar login por senha (apenas OIDC)", + "password_warning": "Isso impedirá TODOS os logins por senha!", + "test_btn": "Testar", + "save_btn": "Salvar", + "saving": "Salvando…", + "settings_saved": "Configurações salvas — OIDC agora está {{status}}", + "quota_modal_title": "Atualizar Cota", + "quota_user_label": "Usuário:", + "new_quota": "Nova Cota", + "quota_unlimited_hint": "0 para ilimitado", + "cancel": "Cancelar", + "create_user_title": "Criar Novo Usuário", + "username_label": "Nome de usuário", + "username_placeholder": "joaosilva", + "username_hint": "3–32 caracteres", + "password_label": "Senha", + "password_placeholder": "Mín 8 caracteres", + "email_label": "E-mail", + "email_optional": "(opcional)", + "email_placeholder": "usuario@exemplo.com (gerado automaticamente se vazio)", + "role_label": "Função", + "role_user": "Usuário", + "role_admin": "Admin", + "quota_label": "Cota", + "creating": "Criando…", + "reset_pw_title": "Redefinir Senha", + "new_password_label": "Nova Senha", + "resetting": "Redefinindo…", + "reset_btn": "Redefinir", + "confirm_role_change": "Alterar função para {{role}}?", + "confirm_deactivate": "Tem certeza de que deseja desativar este usuário?", + "confirm_activate": "Tem certeza de que deseja ativar este usuário?", + "confirm_delete_user": "EXCLUIR usuário \"{{name}}\"? Não pode ser desfeito!", + "confirm_action": "Confirmar Ação", + "confirm_yes": "Confirmar", + "confirm_no": "Cancelar", + "error_username_short": "O nome de usuário deve ter pelo menos 3 caracteres", + "error_password_short": "A senha deve ter pelo menos 8 caracteres", + "error_generic": "Falha", + "error_network": "Erro de rede: {{message}}", + "error_create_user": "Falha ao criar usuário", + "tab_storage": "Armazenamento", + "storage_title": "Configuração de armazenamento", + "storage_current_backend": "Backend atual", + "storage_total_blobs": "Total de blobs", + "storage_total_size": "Tamanho total", + "storage_dedup_ratio": "Taxa de deduplicação", + "storage_backend": "Backend", + "storage_local": "Local", + "storage_s3": "Compatível com S3", + "storage_provider_preset": "Predefinição do fornecedor", + "storage_preset_custom": "Personalizado", + "storage_endpoint_url": "URL do endpoint", + "storage_endpoint_hint": "Deixar em branco para AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Região", + "storage_access_key": "Chave de acesso", + "storage_secret_key": "Chave secreta", + "storage_secret_configured": "Chave configurada", + "storage_key_placeholder": "Introduzir nova chave", + "storage_path_style": "Forçar estilo de caminho", + "storage_path_style_hint": "Necessário para MinIO e alguns serviços compatíveis com S3", + "storage_test_connection": "Testar ligação", + "storage_test_success": "Ligação bem-sucedida", + "storage_test_failure": "Falha na ligação", + "storage_save": "Guardar configuração", + "storage_saved": "Configuração guardada", + "storage_migration": "Migração de dados", + "storage_migration_coming_soon": "Ferramentas de migração em breve", + "migration_status_label": "Estado da migração", + "migration_start": "Iniciar migração", + "migration_pause": "Pausar", + "migration_resume": "Retomar", + "migration_verify": "Verificar", + "migration_complete": "Concluir", + "migration_started": "Migração iniciada", + "migration_paused_msg": "Migração pausada", + "migration_resumed_msg": "Migração retomada", + "migration_completed_msg": "Migração concluída com sucesso", + "migration_verifying": "A verificar...", + "migration_verify_passed": "Verificação aprovada", + "migration_verify_failed": "Verificação falhou", + "migration_failed_blobs": "Blobs com falha", + "testing": "A testar...", + "smtp_disabled": "Desativado (host não configurado)", + "smtp_enabled": "Ativado", + "smtp_enabled_label": "Estado", + "smtp_intro": "SMTP é configurado exclusivamente através de variáveis de ambiente (OXICLOUD_SMTP_*). Os valores abaixo são lidos do servidor em execução — para alterá-los, edite o ambiente e reinicie o OxiCloud.", + "smtp_not_configured": "SMTP não está configurado neste servidor.", + "smtp_send_failed": "Falha no envio.", + "smtp_send_test": "Enviar e-mail de teste", + "smtp_sending": "A enviar…", + "smtp_sent": "E-mail de teste enviado.", + "smtp_server_code": "Resposta do servidor", + "smtp_test_intro": "Envia uma mensagem de diagnóstico pré-definida para o destinatário abaixo e reporta a resposta do servidor SMTP, para que possa correlacioná-la com os registos do seu relay.", + "smtp_test_missing_to": "Introduza um endereço de destinatário.", + "smtp_test_title": "Enviar um e-mail de teste", + "smtp_test_to": "Endereço do destinatário", + "smtp_title": "E-mail de saída (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Perfil", + "back_to_app": "Voltar ao OxiCloud", + "loading": "Carregando…", + "not_authenticated": "Não Autenticado", + "not_authenticated_desc": "Faça login para ver seu perfil.", + "sign_in": "Entrar", + "role_admin": "Administrador", + "role_user": "Usuário", + "account_details": "Detalhes da Conta", + "username": "Nome de usuário", + "email": "E-mail", + "role": "Função", + "last_login": "Último login", + "storage": "Armazenamento", + "used": "Usado", + "quota": "Cota", + "usage": "Uso", + "unlimited": "Ilimitado", + "app_passwords": "Senhas de Aplicativo", + "app_pw_desc": "Gere senhas para clientes WebDAV, CalDAV e CardDAV. Cada senha é exibida apenas uma vez.", + "app_pw_label_placeholder": "Rótulo (ex. Thunderbird, macOS)", + "generate": "Gerar", + "generating": "Gerando…", + "new_password_for": "Nova senha para", + "copy_warning": "Copie esta senha agora. Você não poderá vê-la novamente.", + "copy_to_clipboard": "Copiar para área de transferência", + "col_label": "Rótulo", + "col_created": "Criado", + "col_last_used": "Último uso", + "col_status": "Status", + "active": "Ativa", + "revoked": "Revogada", + "revoke_title": "Revogar", + "no_app_passwords": "Nenhuma senha de aplicativo ainda.", + "client_sessions": "Sessões de cliente", + "client_sessions_desc": "Geradas automaticamente ao conectar um cliente compatível com Nextcloud.", + "col_client": "Cliente", + "never": "Nunca", + "just_now": "Agora mesmo", + "minutes_ago": "{{n}} min atrás", + "hours_ago": "{{n}}h atrás", + "days_ago": "{{n}} dias atrás", + "edit_profile": "Editar perfil", + "edit_oidc_managed": "Para alterar suas informações (nome, sobrenome, foto de perfil, …), atualize-as no seu provedor de identidade. As mudanças aparecerão no próximo login.", + "username_claim_hint": "De 2 a 64 caracteres, letras / dígitos / ponto / hífen / sublinhado. Uma vez escolhido, o nome de usuário não pode ser alterado (clientes DAV/NextCloud dependem dele).", + "username_already_claimed": "Nome de usuário definido e não pode ser alterado (clientes DAV/NextCloud dependem dele).", + "given_name": "Nome", + "family_name": "Sobrenome", + "notify_on_share": "Avisar-me por e-mail quando alguém compartilhar comigo", + "notify_on_share_hint": "Quando desmarcado, os compartilhamentos continuarão aparecendo na sua conta — você apenas não receberá um e-mail sobre eles.", + "save_profile": "Salvar alterações", + "profile_saved": "Perfil atualizado", + "profile_no_changes": "Sem alterações para salvar.", + "profile_save_failed": "Falha ao salvar", + "username_taken_error": "Este nome de usuário já está em uso.", + "username_immutable_error": "Seu nome de usuário já está definido e não pode ser alterado aqui. Contate um administrador se desejar renomeá-lo.", + "change_password": "Alterar Senha", + "current_password": "Senha Atual", + "new_password": "Nova Senha", + "min_8_chars": "Pelo menos 8 caracteres", + "confirm_password": "Confirmar Nova Senha", + "update_password": "Atualizar Senha", + "updating": "Atualizando…", + "password_updated": "Senha atualizada com sucesso", + "passwords_no_match": "As senhas não coincidem", + "password_too_short": "A senha deve ter pelo menos 8 caracteres", + "password_change_failed": "Falha ao alterar a senha", + "error_network": "Erro de rede: {{message}}", + "error_label_required": "Digite um rótulo", + "error_create_pw": "Falha ao criar senha de aplicativo", + "confirm_revoke": "Revogar senha \"{{label}}\"? Clientes que usam esta senha deixarão de funcionar.", + "error_revoke": "Falha ao revogar", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "A carregar...", + "files": "ficheiros", + "complete": "{{count}} / {{total}} carregados" + }, + "storage_quota_exceeded": "Cota de armazenamento excedida", + "sharedwithme": { + "pageTitle": "Compartilhado comigo", + "pageDescription": "Arquivos e pastas que outros usuários compartilharam com você", + "emptyStateTitle": "Nada compartilhado com você ainda", + "emptyStateDesc": "Itens compartilhados com você por outros usuários aparecerão aqui", + "loadMore": "Carregar mais", + "sharedBy": "Compartilhado por", + "colName": "Nome", + "colType": "Tipo", + "colSharedBy": "Compartilhado por", + "colDate": "Data de compartilhamento", + "colPermissions": "Permissões" + }, + "groupby": { + "none": "Nenhum", + "title": "Agrupar por", + "owner": "Proprietário", + "shareDate": "Data de partilha", + "type": "Tipo", + "type.folders": "Pastas", + "accessedAt": "Data de acesso", + "modifiedAt": "Data de modificação", + "createdAt": "Data de criação", + "size": "Tamanho", + "favoriteDate": "Data de favorito", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Novo" + }, + "dateBucket": { + "today": "Hoje", + "last7days": "Últimos 7 dias", + "last30days": "Últimos 30 dias" + }, + "groups": { + "title": "Gerenciar grupos", + "create_button": "Criar grupo", + "create_dialog_title": "Novo grupo", + "edit_dialog_title": "Renomear grupo", + "name_label": "Nome", + "name_placeholder": "engenharia", + "description_label": "Descrição (opcional)", + "members_section": "Membros", + "add_member_placeholder": "Adicionar um usuário ou grupo…", + "no_members": "Ainda não há membros.", + "remove_member": "Remover", + "delete_group": "Excluir grupo", + "delete_confirm": "Excluir o grupo \"{name}\"? As concessões que referenciam este grupo serão revogadas.", + "empty_state": "Ainda não há grupos.", + "load_more": "Carregar mais", + "back_to_list": "Voltar", + "loading": "Carregando…", + "virtual_badge": "Sistema", + "member_count_zero": "Sem membros", + "member_count_one": "1 membro", + "member_count_other": "{count} membros", + "delete_confirm_label": "Digite o nome do grupo para confirmar:", + "delete_confirm_mismatch": "Digite o nome do grupo exatamente para confirmar.", + "virtual_internal_name": "Interno", + "members_loading": "A carregar membros…", + "members_empty": "Sem membros", + "virtual_internal_explanation": "Todos os utilizadores internos neste servidor" + }, + "myshares": { + "copyLink": "Copiar link", + "deleteLink": "Eliminar link", + "notifyByEmail": "Notificar por e-mail", + "notifyFailed": "Não foi possível enviar a notificação.", + "notifyGroupMembers": "Notificar membros do grupo", + "notifyRateLimited": "Demasiadas notificações para este destinatário — tente novamente mais tarde.", + "removeAccess": "Remover acesso", + "resendInvitation": "Reenviar e-mail de convite" + }, + "sort": { + "asc": "ascendente", + "desc": "descendente" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/ru.json b/frontend/static/locales/ru.json new file mode 100644 index 00000000..5f72caf0 --- /dev/null +++ b/frontend/static/locales/ru.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "Эта ссылка для входа больше не действительна", + "expired_body": "Срок действия ссылки мог истечь, или она уже была использована. Мы можем отправить вам новую — она придёт в ваш почтовый ящик через несколько секунд.", + "resend_to": "Отправить новую ссылку на {{email}}", + "generic_unavailable": "Эта ссылка для входа больше не действительна. Возможно, она уже использовалась или срок её действия истёк. Запросите новую ссылку на странице входа.", + "service_unavailable": "Вход по magic-ссылке не включён на этом сервере.", + "internal_error": "При входе произошла ошибка. Пожалуйста, попробуйте ещё раз.", + "resend_failure": "При отправке ссылки произошла ошибка. Пожалуйста, попробуйте ещё раз.", + "cross_browser_title": "Продолжить вход на этом устройстве?", + "cross_browser_body": "Вы открыли эту ссылку для входа в браузере или на устройстве, отличном от того, где она была запрошена.", + "cross_browser_warning": "Если эту ссылку запросили вы, можно безопасно продолжить. В противном случае закройте эту страницу — нажатие «Продолжить» приведёт к входу другого человека в вашу учётную запись.", + "cross_browser_continue": "Продолжить и войти", + "resend_confirmation_title": "Проверьте ваш почтовый ящик", + "resend_confirmation_body": "Если ссылка для входа принадлежала активной учётной записи, новая ссылка только что была отправлена. Пожалуйста, проверьте ваш почтовый ящик.", + "return_link": "Вернуться в OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", + "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте, нажав на ссылку ниже:\n{{link}}\n\nСсылка работает один раз и истекает через {{ttl_hours}} часов.\nЕсли вы не ожидали этого приглашения, можете спокойно проигнорировать это сообщение.\n\n— OxiCloud" + }, + "login": { + "subject": "Вход в OxiCloud", + "body": "Здравствуйте,\n\nИспользуйте ссылку ниже, чтобы войти в OxiCloud. Ссылка работает один раз и истекает через {{ttl_minutes}} минут. Откройте её на том же устройстве, где вы её запросили.\n\n{{link}}\n\nЕсли вы не запрашивали эту ссылку для входа, можете спокойно проигнорировать это сообщение — никаких дальнейших действий не требуется.\n\n— OxiCloud" + }, + "kind_file": "файл", + "kind_folder": "папку", + "english_fallback_divider": "--- Английская версия ниже ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", + "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте OxiCloud, чтобы увидеть новый общий ресурс:\n{{login_link}}\n\nВозможно, у вас есть и другие новые общие ресурсы от {{inviter}} — войдите, чтобы увидеть все элементы, которыми с вами поделились.\n\n— OxiCloud\n\nВы получаете это сообщение, потому что у вас есть учётная запись OxiCloud и предпочтение уведомлений об общих ресурсах включено. Вы можете отключить его в своём профиле (Уведомлять меня по электронной почте, когда кто-то делится со мной)." + } + } + }, + "app": { + "title": "OxiCloud", + "description": "Минималистичная система облачного хранения" + }, + "nav": { + "files": "Файлы", + "shared": "Общие", + "recent": "Недавние", + "favorites": "Избранное", + "photos": "Фото", + "music": "Музыка", + "trash": "Корзина", + "sharedwithme": "Доступно мне" + }, + "photos": { + "empty_state": "Фотографий пока нет", + "empty_hint": "Загрузите изображения или видео, чтобы увидеть их здесь", + "items_selected": "выбрано", + "view_daily": "День", + "view_monthly": "Месяц", + "view_yearly": "Год" + }, + "music": { + "create_playlist": "Создать плейлист", + "playlists": "Плейлисты", + "no_playlists": "Плейлистов пока нет", + "select_playlist": "Выберите плейлист", + "select_hint": "Выберите плейлист на панели слева или создайте новый", + "add_tracks": "Добавить треки", + "no_tracks": "В этом плейлисте нет треков", + "unknown_artist": "Неизвестный исполнитель", + "unknown_title": "Неизвестно", + "confirm_delete": "Удалить этот плейлист?", + "playlist_name": "Название плейлиста", + "create": "Создать", + "delete": "Удалить", + "share": "Поделиться", + "edit": "Редактировать", + "play_all": "Воспроизвести все", + "shuffle": "Перемешать", + "repeat": "Повтор", + "repeat_one": "Повторять один", + "queue": "Очередь", + "queue_empty": "Очередь пуста", + "not_playing": "Ничего не играет", + "play": "Воспроизвести", + "pause": "Пауза", + "previous": "Предыдущий", + "next": "Следующий", + "volume": "Громкость", + "mute": "Выключить звук", + "unmute": "Включить звук", + "title": "Название", + "artist": "Исполнитель", + "album": "Альбом", + "tracks": "треков", + "add": "Добавить", + "added": "Добавлено!", + "added_to_playlist": "добавлен в плейлист", + "add_to_playlist": "Добавить в плейлист", + "load_error": "Ошибка загрузки плейлистов", + "add_error": "Не удалось добавить треки в плейлист", + "no_playlists_yet": "Плейлистов пока нет. Создайте сначала!", + "selected_files": "Выбрано:", + "error": "Ошибка", + "search_audio": "Поиск аудиофайлов…", + "no_audio_files": "Аудиофайлы не найдены", + "selected": "выбрано", + "loading": "Загрузка…", + "search_error": "Не удалось загрузить аудиофайлы", + "adding": "Добавление…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "Поиск файлов...", + "new_folder": "Новая папка", + "upload": "Загрузить", + "upload_files": "Загрузить файлы", + "upload_folder": "Загрузить папку", + "upload.uploading": "Загрузка...", + "upload.complete": "{count} / {total} загружено", + "upload.files": "файлов", + "rename": "Переименовать", + "move": "Переместить в...", + "move_to": "Переместить в", + "delete": "Удалить", + "download": "Скачать", + "view": "Просмотр", + "cancel": "Отмена", + "confirm": "Подтвердить", + "share": "Поделиться", + "favorite": "В избранное", + "unfavorite": "Из избранного", + "copy": "Копировать", + "notify": "Уведомить", + "send": "Отправить", + "clear_recent": "Очистить недавние", + "logout": "Выйти", + "create": "Создать", + "search_btn": "Найти", + "close": "Закрыть", + "delete_permanently": "Удалить навсегда", + "empty_trash": "Очистить корзину", + "open_parent_folder": "Перейти в родительскую папку", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Оформление", + "about": "О OxiCloud", + "about_description": "Платформа облачного хранения на Rust с чистой архитектурой. Быстрая, безопасная и конфиденциальная.", + "admin_panel": "Панель администратора", + "profile": "Мой профиль", + "role_user": "Пользователь", + "theme": { + "light": "Светлая", + "dark": "Тёмная", + "auto": "Как в системе" + }, + "manage_groups": "Управление группами" + }, + "share": { + "dialogTitle": "Ссылка для обмена", + "linkLabel": "Ссылка:", + "copyLink": "Копировать", + "permissions": "Разрешения:", + "permissionRead": "Чтение", + "permissionWrite": "Запись", + "permissionReshare": "Пересылка", + "password": "Защита паролем:", + "generatePassword": "Сгенерировать", + "expiration": "Срок действия:", + "update": "Обновить общий доступ", + "remove": "Удалить общий доступ", + "notifyTitle": "Отправить уведомление", + "notifyEmailLabel": "Адрес email:", + "notifyMessageLabel": "Сообщение (необязательно):", + "notifySend": "Отправить уведомление", + "shareWithOthers": "Поделиться с другими", + "sharePublicly": "Общий доступ", + "shareSettings": "Настройки общего доступа", + "shareCopied": "Ссылка скопирована в буфер обмена", + "shareCreated": "Ссылка для общего доступа успешно создана", + "shareUpdated": "Настройки общего доступа успешно обновлены", + "shareRemoved": "Общий доступ успешно удалён", + "inviteByEmail": "Пригласить по e-mail — приглашение будет отправлено", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "Ссылка для обмена", + "share_linkLabel": "Ссылка:", + "share_copyLink": "Копировать", + "share_permissions": "Разрешения:", + "share_permissionRead": "Чтение", + "share_permissionWrite": "Запись", + "share_permissionReshare": "Пересылка", + "share_password": "Защита паролем:", + "share_generatePassword": "Сгенерировать", + "share_expiration": "Срок действия:", + "share_update": "Обновить общий доступ", + "share_remove": "Удалить общий доступ", + "share_notifyTitle": "Отправить уведомление", + "share_notifyEmailLabel": "Адрес email:", + "share_notifyMessageLabel": "Сообщение (необязательно):", + "share_notifySend": "Отправить уведомление", + "shared": { + "backToFiles": "Назад к файлам", + "pageTitle": "Общие ресурсы", + "pageDescription": "Управление общими файлами и папками", + "filterType": "Тип:", + "filterAll": "Все", + "filterFiles": "Файлы", + "filterFolders": "Папки", + "sortBy": "Сортировка:", + "sortByName": "Имя", + "sortByDate": "Дата", + "sortByExpiration": "Срок действия", + "search": "Поиск", + "colName": "Имя", + "colType": "Тип", + "colDateShared": "Дата общего доступа", + "colExpiration": "Срок действия", + "colPermissions": "Разрешения", + "colPassword": "Пароль", + "colActions": "Действия", + "emptyStateTitle": "Общих ресурсов пока нет", + "emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", + "goToFiles": "Перейти к файлам", + "typeFile": "Файл", + "typeFolder": "Папка", + "noExpiration": "Без срока", + "hasPassword": "Да", + "noPassword": "Нет", + "editShare": "Изменить общий доступ", + "notifyShare": "Уведомить", + "copyLink": "Копировать ссылку", + "removeShare": "Удалить общий доступ", + "linkCopied": "Ссылка скопирована в буфер обмена!", + "linkCopyFailed": "Не удалось скопировать ссылку", + "itemUpdated": "Настройки общего доступа обновлены", + "itemRemoved": "Общий доступ удалён", + "invalidEmail": "Укажите корректный адрес email", + "notificationSent": "Уведомление успешно отправлено", + "notificationFailed": "Не удалось отправить уведомление", + "shared_backToFiles": "Назад к файлам", + "shared_pageTitle": "Общие ресурсы", + "shared_pageDescription": "Управление общими файлами и папками", + "shared_filterType": "Тип:", + "shared_filterAll": "Все", + "shared_filterFiles": "Файлы", + "shared_filterFolders": "Папки", + "shared_sortBy": "Сортировка:", + "shared_sortByName": "Имя", + "shared_sortByDate": "Дата", + "shared_sortByExpiration": "Срок действия", + "shared_search": "Поиск", + "shared_colName": "Имя", + "shared_colType": "Тип", + "shared_colDateShared": "Дата общего доступа", + "shared_colExpiration": "Срок действия", + "shared_colPermissions": "Разрешения", + "shared_colPassword": "Пароль", + "shared_colActions": "Действия", + "shared_emptyStateTitle": "Общих ресурсов пока нет", + "shared_emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", + "shared_goToFiles": "Перейти к файлам", + "shared_typeFile": "Файл", + "shared_typeFolder": "Папка", + "shared_noExpiration": "Без срока", + "shared_hasPassword": "Да", + "shared_noPassword": "Нет", + "shared_editShare": "Изменить общий доступ", + "shared_notifyShare": "Уведомить", + "shared_copyLink": "Копировать ссылку", + "shared_removeShare": "Удалить общий доступ", + "shared_linkCopied": "Ссылка скопирована в буфер обмена!", + "shared_linkCopyFailed": "Не удалось скопировать ссылку", + "shared_itemUpdated": "Настройки общего доступа обновлены", + "shared_itemRemoved": "Общий доступ удалён", + "shared_invalidEmail": "Укажите корректный адрес email", + "shared_notificationSent": "Уведомление успешно отправлено", + "shared_notificationFailed": "Не удалось отправить уведомление" + }, + "files": { + "name": "Имя", + "type": "Тип", + "size": "Размер", + "modified": "Изменён", + "no_files": "В этой папке нет файлов", + "empty_hint": "Загрузите файлы или создайте папки, чтобы начать", + "loading": "Загрузка файлов…", + "view_grid": "Сетка", + "view_list": "Список", + "file_types": { + "document": "Документ", + "image": "Изображение", + "video": "Видео", + "audio": "Аудио", + "pdf": "PDF", + "text": "Текст", + "folder": "Папка", + "spreadsheet": "Таблица", + "presentation": "Презентация", + "archive": "Архив", + "installer": "Установщик", + "code": "Код" + }, + "owner": "Владелец" + }, + "dialogs": { + "rename_folder": "Переименовать папку", + "rename_file": "Переименовать файл", + "new_name": "Новое имя", + "new_folder_title": "Новая папка", + "folder_name": "Имя папки", + "folder_placeholder": "Моя папка", + "rename_title": "Переименовать", + "move_file": "Переместить файл", + "move_folder": "Переместить папку", + "select_destination": "Выберите папку назначения:", + "select_this_folder": "Выбрать эту папку", + "go_to_parent": ".. (родительская папка)", + "no_subfolders": "Нет подпапок", + "root": "Корень", + "delete_confirmation": "Вы уверены, что хотите удалить", + "and_contents": "и всё его содержимое", + "no_undo": "Это действие невозможно отменить", + "confirm_title": "Подтверждение действия", + "confirm_delete": "В корзину", + "confirm_delete_file": "Вы уверены, что хотите переместить файл \"{{name}}\" в корзину?", + "confirm_delete_folder": "Вы уверены, что хотите переместить папку \"{{name}}\" и всё её содержимое в корзину?", + "confirm_permanent_delete": "Удалить навсегда", + "confirm_permanent_delete_msg": "Вы уверены, что хотите навсегда удалить этот элемент? Это действие невозможно отменить.", + "confirm_empty_trash": "Очистить корзину", + "confirm_delete_share": "Удалить ссылку общего доступа", + "confirm_delete_share_msg": "Вы уверены, что хотите удалить эту ссылку общего доступа?", + "share_file": "Поделиться файлом", + "share_folder": "Поделиться папкой", + "existing_shares": "Существующие общие доступы", + "share_options": "Параметры общего доступа", + "password": "Пароль", + "expiration": "Срок действия", + "permissions": "Разрешения", + "generated_link": "Сгенерированная ссылка", + "notify": "Отправить уведомление", + "recipient": "Получатель", + "message": "Сообщение", + "move_to_home": "Переместить в домашнюю папку" + }, + "dropzone": { + "drag_files": "Перетащите файлы сюда или нажмите для выбора", + "drop_files": "Отпустите файлы для загрузки" + }, + "permissions": { + "read": "Чтение", + "write": "Запись", + "reshare": "Пересылка" + }, + "errors": { + "file_not_found": "Файл не найден", + "folder_not_found": "Папка не найдена", + "delete_error": "Ошибка удаления", + "upload_error": "Ошибка загрузки файла", + "rename_error": "Ошибка переименования", + "move_error": "Ошибка перемещения", + "empty_name": "Имя не может быть пустым", + "name_exists": "Файл или папка с таким именем уже существует", + "generic_error": "Произошла ошибка", + "group_name_invalid": "Имя группы должно соответствовать формату префикса эл. почты (буквы, цифры, точка, дефис, подчёркивание; 1–64 символов).", + "group_cycle": "Этот участник создаст циклическую ссылку между группами.", + "group_depth_exceeded": "Глубина вложенности превышает допустимый максимум (8).", + "group_virtual_immutable": "Группа «Internal» управляется системой и не может быть изменена.", + "group_not_found": "Группа не найдена.", + "group_name_taken": "Группа с таким именем уже существует." + }, + "breadcrumb": { + "home": "Главная" + }, + "trash": { + "empty_trash": "Очистить корзину", + "empty_state": "Корзина пуста", + "original_location": "Исходное расположение", + "deleted_date": "Дата удаления", + "remaining": "Осталось", + "actions": "Действия", + "restore": "Восстановить", + "delete_permanently": "Удалить навсегда", + "empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда.", + "groupby": { + "remaining_days": "Осталось дней", + "trashed_time": "Время удаления" + } + }, + "daysRemaining": { + "expired": "Истёк", + "today": "Сегодня", + "tomorrow": "Завтра", + "inDays": "{{count}} дн." + }, + "expiryChip": { + "never": "Никогда не истекает", + "expired": "Истёк", + "today": "Истекает сегодня", + "tomorrow": "Истекает завтра", + "inDays": "Истекает через {{count}} дн.", + "onDate": "Истекает {{date}}" + }, + "auth": { + "login_title": "Вход", + "username": "Имя пользователя", + "username_placeholder": "Введите имя пользователя", + "login_identifier": "Имя пользователя или email", + "login_identifier_placeholder": "Введите имя пользователя или email", + "password": "Пароль", + "password_placeholder": "Введите пароль", + "login_button": "Войти", + "no_account": "Нет аккаунта?", + "register": "Зарегистрироваться", + "admin_setup": "Первый запуск?", + "setup": "Настроить администратора", + "register_title": "Создание аккаунта", + "email": "Email", + "email_placeholder": "Введите email", + "confirm_password": "Подтвердите пароль", + "confirm_password_placeholder": "Подтвердите пароль", + "register_button": "Создать аккаунт", + "have_account": "Уже есть аккаунт?", + "login": "Войти", + "setup_title": "Начальная настройка", + "setup_step1": "Админ", + "setup_step2": "Система", + "setup_step3": "Готово", + "admin_username": "Имя администратора", + "admin_email": "Email администратора", + "admin_password": "Пароль администратора", + "create_admin": "Создать администратора", + "back_to_login": "Уже настроено?", + "admin_success": "Аккаунт администратора успешно создан! Теперь вы можете войти.", + "account_success": "Аккаунт успешно создан! Теперь вы можете войти.", + "passwords_mismatch": "Пароли не совпадают", + "admin_create_error": "Ошибка создания аккаунта администратора", + "or": "или", + "sso_login": "Войти через SSO", + "sso_login_provider": "Войти через {{provider}}", + "magicLinkHint": "Нет пароля? Введите ваш email, и мы пришлём вам одноразовую ссылку для входа.", + "magicLinkEmailLabel": "Адрес электронной почты", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "Отправить ссылку для входа", + "magicLinkSent": "Если для этого адреса существует учётная запись, ссылка для входа отправлена. Проверьте входящие.", + "magicLinkUnavailable": "Вход по электронной почте недоступен на этом сервере.", + "magicLinkNetworkError": "Не удалось подключиться к серверу: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "Хранилище", + "calculating": "Вычисление...", + "used": "{{percentage}}% использовано ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Предварительный просмотр этого типа файлов недоступен.", + "download_file": "Скачать файл", + "zoom_in": "Увеличить", + "zoom_out": "Уменьшить", + "zoom_reset": "Сбросить масштаб" + }, + "language_selector": { + "title": "Добро пожаловать!", + "subtitle": "Выберите язык для продолжения", + "continue": "Продолжить", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ru": "Русский", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands" + } + }, + "favorites": { + "empty_state": "Избранного пока нет", + "empty_hint": "Добавьте файлы или папки в избранное, нажав на звёздочку", + "add": "В избранное", + "remove": "Из избранного", + "added_title": "Добавлено в избранное", + "added_msg": "добавлено в избранное", + "removed_title": "Удалено из избранного", + "removed_msg": "удалено из избранного" + }, + "recent": { + "title": "Недавние", + "clear": "Очистить недавние", + "accessed": "Открыт", + "empty_state": "Нет недавних файлов", + "empty_hint": "Открытые вами файлы будут отображаться здесь", + "loadMore": "Загрузить ещё" + }, + "notifications": { + "file_renamed": "Файл переименован", + "file_renamed_to": "Файл переименован в \"{{name}}\"", + "folder_renamed": "Папка переименована", + "folder_renamed_to": "Папка переименована в \"{{name}}\"", + "file_uploaded": "Файл загружен", + "file_deleted": "Файл перемещён в корзину", + "folder_deleted": "Папка перемещена в корзину", + "item_deleted_permanently": "Элемент удалён навсегда", + "trash_emptied": "Корзина успешно очищена", + "title": "Уведомления", + "empty": "Нет уведомлений", + "link_created": "Ссылка создана", + "share_success": "Ссылка для общего доступа успешно создана", + "upload_files_section_title": "Загрузка здесь недоступна", + "upload_files_section_body": "Перейдите в раздел «Файлы», чтобы загрузить файлы" + }, + "batch": { + "one_selected": "Выбран 1 элемент", + "n_selected": "Выбрано {{count}} элементов", + "confirm_delete": "Вы уверены, что хотите переместить {{count}} элементов в корзину?", + "move_title": "Переместить {{count}} элементов", + "add_favorites": "В избранное", + "move_copy": "Переместить или копировать" + }, + "admin": { + "page_title": "Панель администратора", + "back_to_app": "Назад в OxiCloud", + "loading": "Загрузка…", + "access_denied": "Доступ запрещён", + "access_denied_desc": "Необходимы права администратора.", + "sign_in": "Войти", + "tab_dashboard": "Панель", + "tab_users": "Пользователи", + "tab_oidc": "SSO / OIDC", + "total_users": "Всего пользователей", + "active_users": "Активные", + "admins": "Администраторы", + "version": "Версия", + "storage_overview": "Обзор хранилища", + "used": "Использовано", + "total_quota": "Общая квота", + "usage_pct": "Использование %", + "users_over_80": "Пользователи >80%", + "users_over_quota": "Сверх квоты", + "system": "Система", + "auth_label": "Аутентификация", + "oidc_label": "OIDC", + "quotas_label": "Квоты", + "enabled": "Включено", + "disabled": "Отключено", + "active": "Активен", + "off": "Выкл", + "allow_registration": "Разрешить публичную регистрацию", + "registration_warning": "Публичная регистрация отключена. Только админы могут создавать пользователей.", + "user_management": "Управление пользователями", + "create_user": "Создать пользователя", + "col_user": "Пользователь", + "col_role": "Роль", + "col_auth": "Аутентификация", + "col_status": "Статус", + "col_storage": "Хранилище", + "col_last_login": "Последний вход", + "col_actions": "Действия", + "loading_users": "Загрузка пользователей…", + "failed_load_users": "Не удалось загрузить", + "no_users_found": "Пользователи не найдены", + "showing_users": "Показано {{from}}-{{to}} из {{total}}", + "prev": "Назад", + "next": "Далее", + "inactive": "Неактивен", + "you_badge": "(вы)", + "local": "Локальный", + "never": "Никогда", + "just_now": "Только что", + "minutes_ago": "{{n}} мин назад", + "hours_ago": "{{n}} ч назад", + "days_ago": "{{n}} дн назад", + "edit_quota_title": "Изменить квоту", + "reset_password_title": "Сбросить пароль", + "toggle_role_title": "Сменить роль", + "deactivate_title": "Деактивировать", + "activate_title": "Активировать", + "delete_title": "Удалить", + "sso_title": "Единый вход (OIDC / SSO)", + "enable_sso": "Включить SSO", + "provider_name": "Имя провайдера", + "issuer_url": "URL издателя", + "issuer_url_hint": "URL издателя OpenID Connect", + "auto_discover": "Авто-обнаружение", + "discovering": "Обнаружение…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Оставьте пустым для сохранения", + "secret_configured": "Client secret уже настроен", + "callback_url": "URL обратного вызова", + "callback_url_hint": "(зарегистрируйте в IdP)", + "advanced_settings": "Расширенные настройки", + "scopes": "Области", + "auto_provision": "Автоматически создавать пользователей", + "admin_groups": "Группы администраторов", + "admin_groups_hint": "Имена групп OIDC через запятую", + "disable_password": "Отключить вход по паролю (только OIDC)", + "password_warning": "Это заблокирует ВСЕ входы по паролю!", + "test_btn": "Тест", + "save_btn": "Сохранить", + "saving": "Сохранение…", + "settings_saved": "Настройки сохранены — OIDC теперь {{status}}", + "quota_modal_title": "Обновить квоту", + "quota_user_label": "Пользователь:", + "new_quota": "Новая квота", + "quota_unlimited_hint": "0 для безлимитного", + "cancel": "Отмена", + "create_user_title": "Создать пользователя", + "username_label": "Имя пользователя", + "username_placeholder": "ivanov", + "username_hint": "3–32 символа", + "password_label": "Пароль", + "password_placeholder": "Мин. 8 символов", + "email_label": "Эл. почта", + "email_optional": "(необязательно)", + "email_placeholder": "user@example.com (автоматически если пусто)", + "role_label": "Роль", + "role_user": "Пользователь", + "role_admin": "Админ", + "quota_label": "Квота", + "creating": "Создание…", + "reset_pw_title": "Сбросить пароль", + "new_password_label": "Новый пароль", + "resetting": "Сброс…", + "reset_btn": "Сбросить", + "confirm_role_change": "Изменить роль на {{role}}?", + "confirm_deactivate": "Деактивировать этого пользователя?", + "confirm_activate": "Активировать этого пользователя?", + "confirm_delete_user": "УДАЛИТЬ пользователя \"{{name}}\"? Нельзя отменить!", + "confirm_action": "Подтвердить", + "confirm_yes": "Подтвердить", + "confirm_no": "Отмена", + "error_username_short": "Имя минимум 3 символа", + "error_password_short": "Пароль минимум 8 символов", + "error_generic": "Ошибка", + "error_network": "Ошибка сети: {{message}}", + "error_create_user": "Не удалось создать", + "tab_storage": "Хранилище", + "storage_title": "Настройка хранилища", + "storage_current_backend": "Текущий бэкенд", + "storage_total_blobs": "Всего блобов", + "storage_total_size": "Общий размер", + "storage_dedup_ratio": "Коэффициент дедупликации", + "storage_backend": "Бэкенд", + "storage_local": "Локальный", + "storage_s3": "Совместимый с S3", + "storage_provider_preset": "Пресет провайдера", + "storage_preset_custom": "Пользовательский", + "storage_endpoint_url": "URL конечной точки", + "storage_endpoint_hint": "Оставьте пустым для AWS S3", + "storage_bucket": "Бакет", + "storage_region": "Регион", + "storage_access_key": "Ключ доступа", + "storage_secret_key": "Секретный ключ", + "storage_secret_configured": "Ключ настроен", + "storage_key_placeholder": "Введите новый ключ", + "storage_path_style": "Принудительный стиль пути", + "storage_path_style_hint": "Требуется для MinIO и некоторых S3-совместимых сервисов", + "storage_test_connection": "Проверить соединение", + "storage_test_success": "Соединение успешно", + "storage_test_failure": "Ошибка соединения", + "storage_save": "Сохранить конфигурацию", + "storage_saved": "Конфигурация сохранена", + "storage_migration": "Миграция данных", + "storage_migration_coming_soon": "Инструменты миграции скоро появятся", + "migration_status_label": "Статус миграции", + "migration_start": "Начать миграцию", + "migration_pause": "Пауза", + "migration_resume": "Возобновить", + "migration_verify": "Проверить", + "migration_complete": "Завершить", + "migration_started": "Миграция начата", + "migration_paused_msg": "Миграция приостановлена", + "migration_resumed_msg": "Миграция возобновлена", + "migration_completed_msg": "Миграция успешно завершена", + "migration_verifying": "Проверка...", + "migration_verify_passed": "Проверка пройдена", + "migration_verify_failed": "Проверка не пройдена", + "migration_failed_blobs": "Неудачные блобы", + "testing": "Тестирование...", + "smtp_disabled": "Отключено (хост не задан)", + "smtp_enabled": "Включено", + "smtp_enabled_label": "Статус", + "smtp_intro": "SMTP настраивается исключительно через переменные окружения (OXICLOUD_SMTP_*). Значения ниже считываются с работающего сервера — чтобы изменить их, отредактируйте окружение и перезапустите OxiCloud.", + "smtp_not_configured": "SMTP не настроен на этом сервере.", + "smtp_send_failed": "Сбой отправки.", + "smtp_send_test": "Отправить тестовое письмо", + "smtp_sending": "Отправка…", + "smtp_sent": "Тестовое письмо отправлено.", + "smtp_server_code": "Ответ сервера", + "smtp_test_intro": "Отправляет заранее заданное диагностическое сообщение указанному ниже получателю и сообщает ответ SMTP-сервера, чтобы вы могли сопоставить его с журналами вашего relay.", + "smtp_test_missing_to": "Введите адрес получателя.", + "smtp_test_title": "Отправить тестовое письмо", + "smtp_test_to": "Адрес получателя", + "smtp_title": "Исходящая почта (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "Профиль", + "back_to_app": "Назад в OxiCloud", + "loading": "Загрузка…", + "not_authenticated": "Не аутентифицирован", + "not_authenticated_desc": "Войдите, чтобы просмотреть свой профиль.", + "sign_in": "Войти", + "role_admin": "Администратор", + "role_user": "Пользователь", + "account_details": "Данные аккаунта", + "username": "Имя пользователя", + "email": "Эл. почта", + "role": "Роль", + "last_login": "Последний вход", + "storage": "Хранилище", + "used": "Использовано", + "quota": "Квота", + "usage": "Использование", + "unlimited": "Безлимитный", + "app_passwords": "Пароли приложений", + "app_pw_desc": "Создайте пароли для клиентов WebDAV, CalDAV и CardDAV. Каждый пароль показывается только один раз.", + "app_pw_label_placeholder": "Метка (напр. Thunderbird, macOS)", + "generate": "Создать", + "generating": "Создание…", + "new_password_for": "Новый пароль для", + "copy_warning": "Скопируйте пароль сейчас. Вы не сможете увидеть его снова.", + "copy_to_clipboard": "Копировать в буфер", + "col_label": "Метка", + "col_created": "Создан", + "col_last_used": "Последнее использование", + "col_status": "Статус", + "active": "Активен", + "revoked": "Отозван", + "revoke_title": "Отозвать", + "no_app_passwords": "Паролей приложений пока нет.", + "client_sessions": "Сессии клиентов", + "client_sessions_desc": "Автоматически создаются при подключении клиента, совместимого с Nextcloud.", + "col_client": "Клиент", + "never": "Никогда", + "just_now": "Только что", + "minutes_ago": "{{n}} мин назад", + "hours_ago": "{{n}} ч назад", + "days_ago": "{{n}} дн назад", + "edit_profile": "Редактировать профиль", + "edit_oidc_managed": "Чтобы изменить ваши данные (имя, фамилию, фотографию профиля, …), обновите их у вашего провайдера идентификации. Изменения появятся при следующем входе.", + "username_claim_hint": "2–64 символа, буквы / цифры / точка / дефис / подчёркивание. После выбора имя пользователя нельзя изменить (клиенты DAV/NextCloud зависят от него).", + "username_already_claimed": "Имя пользователя установлено и не может быть изменено (клиенты DAV/NextCloud зависят от него).", + "given_name": "Имя", + "family_name": "Фамилия", + "notify_on_share": "Уведомлять меня по электронной почте, когда кто-то делится со мной", + "notify_on_share_hint": "Если флажок снят, общие ресурсы по-прежнему будут отображаться в вашей учётной записи — вы просто не будете получать о них письма.", + "save_profile": "Сохранить изменения", + "profile_saved": "Профиль обновлён", + "profile_no_changes": "Нет изменений для сохранения.", + "profile_save_failed": "Не удалось сохранить", + "username_taken_error": "Это имя пользователя уже занято.", + "username_immutable_error": "Ваше имя пользователя уже установлено и не может быть изменено здесь. Свяжитесь с администратором, если нужно переименовать.", + "change_password": "Изменить пароль", + "current_password": "Текущий пароль", + "new_password": "Новый пароль", + "min_8_chars": "Минимум 8 символов", + "confirm_password": "Подтвердите новый пароль", + "update_password": "Обновить пароль", + "updating": "Обновление…", + "password_updated": "Пароль успешно обновлён", + "passwords_no_match": "Пароли не совпадают", + "password_too_short": "Пароль должен быть не менее 8 символов", + "password_change_failed": "Не удалось изменить пароль", + "error_network": "Ошибка сети: {{message}}", + "error_label_required": "Введите метку", + "error_create_pw": "Не удалось создать пароль", + "confirm_revoke": "Отозвать пароль «{{label}}»? Клиенты перестанут работать.", + "error_revoke": "Не удалось отозвать", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "upload": { + "uploading": "Загрузка...", + "files": "файлов", + "complete": "{{count}} / {{total}} загружено" + }, + "storage_quota_exceeded": "Превышена квота хранилища", + "sharedwithme": { + "pageTitle": "Доступно мне", + "pageDescription": "Файлы и папки, которые другие пользователи предоставили вам", + "emptyStateTitle": "Вам ещё ничего не предоставлено", + "emptyStateDesc": "Элементы, которые другие пользователи предоставят вам, появятся здесь", + "loadMore": "Загрузить ещё", + "sharedBy": "Предоставлено", + "colName": "Имя", + "colType": "Тип", + "colSharedBy": "Предоставлено", + "colDate": "Дата предоставления", + "colPermissions": "Права" + }, + "groupby": { + "none": "Нет", + "title": "Группировать по", + "owner": "Владелец", + "shareDate": "Дата общего доступа", + "type": "Тип", + "type.folders": "Папки", + "accessedAt": "Дата доступа", + "modifiedAt": "Дата изменения", + "createdAt": "Дата создания", + "size": "Размер", + "favoriteDate": "Дата добавления в избранное", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Новые" + }, + "dateBucket": { + "today": "Сегодня", + "last7days": "Последние 7 дней", + "last30days": "Последние 30 дней" + }, + "groups": { + "title": "Управление группами", + "create_button": "Создать группу", + "create_dialog_title": "Новая группа", + "edit_dialog_title": "Переименовать группу", + "name_label": "Имя", + "name_placeholder": "инженеры", + "description_label": "Описание (необязательно)", + "members_section": "Участники", + "add_member_placeholder": "Добавить пользователя или группу…", + "no_members": "Пока нет участников.", + "remove_member": "Удалить", + "delete_group": "Удалить группу", + "delete_confirm": "Удалить группу «{name}»? Все привязанные к ней разрешения будут отозваны.", + "empty_state": "Пока нет групп.", + "load_more": "Загрузить ещё", + "back_to_list": "Назад", + "loading": "Загрузка…", + "virtual_badge": "Системная", + "member_count_zero": "Нет участников", + "member_count_one": "1 участник", + "member_count_other": "{count} участников", + "delete_confirm_label": "Введите имя группы для подтверждения:", + "delete_confirm_mismatch": "Введите имя группы точно для подтверждения.", + "virtual_internal_name": "Внутренние", + "members_loading": "Загрузка участников…", + "members_empty": "Нет участников", + "virtual_internal_explanation": "Каждый внутренний пользователь на этом сервере" + }, + "myshares": { + "copyLink": "Копировать ссылку", + "deleteLink": "Удалить ссылку", + "notifyByEmail": "Уведомить по e-mail", + "notifyFailed": "Не удалось отправить уведомление.", + "notifyGroupMembers": "Уведомить участников группы", + "notifyRateLimited": "Слишком много уведомлений для этого получателя — попробуйте позже.", + "removeAccess": "Отозвать доступ", + "resendInvitation": "Отправить приглашение повторно" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/zh-TW.json b/frontend/static/locales/zh-TW.json new file mode 100644 index 00000000..f0efc548 --- /dev/null +++ b/frontend/static/locales/zh-TW.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "此登入連結已不再有效", + "expired_body": "連結可能已過期或已被使用。我們可以為您發送一個新的 — 幾秒鐘內將會抵達您的收件匣。", + "resend_to": "發送新連結至 {{email}}", + "generic_unavailable": "此登入連結已不再有效。它可能已被使用或已過期。請在登入頁面請求新連結。", + "service_unavailable": "此伺服器未啟用魔法連結登入。", + "internal_error": "登入時發生錯誤。請重試。", + "resend_failure": "發送連結時發生錯誤。請重試。", + "cross_browser_title": "在此裝置上繼續登入?", + "cross_browser_body": "您在與請求時不同的瀏覽器或裝置上開啟了此登入連結。", + "cross_browser_warning": "如果是您請求了此連結,可以安全繼續。否則,請關閉此頁面 — 點擊「繼續」將使其他人登入您的帳戶。", + "cross_browser_continue": "繼續並登入", + "resend_confirmation_title": "請檢查您的收件匣", + "resend_confirmation_body": "如果登入連結屬於活躍帳戶,新連結剛剛已發送。請檢查您的收件匣。", + "return_link": "返回 OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n點擊下方連結開啟:\n{{link}}\n\n該連結僅可使用一次,並將在 {{ttl_hours}} 小時後過期。\n如果您未預期收到此邀請,可以忽略此訊息。\n\n— OxiCloud" + }, + "login": { + "subject": "登入 OxiCloud", + "body": "您好,\n\n使用下方連結登入 OxiCloud。該連結僅可使用一次,並將在 {{ttl_minutes}} 分鐘後過期。請在請求時使用的同一裝置上開啟。\n\n{{link}}\n\n如果您未請求此登入連結,可以忽略此訊息 — 無需進一步操作。\n\n— OxiCloud" + }, + "kind_file": "檔案", + "kind_folder": "資料夾", + "english_fallback_divider": "--- 以下為英文版本 ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n開啟 OxiCloud 檢視您的新分享:\n{{login_link}}\n\n您可能還有來自 {{inviter}} 的其他新分享 — 登入以檢視所有與您分享的項目。\n\n— OxiCloud\n\n您收到此訊息是因為您擁有 OxiCloud 帳戶且分享通知偏好已開啟。您可以在個人資料中關閉它(當有人與我分享時透過電子郵件通知我)。" + } + } + }, + "app": { + "title": "OxiCloud", + "description": "極簡雲端儲存系統" + }, + "nav": { + "files": "檔案", + "shared": "共享", + "recent": "最近", + "favorites": "收藏", + "photos": "照片", + "music": "音樂", + "trash": "回收站", + "sharedwithme": "與我共享" + }, + "photos": { + "empty_state": "還沒有照片", + "empty_hint": "上傳圖片或影片即可在此檢視", + "items_selected": "已選擇", + "view_daily": "日", + "view_monthly": "月", + "view_yearly": "年" + }, + "music": { + "create_playlist": "建立播放列表", + "playlists": "播放列表", + "no_playlists": "還沒有播放列表", + "select_playlist": "選擇一個播放列表", + "select_hint": "從側邊欄選擇播放列表或建立新播放列表", + "add_tracks": "新增曲目", + "no_tracks": "此播放列表中沒有曲目", + "unknown_artist": "未知藝術家", + "unknown_title": "未知", + "confirm_delete": "刪除此播放列表?", + "playlist_name": "播放列表名稱", + "create": "建立", + "delete": "刪除", + "share": "分享", + "edit": "編輯", + "play_all": "全部播放", + "shuffle": "隨機播放", + "repeat": "重複", + "repeat_one": "單曲迴圈", + "queue": "播放佇列", + "queue_empty": "播放佇列為空", + "not_playing": "未播放", + "play": "播放", + "pause": "暫停", + "previous": "上一首", + "next": "下一首", + "volume": "音量", + "mute": "靜音", + "unmute": "取消靜音", + "title": "標題", + "artist": "藝術家", + "album": "專輯", + "tracks": "首曲目", + "add": "新增", + "added": "已新增!", + "added_to_playlist": "已新增到播放列表", + "add_to_playlist": "新增到播放列表", + "load_error": "載入播放列表出錯", + "add_error": "無法將曲目新增到播放列表", + "no_playlists_yet": "暫無播放列表。請先建立一個!", + "selected_files": "已選擇:", + "error": "錯誤", + "search_audio": "搜尋音訊檔案…", + "no_audio_files": "未找到音訊檔案", + "selected": "已選擇", + "loading": "載入中…", + "search_error": "無法載入音訊檔案", + "adding": "新增中…", + "can_write": "可以編輯", + "cover_updated": "封面已更新", + "empty_hint": "建立你的第一個播放列表來開始整理你的音樂", + "make_private": "設為私人", + "make_public": "設為公開", + "manage_shares": "管理共享", + "no_shares": "尚未共享", + "playback_error": "播放失敗", + "private": "私人", + "public": "公開", + "read_only": "唯讀", + "remove": "移除", + "remove_share": "移除共享", + "set_cover": "設定封面", + "share_with_user": "使用者 ID 或電子郵件", + "toggle_public": "可見性", + "track_removed": "曲目已移除" + }, + "actions": { + "search": "搜尋檔案...", + "new_folder": "新建資料夾", + "upload": "上傳", + "upload_files": "上傳檔案", + "upload_folder": "上傳資料夾", + "upload.uploading": "上傳中...", + "upload.complete": "{count} / {total} 已上傳", + "upload.files": "檔案", + "rename": "重新命名", + "move": "移動到...", + "move_to": "移動到", + "delete": "刪除", + "download": "下載", + "view": "檢視", + "cancel": "取消", + "confirm": "確認", + "share": "共享", + "favorite": "新增到收藏", + "unfavorite": "取消收藏", + "copy": "複製", + "notify": "通知", + "send": "傳送", + "clear_recent": "清除最近", + "logout": "退出登入", + "create": "建立", + "search_btn": "搜尋", + "close": "關閉", + "delete_permanently": "永久刪除", + "empty_trash": "清空回收站", + "open_parent_folder": "轉到父資料夾", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "外觀", + "about": "關於 OxiCloud", + "about_description": "基於 Rust 和整潔架構構建的雲端儲存平臺。快速、安全、私密。", + "admin_panel": "管理面板", + "profile": "我的資料", + "role_user": "使用者", + "theme": { + "light": "淺色", + "dark": "深色", + "auto": "跟隨系統" + }, + "manage_groups": "管理群組" + }, + "share": { + "dialogTitle": "共享連結", + "linkLabel": "共享連結:", + "copyLink": "複製", + "permissions": "許可權:", + "permissionRead": "讀取", + "permissionWrite": "寫入", + "permissionReshare": "再共享", + "password": "密碼保護:", + "generatePassword": "生成", + "expiration": "過期日期:", + "update": "更新共享", + "remove": "移除共享", + "notifyTitle": "傳送通知", + "notifyEmailLabel": "電子郵件地址:", + "notifyMessageLabel": "訊息(可選):", + "notifySend": "傳送通知", + "shareWithOthers": "與他人共享", + "sharePublicly": "公開共享", + "shareSettings": "共享設定", + "shareCopied": "連結已複製到剪貼簿", + "shareCreated": "共享連結建立成功", + "shareUpdated": "共享設定更新成功", + "shareRemoved": "共享已移除", + "inviteByEmail": "透過郵件邀請 — 將傳送邀請", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "共享連結", + "share_linkLabel": "共享連結:", + "share_copyLink": "複製", + "share_permissions": "許可權:", + "share_permissionRead": "讀取", + "share_permissionWrite": "寫入", + "share_permissionReshare": "再共享", + "share_password": "密碼保護:", + "share_generatePassword": "生成", + "share_expiration": "過期日期:", + "share_update": "更新共享", + "share_remove": "移除共享", + "share_notifyTitle": "傳送通知", + "share_notifyEmailLabel": "電子郵件地址:", + "share_notifyMessageLabel": "訊息(可選):", + "share_notifySend": "傳送通知", + "shared": { + "backToFiles": "返回檔案", + "pageTitle": "共享資源", + "pageDescription": "管理你的共享檔案和資料夾", + "filterType": "型別:", + "filterAll": "全部", + "filterFiles": "檔案", + "filterFolders": "資料夾", + "sortBy": "排序依據:", + "sortByName": "名稱", + "sortByDate": "共享日期", + "sortByExpiration": "過期日期", + "search": "搜尋", + "colName": "名稱", + "colType": "型別", + "colDateShared": "共享日期", + "colExpiration": "過期日期", + "colPermissions": "許可權", + "colPassword": "密碼", + "colActions": "操作", + "emptyStateTitle": "尚未有共享資源", + "emptyStateDesc": "當你共享檔案或資料夾時,它們會出現在這裡", + "goToFiles": "前往檔案", + "typeFile": "檔案", + "typeFolder": "資料夾", + "noExpiration": "無過期", + "hasPassword": "有", + "noPassword": "無", + "editShare": "編輯共享", + "notifyShare": "通知某人", + "copyLink": "複製連結", + "removeShare": "移除共享", + "linkCopied": "連結已複製到剪貼簿!", + "linkCopyFailed": "複製連結失敗", + "itemUpdated": "共享設定更新成功", + "itemRemoved": "共享已移除成功", + "invalidEmail": "請輸入有效的電子郵件地址", + "notificationSent": "通知已成功傳送", + "notificationFailed": "傳送通知失敗", + "shared_backToFiles": "返回檔案", + "shared_colActions": "操作", + "shared_colDateShared": "共享日期", + "shared_colExpiration": "過期日期", + "shared_colName": "名稱", + "shared_colPassword": "密碼", + "shared_colPermissions": "許可權", + "shared_colType": "類型", + "shared_copyLink": "複製連結", + "shared_editShare": "編輯共享", + "shared_emptyStateDesc": "當你共享檔案或資料夾時,它們會顯示在此", + "shared_emptyStateTitle": "尚無共享資源", + "shared_filterAll": "全部", + "shared_filterFiles": "檔案", + "shared_filterFolders": "資料夾", + "shared_filterType": "類型:", + "shared_goToFiles": "前往檔案", + "shared_hasPassword": "是", + "shared_invalidEmail": "請輸入有效的電子郵件地址", + "shared_itemRemoved": "共享已成功移除", + "shared_itemUpdated": "共享設定已成功更新", + "shared_linkCopied": "連結已複製到剪貼簿!", + "shared_linkCopyFailed": "複製連結失敗", + "shared_noExpiration": "永不過期", + "shared_noPassword": "否", + "shared_notificationFailed": "傳送通知失敗", + "shared_notificationSent": "通知已成功傳送", + "shared_notifyShare": "通知對方", + "shared_pageDescription": "管理你的共享檔案與資料夾", + "shared_pageTitle": "共享資源", + "shared_removeShare": "移除共享", + "shared_search": "搜尋", + "shared_sortBy": "排序方式:", + "shared_sortByDate": "共享日期", + "shared_sortByExpiration": "過期日期", + "shared_sortByName": "名稱", + "shared_typeFile": "檔案", + "shared_typeFolder": "資料夾" + }, + "files": { + "name": "名稱", + "type": "型別", + "size": "大小", + "modified": "修改日期", + "no_files": "此資料夾中沒有檔案", + "empty_hint": "上傳檔案或建立資料夾以開始使用", + "loading": "正在載入檔案…", + "view_grid": "網格檢視", + "view_list": "列表檢視", + "file_types": { + "document": "文件", + "image": "圖片", + "video": "影片", + "audio": "音訊", + "pdf": "PDF", + "text": "文字", + "folder": "資料夾", + "spreadsheet": "電子表格", + "presentation": "簡報", + "archive": "壓縮檔案", + "installer": "安裝程式", + "code": "程式碼" + }, + "owner": "擁有者" + }, + "dialogs": { + "rename_folder": "重新命名資料夾", + "new_name": "新名稱", + "new_folder_title": "新建資料夾", + "folder_name": "資料夾名稱", + "folder_placeholder": "我的資料夾", + "rename_title": "重新命名", + "move_file": "移動檔案", + "select_destination": "選擇目標資料夾", + "root": "根目錄", + "delete_confirmation": "你確定要刪除", + "and_contents": "及其所有內容", + "no_undo": "此操作無法撤銷", + "share_file": "共享檔案", + "share_folder": "共享資料夾", + "existing_shares": "現有共享", + "share_options": "共享選項", + "password": "密碼", + "expiration": "過期日期", + "permissions": "許可權", + "generated_link": "生成的連結", + "notify": "傳送通知", + "recipient": "收件人", + "message": "訊息", + "confirm_delete": "移至回收站", + "confirm_delete_file": "確定要將檔案「{{name}}」移至回收站嗎?", + "confirm_delete_folder": "確定要將資料夾「{{name}}」及其所有內容移至回收站嗎?", + "confirm_delete_share": "刪除共享連結", + "confirm_delete_share_msg": "確定要刪除此共享連結嗎?", + "confirm_empty_trash": "清空回收站", + "confirm_permanent_delete": "永久刪除", + "confirm_permanent_delete_msg": "確定要永久刪除此項目嗎?此操作無法復原。", + "confirm_title": "確認操作", + "go_to_parent": ".. (上層資料夾)", + "move_folder": "移動資料夾", + "no_subfolders": "沒有子資料夾", + "rename_file": "重新命名檔案", + "select_this_folder": "選擇此資料夾", + "move_to_home": "移動到主資料夾" + }, + "dropzone": { + "drag_files": "將檔案拖到這裡,或點選選擇", + "drop_files": "釋放檔案以上傳" + }, + "permissions": { + "read": "讀取", + "write": "寫入", + "reshare": "再共享" + }, + "errors": { + "file_not_found": "檔案未找到", + "folder_not_found": "資料夾未找到", + "delete_error": "刪除時出錯", + "upload_error": "上傳檔案時出錯", + "rename_error": "重新命名時出錯", + "move_error": "移動時出錯", + "empty_name": "名稱不能為空", + "name_exists": "已存在同名檔案或資料夾", + "generic_error": "發生錯誤", + "group_name_invalid": "群組名稱必須符合電子郵件前綴格式(字母、數字、點、連字符、下劃線;1–64 個字元)。", + "group_cycle": "此成員會在群組之間形成循環參照。", + "group_depth_exceeded": "嵌套深度超過允許的最大值(8)。", + "group_virtual_immutable": "「Internal」群組由系統管理,無法修改。", + "group_not_found": "找不到群組。", + "group_name_taken": "已存在同名群組。" + }, + "breadcrumb": { + "home": "主頁" + }, + "trash": { + "empty_trash": "清空回收站", + "empty_state": "回收站為空", + "original_location": "原始位置", + "deleted_date": "刪除日期", + "remaining": "剩餘", + "actions": "操作", + "restore": "恢復", + "delete_permanently": "永久刪除", + "empty_confirm": "你確定要清空回收站嗎?這將永久刪除所有專案。", + "groupby": { + "remaining_days": "剩餘天數", + "trashed_time": "刪除時間" + } + }, + "daysRemaining": { + "expired": "已過期", + "today": "今天", + "tomorrow": "明天", + "inDays": "{{count}} 天" + }, + "expiryChip": { + "never": "永不過期", + "expired": "已過期", + "today": "今天到期", + "tomorrow": "明天到期", + "inDays": "{{count}} 天後到期", + "onDate": "於 {{date}} 到期" + }, + "auth": { + "login_title": "登入", + "username": "使用者名稱", + "username_placeholder": "輸入你的使用者名稱", + "login_identifier": "使用者名稱或電子郵件", + "login_identifier_placeholder": "請輸入使用者名稱或電子郵件", + "password": "密碼", + "password_placeholder": "輸入你的密碼", + "login_button": "登入", + "no_account": "沒有賬號?", + "register": "註冊", + "admin_setup": "首次使用?", + "setup": "設定管理員", + "register_title": "建立賬號", + "email": "電子郵件", + "email_placeholder": "輸入你的電子郵件", + "confirm_password": "確認密碼", + "confirm_password_placeholder": "確認你的密碼", + "register_button": "建立賬號", + "have_account": "已有賬號?", + "login": "登入", + "setup_title": "初始設定", + "setup_step1": "管理員", + "setup_step2": "系統", + "setup_step3": "完成", + "admin_username": "管理員使用者名稱", + "admin_email": "管理員電子郵件", + "admin_password": "管理員密碼", + "create_admin": "建立管理員", + "back_to_login": "已設定完成?", + "admin_success": "管理員賬號建立成功!您現在可以登入。", + "account_success": "賬號建立成功!您現在可以登入。", + "passwords_mismatch": "密碼不匹配", + "admin_create_error": "建立管理員賬號時出錯", + "or": "或", + "sso_login": "使用 SSO 登入", + "sso_login_provider": "使用 {{provider}} 登入", + "magicLinkHint": "沒有密碼?輸入您的電子郵件,我們將向您發送一次性登入連結。", + "magicLinkEmailLabel": "電子郵件地址", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "傳送登入連結", + "magicLinkSent": "若該電子郵件存在帳號,登入連結已發送。請查收您的收件匣。", + "magicLinkUnavailable": "此伺服器不支援電子郵件登入。", + "magicLinkNetworkError": "無法連線到伺服器:{{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "儲存空間", + "calculating": "計算中...", + "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "無法預覽此檔案型別。", + "download_file": "下載檔案", + "zoom_in": "放大", + "zoom_out": "縮小", + "zoom_reset": "重置縮放" + }, + "language_selector": { + "title": "歡迎!", + "subtitle": "選擇您的語言以繼續", + "continue": "繼續", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "還沒有收藏", + "empty_hint": "為檔案或資料夾新增星標以將其新增到收藏夾", + "add": "新增到收藏夾", + "remove": "從收藏夾移除", + "added_title": "已新增到收藏", + "added_msg": "已新增到收藏", + "removed_title": "已從收藏移除", + "removed_msg": "已從收藏移除" + }, + "recent": { + "title": "最近", + "clear": "清除最近", + "accessed": "訪問於", + "empty_state": "沒有最近檔案", + "empty_hint": "您開啟的檔案將顯示在這裡", + "loadMore": "載入更多" + }, + "batch": { + "one_selected": "已選擇 1 個專案", + "n_selected": "已選擇 {{count}} 個專案", + "confirm_delete": "確定要將 {{count}} 個專案移至回收站嗎?", + "move_title": "移動 {{count}} 個專案", + "add_favorites": "新增到收藏夾", + "move_copy": "移動或複製" + }, + "admin": { + "page_title": "管理面板", + "back_to_app": "返回 OxiCloud", + "loading": "載入中…", + "access_denied": "拒絕訪問", + "access_denied_desc": "需要管理員許可權。", + "sign_in": "登入", + "tab_dashboard": "儀表盤", + "tab_users": "使用者", + "tab_oidc": "SSO / OIDC", + "total_users": "使用者總數", + "active_users": "活躍使用者", + "admins": "管理員", + "version": "版本", + "storage_overview": "儲存概覽", + "used": "已使用", + "total_quota": "總配額", + "usage_pct": "使用率", + "users_over_80": "超過80%配額", + "users_over_quota": "超過配額", + "system": "系統", + "auth_label": "認證", + "oidc_label": "OIDC", + "quotas_label": "配額", + "enabled": "已啟用", + "disabled": "已禁用", + "active": "活躍", + "off": "關閉", + "allow_registration": "允許公開自助註冊", + "registration_warning": "公開註冊已禁用。只有管理員可以建立新使用者。", + "user_management": "使用者管理", + "create_user": "建立使用者", + "col_user": "使用者", + "col_role": "角色", + "col_auth": "認證", + "col_status": "狀態", + "col_storage": "儲存", + "col_last_login": "最後登入", + "col_actions": "操作", + "loading_users": "正在載入使用者…", + "failed_load_users": "載入失敗", + "no_users_found": "未找到使用者", + "showing_users": "顯示 {{from}}-{{to}} / {{total}}", + "prev": "上一頁", + "next": "下一頁", + "inactive": "未啟用", + "you_badge": "(你)", + "local": "本地", + "never": "從未", + "just_now": "剛剛", + "minutes_ago": "{{n}}分鐘前", + "hours_ago": "{{n}}小時前", + "days_ago": "{{n}}天前", + "edit_quota_title": "編輯配額", + "reset_password_title": "重置密碼", + "toggle_role_title": "切換角色", + "deactivate_title": "停用", + "activate_title": "啟用", + "delete_title": "刪除", + "sso_title": "單點登入 (OIDC / SSO)", + "enable_sso": "啟用 SSO 認證", + "provider_name": "提供商名稱", + "issuer_url": "發行者 URL", + "issuer_url_hint": "您的身份提供商的 OpenID Connect 發行者 URL", + "auto_discover": "自動發現", + "discovering": "發現中…", + "client_id": "客戶端 ID", + "client_secret": "客戶端金鑰", + "client_secret_placeholder": "留空以保留當前值", + "secret_configured": "已配置客戶端金鑰", + "callback_url": "回撥 URL", + "callback_url_hint": "(在您的 IdP 中註冊)", + "advanced_settings": "高階設定", + "scopes": "範圍", + "auto_provision": "首次登入時自動配置使用者", + "admin_groups": "管理組", + "admin_groups_hint": "對映到管理員角色的逗號分隔 OIDC 組名", + "disable_password": "禁用密碼登入 (僅 OIDC)", + "password_warning": "這將阻止所有基於密碼的登入!", + "test_btn": "測試", + "save_btn": "儲存", + "saving": "儲存中…", + "settings_saved": "設定已儲存 — OIDC 現在 {{status}}", + "quota_modal_title": "更新儲存配額", + "quota_user_label": "使用者:", + "new_quota": "新配額", + "quota_unlimited_hint": "0表示無限制", + "cancel": "取消", + "create_user_title": "建立新使用者", + "username_label": "使用者名稱", + "username_placeholder": "zhangsan", + "username_hint": "3–32個字元", + "password_label": "密碼", + "password_placeholder": "至少8個字元", + "email_label": "郵箱", + "email_optional": "(可選)", + "email_placeholder": "user@example.com (留空自動生成)", + "role_label": "角色", + "role_user": "使用者", + "role_admin": "管理員", + "quota_label": "配額", + "creating": "建立中…", + "reset_pw_title": "重置密碼", + "new_password_label": "新密碼", + "resetting": "重置中…", + "reset_btn": "重置", + "confirm_role_change": "將角色更改為 {{role}}?", + "confirm_deactivate": "確定要停用此使用者嗎?", + "confirm_activate": "確定要啟用此使用者嗎?", + "confirm_delete_user": "刪除使用者 \"{{name}}\"?此操作無法撤消!", + "confirm_action": "確認操作", + "confirm_yes": "確認", + "confirm_no": "取消", + "error_username_short": "使用者名稱至少需要3個字元", + "error_password_short": "密碼至少需要8個字元", + "error_generic": "失敗", + "error_network": "網路錯誤:{{message}}", + "error_create_user": "建立使用者失敗", + "tab_storage": "儲存", + "storage_title": "儲存配置", + "storage_current_backend": "當前後端", + "storage_total_blobs": "總塊數", + "storage_total_size": "總大小", + "storage_dedup_ratio": "去重比率", + "storage_backend": "後端", + "storage_local": "本地", + "storage_s3": "S3 相容", + "storage_provider_preset": "提供商預設", + "storage_preset_custom": "自定義", + "storage_endpoint_url": "端點 URL", + "storage_endpoint_hint": "AWS S3 請留空", + "storage_bucket": "儲存桶", + "storage_region": "地區", + "storage_access_key": "訪問金鑰", + "storage_secret_key": "金鑰", + "storage_secret_configured": "金鑰已配置", + "storage_key_placeholder": "輸入新金鑰", + "storage_path_style": "強制路徑風格", + "storage_path_style_hint": "MinIO 及某些 S3 相容服務需要此選項", + "storage_test_connection": "測試連線", + "storage_test_success": "連線成功", + "storage_test_failure": "連線失敗", + "storage_save": "儲存配置", + "storage_saved": "配置已儲存", + "storage_migration": "資料遷移", + "storage_migration_coming_soon": "遷移工具即將推出", + "migration_status_label": "遷移狀態", + "migration_start": "開始遷移", + "migration_pause": "暫停", + "migration_resume": "繼續", + "migration_verify": "驗證", + "migration_complete": "完成", + "migration_started": "遷移已開始", + "migration_paused_msg": "遷移已暫停", + "migration_resumed_msg": "遷移已繼續", + "migration_completed_msg": "遷移成功完成", + "migration_verifying": "正在驗證...", + "migration_verify_passed": "驗證透過", + "migration_verify_failed": "驗證失敗", + "migration_failed_blobs": "失敗的塊", + "testing": "正在測試...", + "smtp_disabled": "已停用(未設定主機)", + "smtp_enabled": "已啟用", + "smtp_enabled_label": "狀態", + "smtp_intro": "SMTP 僅透過環境變數(OXICLOUD_SMTP_*)設定。下方數值是從運行中的伺服器讀取的 — 如需修改,請編輯環境變數並重新啟動 OxiCloud。", + "smtp_not_configured": "此伺服器未設定 SMTP。", + "smtp_send_failed": "傳送失敗。", + "smtp_send_test": "傳送測試郵件", + "smtp_sending": "傳送中…", + "smtp_sent": "測試郵件已傳送。", + "smtp_server_code": "伺服器回應", + "smtp_test_intro": "向下方收件者傳送預設的診斷訊息,並回報 SMTP 伺服器的回應,以便您與轉發日誌進行對照。", + "smtp_test_missing_to": "請輸入收件者地址。", + "smtp_test_title": "傳送測試郵件", + "smtp_test_to": "收件者地址", + "smtp_title": "外寄郵件 (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "個人資料", + "back_to_app": "返回 OxiCloud", + "loading": "載入中…", + "not_authenticated": "未認證", + "not_authenticated_desc": "請登入以檢視您的個人資料。", + "sign_in": "登入", + "role_admin": "管理員", + "role_user": "使用者", + "account_details": "賬戶詳情", + "username": "使用者名稱", + "email": "郵箱", + "role": "角色", + "last_login": "最後登入", + "storage": "儲存", + "used": "已使用", + "quota": "配額", + "usage": "使用率", + "unlimited": "無限制", + "app_passwords": "應用密碼", + "app_pw_desc": "為 WebDAV、CalDAV 和 CardDAV 客戶端生成密碼。每個密碼只顯示一次。", + "app_pw_label_placeholder": "標籤(如 Thunderbird、macOS)", + "generate": "生成", + "generating": "生成中…", + "new_password_for": "新密碼用於", + "copy_warning": "請立即複製此密碼,之後將無法再次檢視。", + "copy_to_clipboard": "複製到剪貼簿", + "col_label": "標籤", + "col_created": "建立時間", + "col_last_used": "最後使用", + "col_status": "狀態", + "active": "活躍", + "revoked": "已撤銷", + "revoke_title": "撤銷", + "no_app_passwords": "暫無應用密碼。", + "client_sessions": "客戶端會話", + "client_sessions_desc": "連線 Nextcloud 相容客戶端時自動生成。", + "col_client": "客戶端", + "never": "從未", + "just_now": "剛剛", + "minutes_ago": "{{n}}分鐘前", + "hours_ago": "{{n}}小時前", + "days_ago": "{{n}}天前", + "edit_profile": "編輯個人資料", + "edit_oidc_managed": "要更改您的資訊(姓名、名字、頭像等),請前往您的身分提供者更新。變更將在您下次登入時顯示。", + "username_claim_hint": "2-64 個字元,字母 / 數字 / 點 / 短橫線 / 底線。一旦選定,使用者名稱將無法更改(DAV/NextCloud 用戶端依賴它)。", + "username_already_claimed": "使用者名稱已設定,不可更改(DAV/NextCloud 用戶端依賴它)。", + "given_name": "名", + "family_name": "姓", + "notify_on_share": "當有人與我分享時透過電子郵件通知我", + "notify_on_share_hint": "取消勾選後,分享項目仍會顯示在您的帳戶中 — 只是不會收到相關郵件通知。", + "save_profile": "儲存變更", + "profile_saved": "個人資料已更新", + "profile_no_changes": "沒有變更可儲存。", + "profile_save_failed": "儲存失敗", + "username_taken_error": "該使用者名稱已被使用。", + "username_immutable_error": "您的使用者名稱已設定,無法在此更改。如需重新命名,請聯絡管理員。", + "change_password": "修改密碼", + "current_password": "當前密碼", + "new_password": "新密碼", + "min_8_chars": "至少8個字元", + "confirm_password": "確認新密碼", + "update_password": "更新密碼", + "updating": "更新中…", + "password_updated": "密碼更新成功", + "passwords_no_match": "密碼不匹配", + "password_too_short": "密碼至少需要8個字元", + "password_change_failed": "修改密碼失敗", + "error_network": "網路錯誤:{{message}}", + "error_label_required": "請輸入標籤", + "error_create_pw": "建立應用密碼失敗", + "confirm_revoke": "撤銷應用密碼\"{{label}}\"?使用此密碼的客戶端將停止工作。", + "error_revoke": "撤銷失敗", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "notifications": { + "file_renamed": "檔案已重新命名", + "file_renamed_to": "檔案已重新命名為\"{{name}}\"", + "folder_renamed": "資料夾已重新命名", + "folder_renamed_to": "資料夾已重新命名為\"{{name}}\"", + "file_uploaded": "檔案已上傳", + "file_deleted": "檔案已移至回收站", + "folder_deleted": "資料夾已移至回收站", + "item_deleted_permanently": "專案已永久刪除", + "trash_emptied": "回收站已清空", + "title": "通知", + "empty": "暫無通知", + "link_created": "連結已建立", + "share_success": "分享連結建立成功", + "upload_files_section_title": "此處不支援上傳", + "upload_files_section_body": "請前往檔案部分上傳檔案" + }, + "upload": { + "uploading": "正在上傳...", + "files": "個檔案", + "complete": "已上傳 {{count}} / {{total}}" + }, + "storage_quota_exceeded": "儲存配額已超限", + "sharedwithme": { + "pageTitle": "與我共享", + "pageDescription": "其他使用者與您共享的檔案和資料夾", + "emptyStateTitle": "目前沒有內容與您共享", + "emptyStateDesc": "其他使用者與您共享的項目將顯示在這裡", + "loadMore": "載入更多", + "sharedBy": "共享者", + "colName": "名稱", + "colType": "類型", + "colSharedBy": "共享者", + "colDate": "共享日期", + "colPermissions": "權限" + }, + "groupby": { + "none": "無", + "title": "分組方式", + "owner": "擁有者", + "shareDate": "分享日期", + "type": "類型", + "type.folders": "資料夾", + "accessedAt": "存取日期", + "modifiedAt": "修改日期", + "createdAt": "建立日期", + "size": "大小", + "favoriteDate": "收藏日期", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "新增" + }, + "dateBucket": { + "today": "今天", + "last7days": "近7天", + "last30days": "近30天" + }, + "groups": { + "title": "管理群組", + "create_button": "建立群組", + "create_dialog_title": "新群組", + "edit_dialog_title": "重新命名群組", + "name_label": "名稱", + "name_placeholder": "engineering", + "description_label": "描述(選填)", + "members_section": "成員", + "add_member_placeholder": "新增使用者或群組…", + "no_members": "尚無成員。", + "remove_member": "移除", + "delete_group": "刪除群組", + "delete_confirm": "刪除群組「{name}」?引用此群組的所有授權將被撤銷。", + "empty_state": "尚無群組。", + "load_more": "載入更多", + "back_to_list": "返回", + "loading": "載入中…", + "virtual_badge": "系統", + "member_count_zero": "無成員", + "member_count_one": "1 個成員", + "member_count_other": "{count} 個成員", + "delete_confirm_label": "請輸入群組名稱以確認:", + "delete_confirm_mismatch": "請準確輸入群組名稱以確認。", + "virtual_internal_name": "內部", + "members_loading": "正在載入成員…", + "members_empty": "無成員", + "virtual_internal_explanation": "本伺服器上的所有內部使用者" + }, + "myshares": { + "copyLink": "複製連結", + "deleteLink": "刪除連結", + "notifyByEmail": "透過郵件通知", + "notifyFailed": "無法傳送通知。", + "notifyGroupMembers": "通知群組成員", + "notifyRateLimited": "對此收件者的通知過多 — 請稍後重試。", + "removeAccess": "移除存取權限", + "resendInvitation": "重新傳送邀請郵件" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/static/locales/zh.json b/frontend/static/locales/zh.json new file mode 100644 index 00000000..a4af3ee3 --- /dev/null +++ b/frontend/static/locales/zh.json @@ -0,0 +1,980 @@ +{ + "server": { + "magic_link": { + "page": { + "expired_title": "此登录链接已不再有效", + "expired_body": "链接可能已过期或已被使用。我们可以为您发送一个新的 — 几秒钟内它将到达您的收件箱。", + "resend_to": "发送新链接至 {{email}}", + "generic_unavailable": "此登录链接已不再有效。它可能已被使用或已过期。请在登录页面请求新链接。", + "service_unavailable": "此服务器未启用魔法链接登录。", + "internal_error": "登录时出错。请重试。", + "resend_failure": "发送链接时出错。请重试。", + "cross_browser_title": "在此设备上继续登录?", + "cross_browser_body": "您在与请求时不同的浏览器或设备上打开了此登录链接。", + "cross_browser_warning": "如果是您请求了此链接,可以安全继续。否则,请关闭此页面 — 点击「继续」将使其他人登录您的账户。", + "cross_browser_continue": "继续并登录", + "resend_confirmation_title": "请检查您的收件箱", + "resend_confirmation_body": "如果登录链接属于活跃账户,新链接刚刚已发送。请检查您的收件箱。", + "return_link": "返回 OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n点击下方链接打开:\n{{link}}\n\n该链接仅可使用一次,并将在 {{ttl_hours}} 小时后过期。\n如果您未预期收到此邀请,可以忽略此消息。\n\n— OxiCloud" + }, + "login": { + "subject": "登录 OxiCloud", + "body": "您好,\n\n使用下方链接登录 OxiCloud。该链接仅可使用一次,并将在 {{ttl_minutes}} 分钟后过期。请在请求时使用的同一设备上打开。\n\n{{link}}\n\n如果您未请求此登录链接,可以忽略此消息 — 无需进一步操作。\n\n— OxiCloud" + }, + "kind_file": "文件", + "kind_folder": "文件夹", + "english_fallback_divider": "--- 以下为英文版本 ---" + } + }, + "notification": { + "share": { + "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n打开 OxiCloud 查看您的新共享:\n{{login_link}}\n\n您可能还有来自 {{inviter}} 的其他新共享 — 登录以查看所有共享给您的项目。\n\n— OxiCloud\n\n您收到此消息是因为您拥有 OxiCloud 账户且共享通知偏好已开启。您可以在个人资料中关闭它(当有人与我共享时通过电子邮件通知我)。" + } + } + }, + "app": { + "title": "OxiCloud", + "description": "极简云存储系统" + }, + "nav": { + "files": "文件", + "shared": "共享", + "recent": "最近", + "favorites": "收藏", + "photos": "照片", + "music": "音乐", + "trash": "回收站", + "sharedwithme": "与我共享" + }, + "photos": { + "empty_state": "还没有照片", + "empty_hint": "上传图片或视频即可在此查看", + "items_selected": "已选择", + "view_daily": "日", + "view_monthly": "月", + "view_yearly": "年" + }, + "music": { + "create_playlist": "创建播放列表", + "playlists": "播放列表", + "no_playlists": "还没有播放列表", + "select_playlist": "选择一个播放列表", + "select_hint": "从侧边栏选择播放列表或创建新播放列表", + "add_tracks": "添加曲目", + "no_tracks": "此播放列表中没有曲目", + "unknown_artist": "未知艺术家", + "unknown_title": "未知", + "confirm_delete": "删除此播放列表?", + "playlist_name": "播放列表名称", + "create": "创建", + "delete": "删除", + "share": "分享", + "edit": "编辑", + "play_all": "全部播放", + "shuffle": "随机播放", + "repeat": "重复", + "repeat_one": "单曲循环", + "queue": "播放队列", + "queue_empty": "播放队列为空", + "not_playing": "未播放", + "play": "播放", + "pause": "暂停", + "previous": "上一首", + "next": "下一首", + "volume": "音量", + "mute": "静音", + "unmute": "取消静音", + "title": "标题", + "artist": "艺术家", + "album": "专辑", + "tracks": "首曲目", + "add": "添加", + "added": "已添加!", + "added_to_playlist": "已添加到播放列表", + "add_to_playlist": "添加到播放列表", + "load_error": "加载播放列表出错", + "add_error": "无法将曲目添加到播放列表", + "no_playlists_yet": "暂无播放列表。请先创建一个!", + "selected_files": "已选择:", + "error": "错误", + "search_audio": "搜索音频文件…", + "no_audio_files": "未找到音频文件", + "selected": "已选择", + "loading": "加载中…", + "search_error": "无法加载音频文件", + "adding": "添加中…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed" + }, + "actions": { + "search": "搜索文件...", + "new_folder": "新建文件夹", + "upload": "上传", + "upload_files": "上传文件", + "upload_folder": "上传文件夹", + "upload.uploading": "上传中...", + "upload.complete": "{count} / {total} 已上传", + "upload.files": "文件", + "rename": "重命名", + "move": "移动到...", + "move_to": "移动到", + "delete": "删除", + "download": "下载", + "view": "查看", + "cancel": "取消", + "confirm": "确认", + "share": "共享", + "favorite": "添加到收藏", + "unfavorite": "取消收藏", + "copy": "复制", + "notify": "通知", + "send": "发送", + "clear_recent": "清除最近", + "logout": "退出登录", + "create": "创建", + "search_btn": "搜索", + "close": "关闭", + "delete_permanently": "Delete permanently", + "empty_trash": "Empty trash", + "open_parent_folder": "转到父文件夹", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "外观", + "about": "关于 OxiCloud", + "about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。", + "admin_panel": "管理面板", + "profile": "我的资料", + "role_user": "用户", + "theme": { + "light": "浅色", + "dark": "深色", + "auto": "跟随系统" + }, + "manage_groups": "管理群组" + }, + "share": { + "dialogTitle": "共享链接", + "linkLabel": "共享链接:", + "copyLink": "复制", + "permissions": "权限:", + "permissionRead": "读取", + "permissionWrite": "写入", + "permissionReshare": "再共享", + "password": "密码保护:", + "generatePassword": "生成", + "expiration": "过期日期:", + "update": "更新共享", + "remove": "移除共享", + "notifyTitle": "发送通知", + "notifyEmailLabel": "电子邮件地址:", + "notifyMessageLabel": "消息(可选):", + "notifySend": "发送通知", + "shareWithOthers": "与他人共享", + "sharePublicly": "公开共享", + "shareSettings": "共享设置", + "shareCopied": "链接已复制到剪贴板", + "shareCreated": "共享链接创建成功", + "shareUpdated": "共享设置更新成功", + "shareRemoved": "共享已移除", + "inviteByEmail": "通过邮件邀请 — 将发送邀请", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link" + }, + "share_dialogTitle": "共享链接", + "share_linkLabel": "共享链接:", + "share_copyLink": "复制", + "share_permissions": "权限:", + "share_permissionRead": "读取", + "share_permissionWrite": "写入", + "share_permissionReshare": "再共享", + "share_password": "密码保护:", + "share_generatePassword": "生成", + "share_expiration": "过期日期:", + "share_update": "更新共享", + "share_remove": "移除共享", + "share_notifyTitle": "发送通知", + "share_notifyEmailLabel": "电子邮件地址:", + "share_notifyMessageLabel": "消息(可选):", + "share_notifySend": "发送通知", + "shared": { + "backToFiles": "返回文件", + "pageTitle": "共享资源", + "pageDescription": "管理你的共享文件和文件夹", + "filterType": "类型:", + "filterAll": "全部", + "filterFiles": "文件", + "filterFolders": "文件夹", + "sortBy": "排序依据:", + "sortByName": "名称", + "sortByDate": "共享日期", + "sortByExpiration": "过期日期", + "search": "搜索", + "colName": "名称", + "colType": "类型", + "colDateShared": "共享日期", + "colExpiration": "过期日期", + "colPermissions": "权限", + "colPassword": "密码", + "colActions": "操作", + "emptyStateTitle": "尚未有共享资源", + "emptyStateDesc": "当你共享文件或文件夹时,它们会出现在这里", + "goToFiles": "前往文件", + "typeFile": "文件", + "typeFolder": "文件夹", + "noExpiration": "无过期", + "hasPassword": "有", + "noPassword": "无", + "editShare": "编辑共享", + "notifyShare": "通知某人", + "copyLink": "复制链接", + "removeShare": "移除共享", + "linkCopied": "链接已复制到剪贴板!", + "linkCopyFailed": "复制链接失败", + "itemUpdated": "共享设置更新成功", + "itemRemoved": "共享已移除成功", + "invalidEmail": "请输入有效的电子邮件地址", + "notificationSent": "通知已成功发送", + "notificationFailed": "发送通知失败", + "shared_backToFiles": "Back to Files", + "shared_colActions": "Actions", + "shared_colDateShared": "Date Shared", + "shared_colExpiration": "Expiration", + "shared_colName": "Name", + "shared_colPassword": "Password", + "shared_colPermissions": "Permissions", + "shared_colType": "Type", + "shared_copyLink": "Copy Link", + "shared_editShare": "Edit Share", + "shared_emptyStateDesc": "When you share files or folders, they will appear here", + "shared_emptyStateTitle": "No shared resources yet", + "shared_filterAll": "All", + "shared_filterFiles": "Files", + "shared_filterFolders": "Folders", + "shared_filterType": "Type:", + "shared_goToFiles": "Go to Files", + "shared_hasPassword": "Yes", + "shared_invalidEmail": "Please enter a valid email address", + "shared_itemRemoved": "Share removed successfully", + "shared_itemUpdated": "Share settings updated successfully", + "shared_linkCopied": "Link copied to clipboard!", + "shared_linkCopyFailed": "Failed to copy link", + "shared_noExpiration": "No expiration", + "shared_noPassword": "No", + "shared_notificationFailed": "Failed to send notification", + "shared_notificationSent": "Notification sent successfully", + "shared_notifyShare": "Notify Someone", + "shared_pageDescription": "Manage your shared files and folders", + "shared_pageTitle": "Shared Resources", + "shared_removeShare": "Remove Share", + "shared_search": "Search", + "shared_sortBy": "Sort by:", + "shared_sortByDate": "Date shared", + "shared_sortByExpiration": "Expiration", + "shared_sortByName": "Name", + "shared_typeFile": "File", + "shared_typeFolder": "Folder" + }, + "files": { + "name": "名称", + "type": "类型", + "size": "大小", + "modified": "修改日期", + "no_files": "此文件夹中没有文件", + "empty_hint": "上传文件或创建文件夹以开始使用", + "loading": "正在加载文件…", + "view_grid": "网格视图", + "view_list": "列表视图", + "file_types": { + "document": "文档", + "image": "图片", + "video": "视频", + "audio": "音频", + "pdf": "PDF", + "text": "文本", + "folder": "文件夹", + "spreadsheet": "电子表格", + "presentation": "演示文稿", + "archive": "压缩文件", + "installer": "安装程序", + "code": "代码" + }, + "owner": "所有者" + }, + "dialogs": { + "rename_folder": "重命名文件夹", + "new_name": "新名称", + "new_folder_title": "新建文件夹", + "folder_name": "文件夹名称", + "folder_placeholder": "我的文件夹", + "rename_title": "重命名", + "move_file": "移动文件", + "select_destination": "选择目标文件夹", + "root": "根目录", + "delete_confirmation": "你确定要删除", + "and_contents": "及其所有内容", + "no_undo": "此操作无法撤销", + "share_file": "共享文件", + "share_folder": "共享文件夹", + "existing_shares": "现有共享", + "share_options": "共享选项", + "password": "密码", + "expiration": "过期日期", + "permissions": "权限", + "generated_link": "生成的链接", + "notify": "发送通知", + "recipient": "收件人", + "message": "消息", + "confirm_delete": "Move to trash", + "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", + "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", + "confirm_delete_share": "Delete share link", + "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", + "confirm_empty_trash": "Empty trash", + "confirm_permanent_delete": "Delete permanently", + "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", + "confirm_title": "Confirm action", + "go_to_parent": ".. (parent folder)", + "move_folder": "Move folder", + "no_subfolders": "No subfolders", + "rename_file": "Rename file", + "select_this_folder": "Select this folder", + "move_to_home": "移动到主文件夹" + }, + "dropzone": { + "drag_files": "将文件拖到这里,或点击选择", + "drop_files": "释放文件以上传" + }, + "permissions": { + "read": "读取", + "write": "写入", + "reshare": "再共享" + }, + "errors": { + "file_not_found": "文件未找到", + "folder_not_found": "文件夹未找到", + "delete_error": "删除时出错", + "upload_error": "上传文件时出错", + "rename_error": "重命名时出错", + "move_error": "移动时出错", + "empty_name": "名称不能为空", + "name_exists": "已存在同名文件或文件夹", + "generic_error": "发生错误", + "group_name_invalid": "组名必须符合邮件前缀格式(字母、数字、点、连字符、下划线;1–64个字符)。", + "group_cycle": "此成员会在组之间形成循环引用。", + "group_depth_exceeded": "嵌套深度超出允许的最大值(8)。", + "group_virtual_immutable": "“Internal”组由系统管理,无法修改。", + "group_not_found": "未找到组。", + "group_name_taken": "同名组已存在。" + }, + "breadcrumb": { + "home": "主页" + }, + "trash": { + "empty_trash": "清空回收站", + "empty_state": "回收站为空", + "original_location": "原始位置", + "deleted_date": "删除日期", + "remaining": "剩余", + "actions": "操作", + "restore": "恢复", + "delete_permanently": "永久删除", + "empty_confirm": "你确定要清空回收站吗?这将永久删除所有项目。", + "groupby": { + "remaining_days": "剩余天数", + "trashed_time": "删除时间" + } + }, + "daysRemaining": { + "expired": "已过期", + "today": "今天", + "tomorrow": "明天", + "inDays": "{{count}} 天" + }, + "expiryChip": { + "never": "永不过期", + "expired": "已过期", + "today": "今天到期", + "tomorrow": "明天到期", + "inDays": "{{count}} 天后到期", + "onDate": "于 {{date}} 到期" + }, + "auth": { + "login_title": "登录", + "username": "用户名", + "username_placeholder": "输入你的用户名", + "login_identifier": "用户名或邮箱", + "login_identifier_placeholder": "请输入用户名或邮箱", + "password": "密码", + "password_placeholder": "输入你的密码", + "login_button": "登录", + "no_account": "没有账号?", + "register": "注册", + "admin_setup": "首次使用?", + "setup": "设置管理员", + "register_title": "创建账号", + "email": "电子邮件", + "email_placeholder": "输入你的电子邮件", + "confirm_password": "确认密码", + "confirm_password_placeholder": "确认你的密码", + "register_button": "创建账号", + "have_account": "已有账号?", + "login": "登录", + "setup_title": "初始设置", + "setup_step1": "管理员", + "setup_step2": "系统", + "setup_step3": "完成", + "admin_username": "管理员用户名", + "admin_email": "管理员电子邮件", + "admin_password": "管理员密码", + "create_admin": "创建管理员", + "back_to_login": "已设置完成?", + "admin_success": "管理员账号创建成功!您现在可以登录。", + "account_success": "账号创建成功!您现在可以登录。", + "passwords_mismatch": "密码不匹配", + "admin_create_error": "创建管理员账号时出错", + "or": "或", + "sso_login": "使用 SSO 登录", + "sso_login_provider": "使用 {{provider}} 登录", + "magicLinkHint": "没有密码?输入您的邮箱,我们将向您发送一次性登录链接。", + "magicLinkEmailLabel": "邮箱地址", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "发送登录链接", + "magicLinkSent": "如果该邮箱存在账户,登录链接已发送。请查收您的收件箱。", + "magicLinkUnavailable": "此服务器不支持邮箱登录。", + "magicLinkNetworkError": "无法连接到服务器:{{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on" + }, + "storage": { + "title": "存储空间", + "calculating": "计算中...", + "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "无法预览此文件类型。", + "download_file": "下载文件", + "zoom_in": "放大", + "zoom_out": "缩小", + "zoom_reset": "重置缩放" + }, + "language_selector": { + "title": "欢迎!", + "subtitle": "选择您的语言以继续", + "continue": "继续", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "还没有收藏", + "empty_hint": "为文件或文件夹添加星标以将其添加到收藏夹", + "add": "添加到收藏夹", + "remove": "从收藏夹移除", + "added_title": "已添加到收藏", + "added_msg": "已添加到收藏", + "removed_title": "已从收藏移除", + "removed_msg": "已从收藏移除" + }, + "recent": { + "title": "最近", + "clear": "清除最近", + "accessed": "访问于", + "empty_state": "没有最近文件", + "empty_hint": "您打开的文件将显示在这里", + "loadMore": "加载更多" + }, + "batch": { + "one_selected": "已选择 1 个项目", + "n_selected": "已选择 {{count}} 个项目", + "confirm_delete": "确定要将 {{count}} 个项目移至回收站吗?", + "move_title": "移动 {{count}} 个项目", + "add_favorites": "添加到收藏夹", + "move_copy": "移动或复制" + }, + "admin": { + "page_title": "管理面板", + "back_to_app": "返回 OxiCloud", + "loading": "加载中…", + "access_denied": "拒绝访问", + "access_denied_desc": "需要管理员权限。", + "sign_in": "登录", + "tab_dashboard": "仪表盘", + "tab_users": "用户", + "tab_oidc": "SSO / OIDC", + "total_users": "用户总数", + "active_users": "活跃用户", + "admins": "管理员", + "version": "版本", + "storage_overview": "存储概览", + "used": "已使用", + "total_quota": "总配额", + "usage_pct": "使用率", + "users_over_80": "超过80%配额", + "users_over_quota": "超过配额", + "system": "系统", + "auth_label": "认证", + "oidc_label": "OIDC", + "quotas_label": "配额", + "enabled": "已启用", + "disabled": "已禁用", + "active": "活跃", + "off": "关闭", + "allow_registration": "允许公开自助注册", + "registration_warning": "公开注册已禁用。只有管理员可以创建新用户。", + "user_management": "用户管理", + "create_user": "创建用户", + "col_user": "用户", + "col_role": "角色", + "col_auth": "认证", + "col_status": "状态", + "col_storage": "存储", + "col_last_login": "最后登录", + "col_actions": "操作", + "loading_users": "正在加载用户…", + "failed_load_users": "加载失败", + "no_users_found": "未找到用户", + "showing_users": "显示 {{from}}-{{to}} / {{total}}", + "prev": "上一页", + "next": "下一页", + "inactive": "未激活", + "you_badge": "(你)", + "local": "本地", + "never": "从未", + "just_now": "刚刚", + "minutes_ago": "{{n}}分钟前", + "hours_ago": "{{n}}小时前", + "days_ago": "{{n}}天前", + "edit_quota_title": "编辑配额", + "reset_password_title": "重置密码", + "toggle_role_title": "切换角色", + "deactivate_title": "停用", + "activate_title": "启用", + "delete_title": "删除", + "sso_title": "单点登录 (OIDC / SSO)", + "enable_sso": "启用 SSO 认证", + "provider_name": "提供商名称", + "issuer_url": "发行者 URL", + "issuer_url_hint": "您的身份提供商的 OpenID Connect 发行者 URL", + "auto_discover": "自动发现", + "discovering": "发现中…", + "client_id": "客户端 ID", + "client_secret": "客户端密钥", + "client_secret_placeholder": "留空以保留当前值", + "secret_configured": "已配置客户端密钥", + "callback_url": "回调 URL", + "callback_url_hint": "(在您的 IdP 中注册)", + "advanced_settings": "高级设置", + "scopes": "范围", + "auto_provision": "首次登录时自动配置用户", + "admin_groups": "管理组", + "admin_groups_hint": "映射到管理员角色的逗号分隔 OIDC 组名", + "disable_password": "禁用密码登录 (仅 OIDC)", + "password_warning": "这将阻止所有基于密码的登录!", + "test_btn": "测试", + "save_btn": "保存", + "saving": "保存中…", + "settings_saved": "设置已保存 — OIDC 现在 {{status}}", + "quota_modal_title": "更新存储配额", + "quota_user_label": "用户:", + "new_quota": "新配额", + "quota_unlimited_hint": "0表示无限制", + "cancel": "取消", + "create_user_title": "创建新用户", + "username_label": "用户名", + "username_placeholder": "zhangsan", + "username_hint": "3–32个字符", + "password_label": "密码", + "password_placeholder": "至少8个字符", + "email_label": "邮箱", + "email_optional": "(可选)", + "email_placeholder": "user@example.com (留空自动生成)", + "role_label": "角色", + "role_user": "用户", + "role_admin": "管理员", + "quota_label": "配额", + "creating": "创建中…", + "reset_pw_title": "重置密码", + "new_password_label": "新密码", + "resetting": "重置中…", + "reset_btn": "重置", + "confirm_role_change": "将角色更改为 {{role}}?", + "confirm_deactivate": "确定要停用此用户吗?", + "confirm_activate": "确定要启用此用户吗?", + "confirm_delete_user": "删除用户 \"{{name}}\"?此操作无法撤消!", + "confirm_action": "确认操作", + "confirm_yes": "确认", + "confirm_no": "取消", + "error_username_short": "用户名至少需要3个字符", + "error_password_short": "密码至少需要8个字符", + "error_generic": "失败", + "error_network": "网络错误:{{message}}", + "error_create_user": "创建用户失败", + "tab_storage": "存储", + "storage_title": "存储配置", + "storage_current_backend": "当前后端", + "storage_total_blobs": "总块数", + "storage_total_size": "总大小", + "storage_dedup_ratio": "去重比率", + "storage_backend": "后端", + "storage_local": "本地", + "storage_s3": "S3 兼容", + "storage_provider_preset": "提供商预设", + "storage_preset_custom": "自定义", + "storage_endpoint_url": "端点 URL", + "storage_endpoint_hint": "AWS S3 请留空", + "storage_bucket": "存储桶", + "storage_region": "地区", + "storage_access_key": "访问密钥", + "storage_secret_key": "密钥", + "storage_secret_configured": "密钥已配置", + "storage_key_placeholder": "输入新密钥", + "storage_path_style": "强制路径风格", + "storage_path_style_hint": "MinIO 及某些 S3 兼容服务需要此选项", + "storage_test_connection": "测试连接", + "storage_test_success": "连接成功", + "storage_test_failure": "连接失败", + "storage_save": "保存配置", + "storage_saved": "配置已保存", + "storage_migration": "数据迁移", + "storage_migration_coming_soon": "迁移工具即将推出", + "migration_status_label": "迁移状态", + "migration_start": "开始迁移", + "migration_pause": "暂停", + "migration_resume": "继续", + "migration_verify": "验证", + "migration_complete": "完成", + "migration_started": "迁移已开始", + "migration_paused_msg": "迁移已暂停", + "migration_resumed_msg": "迁移已继续", + "migration_completed_msg": "迁移成功完成", + "migration_verifying": "正在验证...", + "migration_verify_passed": "验证通过", + "migration_verify_failed": "验证失败", + "migration_failed_blobs": "失败的块", + "testing": "正在测试...", + "smtp_disabled": "已禁用(未设置主机)", + "smtp_enabled": "已启用", + "smtp_enabled_label": "状态", + "smtp_intro": "SMTP 仅通过环境变量(OXICLOUD_SMTP_*)配置。以下值是从运行中的服务器读取的 — 如需修改,请编辑环境变量并重启 OxiCloud。", + "smtp_not_configured": "此服务器未配置 SMTP。", + "smtp_send_failed": "发送失败。", + "smtp_send_test": "发送测试邮件", + "smtp_sending": "发送中…", + "smtp_sent": "测试邮件已发送。", + "smtp_server_code": "服务器回复", + "smtp_test_intro": "向下方收件人发送预设的诊断消息,并报告 SMTP 服务器的响应,以便您与中继日志进行核对。", + "smtp_test_missing_to": "请输入收件人地址。", + "smtp_test_title": "发送测试邮件", + "smtp_test_to": "收件人地址", + "smtp_title": "出站邮件 (SMTP)", + "tab_smtp": "SMTP" + }, + "profile": { + "page_title": "个人资料", + "back_to_app": "返回 OxiCloud", + "loading": "加载中…", + "not_authenticated": "未认证", + "not_authenticated_desc": "请登录以查看您的个人资料。", + "sign_in": "登录", + "role_admin": "管理员", + "role_user": "用户", + "account_details": "账户详情", + "username": "用户名", + "email": "邮箱", + "role": "角色", + "last_login": "最后登录", + "storage": "存储", + "used": "已使用", + "quota": "配额", + "usage": "使用率", + "unlimited": "无限制", + "app_passwords": "应用密码", + "app_pw_desc": "为 WebDAV、CalDAV 和 CardDAV 客户端生成密码。每个密码只显示一次。", + "app_pw_label_placeholder": "标签(如 Thunderbird、macOS)", + "generate": "生成", + "generating": "生成中…", + "new_password_for": "新密码用于", + "copy_warning": "请立即复制此密码,之后将无法再次查看。", + "copy_to_clipboard": "复制到剪贴板", + "col_label": "标签", + "col_created": "创建时间", + "col_last_used": "最后使用", + "col_status": "状态", + "active": "活跃", + "revoked": "已撤销", + "revoke_title": "撤销", + "no_app_passwords": "暂无应用密码。", + "client_sessions": "客户端会话", + "client_sessions_desc": "连接 Nextcloud 兼容客户端时自动生成。", + "col_client": "客户端", + "never": "从未", + "just_now": "刚刚", + "minutes_ago": "{{n}}分钟前", + "hours_ago": "{{n}}小时前", + "days_ago": "{{n}}天前", + "edit_profile": "编辑个人资料", + "edit_oidc_managed": "要更改您的信息(姓名、名字、头像等),请前往您的身份提供商更新。变更将在您下次登录时显示。", + "username_claim_hint": "2-64 个字符,字母 / 数字 / 点 / 短横线 / 下划线。一旦选定,用户名将无法更改(DAV/NextCloud 客户端依赖它)。", + "username_already_claimed": "用户名已设置,不可更改(DAV/NextCloud 客户端依赖它)。", + "given_name": "名", + "family_name": "姓", + "notify_on_share": "当有人与我共享时通过电子邮件通知我", + "notify_on_share_hint": "取消勾选后,共享项目仍会显示在您的账户中 — 只是不会收到相关邮件通知。", + "save_profile": "保存更改", + "profile_saved": "个人资料已更新", + "profile_no_changes": "无更改可保存。", + "profile_save_failed": "保存失败", + "username_taken_error": "该用户名已被占用。", + "username_immutable_error": "您的用户名已设置,无法在此更改。如需重命名,请联系管理员。", + "change_password": "修改密码", + "current_password": "当前密码", + "new_password": "新密码", + "min_8_chars": "至少8个字符", + "confirm_password": "确认新密码", + "update_password": "更新密码", + "updating": "更新中…", + "password_updated": "密码更新成功", + "passwords_no_match": "密码不匹配", + "password_too_short": "密码至少需要8个字符", + "password_change_failed": "修改密码失败", + "error_network": "网络错误:{{message}}", + "error_label_required": "请输入标签", + "error_create_pw": "创建应用密码失败", + "confirm_revoke": "撤销应用密码\"{{label}}\"?使用此密码的客户端将停止工作。", + "error_revoke": "撤销失败", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider." + }, + "notifications": { + "file_renamed": "文件已重命名", + "file_renamed_to": "文件已重命名为\"{{name}}\"", + "folder_renamed": "文件夹已重命名", + "folder_renamed_to": "文件夹已重命名为\"{{name}}\"", + "file_uploaded": "文件已上传", + "file_deleted": "文件已移至回收站", + "folder_deleted": "文件夹已移至回收站", + "item_deleted_permanently": "项目已永久删除", + "trash_emptied": "回收站已清空", + "title": "通知", + "empty": "暂无通知", + "link_created": "链接已创建", + "share_success": "分享链接创建成功", + "upload_files_section_title": "此处不支持上传", + "upload_files_section_body": "请前往文件部分上传文件" + }, + "upload": { + "uploading": "正在上传...", + "files": "个文件", + "complete": "已上传 {{count}} / {{total}}" + }, + "storage_quota_exceeded": "存储配额已超限", + "sharedwithme": { + "pageTitle": "与我共享", + "pageDescription": "其他用户与您共享的文件和文件夹", + "emptyStateTitle": "暂无内容与您共享", + "emptyStateDesc": "其他用户与您共享的项目将显示在此处", + "loadMore": "加载更多", + "sharedBy": "共享者", + "colName": "名称", + "colType": "类型", + "colSharedBy": "共享者", + "colDate": "共享日期", + "colPermissions": "权限" + }, + "groupby": { + "none": "无", + "title": "分组方式", + "owner": "所有者", + "shareDate": "分享日期", + "type": "类型", + "type.folders": "文件夹", + "accessedAt": "访问日期", + "modifiedAt": "修改日期", + "createdAt": "创建日期", + "size": "大小", + "favoriteDate": "收藏日期", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "新建" + }, + "dateBucket": { + "today": "今天", + "last7days": "近7天", + "last30days": "近30天" + }, + "groups": { + "title": "管理群组", + "create_button": "创建群组", + "create_dialog_title": "新群组", + "edit_dialog_title": "重命名群组", + "name_label": "名称", + "name_placeholder": "engineering", + "description_label": "描述(可选)", + "members_section": "成员", + "add_member_placeholder": "添加用户或群组…", + "no_members": "暂无成员。", + "remove_member": "移除", + "delete_group": "删除群组", + "delete_confirm": "删除群组 \"{name}\"?引用此群组的所有权限将被撤销。", + "empty_state": "暂无群组。", + "load_more": "加载更多", + "back_to_list": "返回", + "loading": "加载中…", + "virtual_badge": "系统", + "member_count_zero": "无成员", + "member_count_one": "1 个成员", + "member_count_other": "{count} 个成员", + "delete_confirm_label": "请输入群组名称以确认:", + "delete_confirm_mismatch": "请准确输入群组名称以确认。", + "virtual_internal_name": "内部", + "members_loading": "正在加载成员…", + "members_empty": "无成员", + "virtual_internal_explanation": "本服务器上的所有内部用户" + }, + "myshares": { + "copyLink": "复制链接", + "deleteLink": "删除链接", + "notifyByEmail": "通过邮件通知", + "notifyFailed": "无法发送通知。", + "notifyGroupMembers": "通知群组成员", + "notifyRateLimited": "对此收件人的通知过多 — 请稍后重试。", + "removeAccess": "移除访问权限", + "resendInvitation": "重新发送邀请邮件" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + } +} diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js new file mode 100644 index 00000000..d11cb7cb --- /dev/null +++ b/frontend/svelte.config.js @@ -0,0 +1,37 @@ +import adapter from '@sveltejs/adapter-static'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** + * SvelteKit config — pure SPA via adapter-static. + * + * Phase 0: output to the local `build/` dir so the existing `static-dist/` + * (still produced by build.rs) is untouched. At cutover (Phase 5) the + * `pages`/`assets` targets switch to `../static-dist` and build.rs stops + * generating assets. + * + * `fallback: 'index.html'` makes every unmatched client route serve the SPA + * shell, which the Rust web layer will mirror with a ServeFile fallback. + * + * @type {import('@sveltejs/kit').Config} + */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter({ + // Cutover: emit the SPA into the repo-root `static-dist/` that the Rust + // web layer serves in release. build.rs no longer generates this dir + // (gated behind OXICLOUD_LEGACY_ASSETS for rollback). + pages: '../static-dist', + assets: '../static-dist', + fallback: 'index.html', + precompress: false, + strict: true + }), + // All routes are client-rendered; SSR/prerender are disabled in +layout.ts. + alias: { + $lib: './src/lib' + } + } +}; + +export default config; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 00000000..a8f10c8e --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 00000000..c0ae1f70 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,33 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vitest/config'; + +// Backend dev server (cargo run) — the Vite dev server proxies API/protocol +// traffic here so cookies, CSRF, and the auth-refresh flow are same-origin. +const BACKEND = process.env.OXICLOUD_BACKEND ?? 'http://localhost:8086'; + +const proxy = { + '/api': { target: BACKEND, changeOrigin: true }, + '/locales': { target: BACKEND, changeOrigin: true }, + '/.well-known': { target: BACKEND, changeOrigin: true }, + '/remote.php': { target: BACKEND, changeOrigin: true }, + '/ocs': { target: BACKEND, changeOrigin: true }, + '/status.php': { target: BACKEND, changeOrigin: true }, + '/webdav': { target: BACKEND, changeOrigin: true }, + '/caldav': { target: BACKEND, changeOrigin: true }, + '/carddav': { target: BACKEND, changeOrigin: true }, + '/wopi': { target: BACKEND, changeOrigin: true } +}; + +export default defineConfig({ + plugins: [sveltekit()], + server: { + port: 5173, + proxy + }, + test: { + environment: 'jsdom', + setupFiles: ['./vitest-setup.ts'], + include: ['src/**/*.{test,spec}.{js,ts}'], + globals: true + } +}); diff --git a/frontend/vitest-setup.ts b/frontend/vitest-setup.ts new file mode 100644 index 00000000..bb02c60c --- /dev/null +++ b/frontend/vitest-setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest'; diff --git a/justfile b/justfile index 29f47595..b931dbf7 100644 --- a/justfile +++ b/justfile @@ -7,9 +7,10 @@ build: cargo build release: + # Build the SvelteKit SPA into static-dist/ (build.rs no longer bundles), + # then compile the release binary which serves it. + cd frontend && npm ci && npm run build cargo build --release - # check that app is clean - node --check static-dist/js/app.*.js run: cargo run @@ -153,3 +154,38 @@ front-design: api-test: bash tests/api/run.sh bash tests/webdav/run.sh + +# --------------------------------------------------------------------------- +# New SvelteKit frontend (frontend/). The legacy vanilla frontend (static/) +# and its `front-*` recipes remain until the Phase 5 cutover; these `fe-*` +# recipes drive the rewrite in the meantime. +# --------------------------------------------------------------------------- + +# install frontend dependencies +fe-install: + cd frontend && npm ci + +# Vite dev server only (HMR) — backend must already be running on :8086 +fe-dev: + cd frontend && npm run dev + +# build the SPA (Phase 0: -> frontend/build; Phase 5: -> static-dist) +fe-build: + cd frontend && npm run build + +# svelte-check + eslint + stylelint + prettier +fe-check: + cd frontend && npm run check + +# Vitest unit/component tests +fe-test: + cd frontend && npm run test:unit + +# Run backend (API) and the Vite dev server together; one Ctrl-C stops both. +dev: + #!/usr/bin/env bash + set -euo pipefail + cargo run & + backend=$! + trap 'kill $backend 2>/dev/null' EXIT INT TERM + cd frontend && npm run dev diff --git a/src/interfaces/web/mod.rs b/src/interfaces/web/mod.rs index 710f601a..37a27cb0 100644 --- a/src/interfaces/web/mod.rs +++ b/src/interfaces/web/mod.rs @@ -1,35 +1,37 @@ use crate::common::config::AppConfig; use crate::common::di::AppState; +use axum::Router; use axum::http::header::{CACHE_CONTROL, HeaderValue}; -use axum::{Router, response::Html, routing::get}; +use axum::routing::get_service; +use std::path::Path; use std::sync::Arc; use tower_http::compression::CompressionLayer; -use tower_http::services::ServeDir; +use tower_http::services::{ServeDir, ServeFile}; use tower_http::set_header::SetResponseHeaderLayer; -/// Creates web routes for serving static files +/// Serves the SvelteKit single-page app. +/// +/// The frontend is built by Vite into `static-dist/` (repo root). Real files are +/// served from disk; any unmatched client route (deep links such as +/// `/files/`, `/s/`, `/login`) falls back to the SPA shell +/// `index.html`, which boots the client router. +/// +/// Caching: content-hashed assets under `/_app/immutable` are cached forever; +/// everything else — crucially the `index.html` shell — is `no-cache` so a deploy +/// can't leave a stale app pinned in browsers. pub fn create_web_routes() -> Router> { - // Get config to access static path let config = AppConfig::from_env(); - // XXX do we prefer PROFILE or ENV ? + // `PROFILE=dev` (the `just front-dev`/legacy path) serves the unbuilt source + // dir; normal release serves the Vite output in `static-dist/`. let is_dev = std::env::var("PROFILE").is_ok_and(|profile| profile == "dev"); + let assets_dir = if is_dev { "static" } else { "static-dist" }; - let assets_dir = if is_dev { - // take directly the source (permits faster development) - "static" - } else { - // take the compiled assets - "static-dist" - }; - - // In release builds, serve from static-dist/ (processed assets). - // In debug builds, serve from the original static/ directory. let static_path = if cfg!(not(debug_assertions)) { let dist = config .static_path .parent() - .unwrap_or(std::path::Path::new(".")) + .unwrap_or(Path::new(".")) .join(assets_dir); if dist.exists() { dist @@ -40,59 +42,31 @@ pub fn create_web_routes() -> Router> { config.static_path.clone() }; - // Static assets (JS, CSS, JSON, SVG, ICO) served via ServeDir - // with brotli + gzip compression and aggressive caching (7 days). - // HTML pages are served via explicit routes (include_str!) and - // do NOT pass through these layers. - let static_service = ServeDir::new(&static_path); + // SPA fallback: serve the file if it exists, else the app shell. + let spa = ServeDir::new(&static_path).fallback(ServeFile::new(static_path.join("index.html"))); - // By default assets are cached in release/production environement and no cache in dev - let cache_control_value = if is_dev { + // Hashed, immutable assets (SvelteKit emits these under /_app/immutable). + let app_immutable = ServeDir::new(static_path.join("_app").join("immutable")); + + let shell_cache = if is_dev { "max-age=0, no-cache, no-store" } else { - "public, max-age=604800, stale-while-revalidate=86400" + "no-cache" }; Router::new() - // Add specific routes for clean URLs (without .html) - .route("/login", get(serve_login_page)) - .route("/profile", get(serve_profile_page)) - .route("/admin", get(serve_admin_page)) - .route("/device", get(serve_device_verify_page)) - .route("/s/{token}", get(serve_share_page)) - // Serve static files with compression + cache headers - .fallback_service(static_service) + .nest_service( + "/_app/immutable", + get_service(app_immutable).layer(SetResponseHeaderLayer::overriding( + CACHE_CONTROL, + HeaderValue::from_static("public, max-age=31536000, immutable"), + )), + ) + .fallback_service(spa) .layer(CompressionLayer::new().br(true).gzip(true)) + // `if_not_present` so the immutable assets above keep their long cache. .layer(SetResponseHeaderLayer::if_not_present( CACHE_CONTROL, - HeaderValue::from_static(cache_control_value), + HeaderValue::from_static(shell_cache), )) } - -/// Serve the login page -async fn serve_login_page() -> Html<&'static str> { - Html(include_str!(concat!(env!("OUT_DIR"), "/login.html"))) -} - -/// Serve the profile page -async fn serve_profile_page() -> Html<&'static str> { - Html(include_str!(concat!(env!("OUT_DIR"), "/profile.html"))) -} - -/// Serve the admin page -async fn serve_admin_page() -> Html<&'static str> { - Html(include_str!(concat!(env!("OUT_DIR"), "/admin.html"))) -} - -/// Serve the device verification page (RFC 8628 Device Authorization Grant) -async fn serve_device_verify_page() -> Html<&'static str> { - Html(include_str!(concat!( - env!("OUT_DIR"), - "/device-verify.html" - ))) -} - -/// Serve the public share page (unauthenticated) -async fn serve_share_page() -> Html<&'static str> { - Html(include_str!(concat!(env!("OUT_DIR"), "/share.html"))) -} From 89e14f8f9e3504429a1eff60fa01d7766069c66e Mon Sep 17 00:00:00 2001 From: Bradley Nelson Date: Wed, 17 Jun 2026 22:07:18 -0600 Subject: [PATCH 2/6] visual continunity --- Dockerfile | 2 +- build.rs | 12 +- frontend/.prettierignore | 6 +- frontend/.stylelintignore | 4 +- frontend/eslint.config.js | 4 +- frontend/src/lib/api/client.ts | 4 +- frontend/src/lib/api/endpoints/admin.ts | 286 ++- frontend/src/lib/api/endpoints/auth.ts | 121 + frontend/src/lib/api/endpoints/batch.ts | 35 + frontend/src/lib/api/endpoints/deltaUpload.ts | 130 + frontend/src/lib/api/endpoints/device.ts | 24 +- frontend/src/lib/api/endpoints/favorites.ts | 100 + frontend/src/lib/api/endpoints/files.ts | 30 + frontend/src/lib/api/endpoints/grants.ts | 151 +- frontend/src/lib/api/endpoints/groups.ts | 84 +- frontend/src/lib/api/endpoints/music.ts | 71 +- frontend/src/lib/api/endpoints/photos.ts | 76 + frontend/src/lib/api/endpoints/profile.ts | 82 +- frontend/src/lib/api/endpoints/recipients.ts | 166 ++ frontend/src/lib/api/endpoints/resources.ts | 2 +- frontend/src/lib/api/endpoints/search.ts | 77 + frontend/src/lib/api/endpoints/share.ts | 5 +- frontend/src/lib/api/endpoints/shares.ts | 110 + frontend/src/lib/api/endpoints/trash.ts | 76 + frontend/src/lib/api/endpoints/wopi.ts | 85 + frontend/src/lib/components/AppShell.svelte | 1077 +++++++- .../src/lib/components/CommandPalette.svelte | 384 +++ frontend/src/lib/components/DialogHost.svelte | 118 + frontend/src/lib/components/FileRow.svelte | 79 - frontend/src/lib/components/FileViewer.svelte | 401 +++ .../src/lib/components/ListToolbar.svelte | 125 + frontend/src/lib/components/Modal.svelte | 90 +- frontend/src/lib/components/MoveDialog.svelte | 273 ++ .../src/lib/components/ResourceList.svelte | 709 ++++++ .../lib/components/ResourceListShell.svelte | 95 - .../src/lib/components/ShareDialog.svelte | 846 +++++++ frontend/src/lib/components/Toaster.svelte | 9 +- frontend/src/lib/components/WopiEditor.svelte | 173 ++ frontend/src/lib/i18n/index.svelte.ts | 50 +- frontend/src/lib/stores/dialogs.svelte.ts | 113 + frontend/src/lib/stores/files.svelte.ts | 74 +- frontend/src/lib/stores/session.svelte.ts | 2 +- frontend/src/lib/stores/theme.svelte.ts | 2 +- frontend/src/lib/stores/ui.svelte.ts | 152 +- frontend/src/lib/styles/app.css | 2 +- frontend/src/lib/styles/legacy.css | 13 - frontend/src/lib/styles/ported.css | 18 + .../lib/styles/{legacy => ported}/auth.css | 0 .../src/lib/styles/ported/batchToolbar.css | 101 + .../styles/{legacy => ported}/breadcrumb.css | 0 .../lib/styles/{legacy => ported}/buttons.css | 0 .../lib/styles/{legacy => ported}/content.css | 0 .../styles/{legacy => ported}/fileManager.css | 0 frontend/src/lib/styles/ported/music.css | 1435 +++++++++++ .../src/lib/styles/ported/notifications.css | 371 +++ .../{legacy => ported}/resourceList.css | 20 + .../lib/styles/{legacy => ported}/sidebar.css | 0 .../styles/{legacy => ported}/skeleton.css | 0 .../lib/styles/{legacy => ported}/topbar.css | 0 .../src/lib/styles/ported/uploadDropdown.css | 78 + frontend/src/lib/styles/ported/userMenu.css | 294 +++ frontend/src/lib/utils/display.ts | 2 +- frontend/src/lib/utils/hashRedirect.test.ts | 34 + .../utils/{legacyHash.ts => hashRedirect.ts} | 8 +- frontend/src/lib/utils/imageResize.ts | 38 + frontend/src/lib/utils/legacyHash.test.ts | 34 - frontend/src/routes/+layout.svelte | 8 +- frontend/src/routes/admin/+page.svelte | 2225 ++++++++++++++++- frontend/src/routes/device/+page.svelte | 86 +- frontend/src/routes/favorites/+page.svelte | 340 ++- .../src/routes/files/[...path]/+page.svelte | 1641 ++++++++++-- frontend/src/routes/groups/+page.svelte | 384 ++- frontend/src/routes/login/+page.svelte | 633 ++++- frontend/src/routes/music/+page.svelte | 1182 +++++++-- .../src/routes/nextcloud/error/+page.svelte | 86 +- .../src/routes/nextcloud/login/+page.svelte | 158 +- .../src/routes/nextcloud/success/+page.svelte | 25 + frontend/src/routes/photos/+page.svelte | 801 +++++- frontend/src/routes/profile/+page.svelte | 1013 +++++++- frontend/src/routes/recent/+page.svelte | 384 ++- frontend/src/routes/s/[token]/+page.svelte | 458 +++- frontend/src/routes/search/+page.svelte | 398 +++ .../src/routes/shared-with-me/+page.svelte | 74 +- frontend/src/routes/shared/+page.svelte | 850 ++++++- frontend/src/routes/trash/+page.svelte | 232 +- .../vendors/hash-wasm/oxicloud_hash_wasm.js | 343 +++ .../hash-wasm/oxicloud_hash_wasm_bg.wasm | Bin 0 -> 63589 bytes frontend/static/workers/deltaWorker.js | 330 +++ justfile | 2 +- 89 files changed, 19249 insertions(+), 1367 deletions(-) create mode 100644 frontend/src/lib/api/endpoints/batch.ts create mode 100644 frontend/src/lib/api/endpoints/deltaUpload.ts create mode 100644 frontend/src/lib/api/endpoints/recipients.ts create mode 100644 frontend/src/lib/api/endpoints/search.ts create mode 100644 frontend/src/lib/api/endpoints/shares.ts create mode 100644 frontend/src/lib/api/endpoints/wopi.ts create mode 100644 frontend/src/lib/components/CommandPalette.svelte create mode 100644 frontend/src/lib/components/DialogHost.svelte delete mode 100644 frontend/src/lib/components/FileRow.svelte create mode 100644 frontend/src/lib/components/FileViewer.svelte create mode 100644 frontend/src/lib/components/ListToolbar.svelte create mode 100644 frontend/src/lib/components/MoveDialog.svelte create mode 100644 frontend/src/lib/components/ResourceList.svelte delete mode 100644 frontend/src/lib/components/ResourceListShell.svelte create mode 100644 frontend/src/lib/components/ShareDialog.svelte create mode 100644 frontend/src/lib/components/WopiEditor.svelte create mode 100644 frontend/src/lib/stores/dialogs.svelte.ts delete mode 100644 frontend/src/lib/styles/legacy.css create mode 100644 frontend/src/lib/styles/ported.css rename frontend/src/lib/styles/{legacy => ported}/auth.css (100%) create mode 100644 frontend/src/lib/styles/ported/batchToolbar.css rename frontend/src/lib/styles/{legacy => ported}/breadcrumb.css (100%) rename frontend/src/lib/styles/{legacy => ported}/buttons.css (100%) rename frontend/src/lib/styles/{legacy => ported}/content.css (100%) rename frontend/src/lib/styles/{legacy => ported}/fileManager.css (100%) create mode 100644 frontend/src/lib/styles/ported/music.css create mode 100644 frontend/src/lib/styles/ported/notifications.css rename frontend/src/lib/styles/{legacy => ported}/resourceList.css (97%) rename frontend/src/lib/styles/{legacy => ported}/sidebar.css (100%) rename frontend/src/lib/styles/{legacy => ported}/skeleton.css (100%) rename frontend/src/lib/styles/{legacy => ported}/topbar.css (100%) create mode 100644 frontend/src/lib/styles/ported/uploadDropdown.css create mode 100644 frontend/src/lib/styles/ported/userMenu.css create mode 100644 frontend/src/lib/utils/hashRedirect.test.ts rename frontend/src/lib/utils/{legacyHash.ts => hashRedirect.ts} (71%) create mode 100644 frontend/src/lib/utils/imageResize.ts delete mode 100644 frontend/src/lib/utils/legacyHash.test.ts create mode 100644 frontend/src/routes/search/+page.svelte create mode 100644 frontend/static/vendors/hash-wasm/oxicloud_hash_wasm.js create mode 100644 frontend/static/vendors/hash-wasm/oxicloud_hash_wasm_bg.wasm create mode 100644 frontend/static/workers/deltaWorker.js diff --git a/Dockerfile b/Dockerfile index 1f34c157..311638e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,7 @@ COPY templates templates ARG DATABASE_URL="postgres://postgres:postgres@localhost/oxicloud" RUN DATABASE_URL="${DATABASE_URL}" cargo build --release # The SPA is built by the frontend stage; bring it in for the runtime copy below. -# (build.rs no longer generates static-dist unless OXICLOUD_LEGACY_ASSETS=1.) +# (build.rs no longer generates static-dist unless OXICLOUD_RUST_ASSETS=1.) COPY --from=frontend /static-dist ./static-dist # ─── Stage 4: Minimal runtime image ────────────────────────────────────────── diff --git a/build.rs b/build.rs index 1ae891bb..64333458 100644 --- a/build.rs +++ b/build.rs @@ -40,15 +40,15 @@ fn main() { println!("cargo:rerun-if-changed=static"); println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=OXICLOUD_LEGACY_ASSETS"); + println!("cargo:rerun-if-env-changed=OXICLOUD_RUST_ASSETS"); git_status(); - // Post-cutover (Svelte/Vite): the frontend is built by Vite into - // `static-dist/` and the Rust web layer serves it directly — no `include_str!` - // HTML, no Rust-side bundling. The legacy pure-Rust asset pipeline below is - // retained, behind `OXICLOUD_LEGACY_ASSETS=1`, for one-release rollback only. - if env_or("OXICLOUD_LEGACY_ASSETS", "0") != "1" { + // The frontend is built by Vite into `static-dist/` and the Rust web layer + // serves it directly — no `include_str!` HTML, no Rust-side bundling. The + // pure-Rust asset pipeline below is retained, behind `OXICLOUD_RUST_ASSETS=1`, + // for one-release rollback only. + if env_or("OXICLOUD_RUST_ASSETS", "0") != "1" { return; } diff --git a/frontend/.prettierignore b/frontend/.prettierignore index 1ddced4a..45415cfe 100644 --- a/frontend/.prettierignore +++ b/frontend/.prettierignore @@ -7,7 +7,9 @@ package-lock.json src/lib/i18n/locales/ # vendored / generated — kept byte-faithful to their source src/lib/styles/base/ -src/lib/styles/legacy/ -src/lib/styles/legacy.css +src/lib/styles/ported/ +src/lib/styles/ported.css src/lib/icons/registry.ts static/locales/ +static/vendors/ +static/workers/ diff --git a/frontend/.stylelintignore b/frontend/.stylelintignore index 7020a179..8c2dc071 100644 --- a/frontend/.stylelintignore +++ b/frontend/.stylelintignore @@ -4,5 +4,5 @@ node_modules/ # Ported verbatim from static/css/base — treated as vendored design tokens. # New component styles (Svelte diff --git a/frontend/src/lib/components/CommandPalette.svelte b/frontend/src/lib/components/CommandPalette.svelte new file mode 100644 index 00000000..43958019 --- /dev/null +++ b/frontend/src/lib/components/CommandPalette.svelte @@ -0,0 +1,384 @@ + + + + +{#if open} + +{/if} + + diff --git a/frontend/src/lib/components/DialogHost.svelte b/frontend/src/lib/components/DialogHost.svelte new file mode 100644 index 00000000..c92a9a9a --- /dev/null +++ b/frontend/src/lib/components/DialogHost.svelte @@ -0,0 +1,118 @@ + + +{#if dialogs.current} + {@const c = dialogs.current} + dialogs.cancel()}> + {#if c.kind === 'prompt'} +
    + {#if c.opts.message}

    {c.opts.message}

    {/if} + +
    + {:else if c.opts.message} +

    {c.opts.message}

    + {/if} + + {#if dialogs.error} + + {/if} + + {#snippet footer()} + + {#if c.kind === 'prompt'} + + {:else} + + {/if} + {/snippet} +
    +{/if} + + diff --git a/frontend/src/lib/components/FileRow.svelte b/frontend/src/lib/components/FileRow.svelte deleted file mode 100644 index 1a153aac..00000000 --- a/frontend/src/lib/components/FileRow.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - -
  • - - - {name} - {#if subtitle}{subtitle}{/if} - - {#if date}{date}{/if} - {#if actions}{@render actions()}{/if} -
  • - - diff --git a/frontend/src/lib/components/FileViewer.svelte b/frontend/src/lib/components/FileViewer.svelte new file mode 100644 index 00000000..1821e41b --- /dev/null +++ b/frontend/src/lib/components/FileViewer.svelte @@ -0,0 +1,401 @@ + + + + +{#if open && file} + + + + { + onrefresh?.(); + // If the editor was auto-opened for an Office doc, closing it should + // dismiss the whole viewer (there's nothing to preview behind it). + if (kind === 'other') close(); + }} + /> +{/if} + + diff --git a/frontend/src/lib/components/ListToolbar.svelte b/frontend/src/lib/components/ListToolbar.svelte new file mode 100644 index 00000000..0a1d9254 --- /dev/null +++ b/frontend/src/lib/components/ListToolbar.svelte @@ -0,0 +1,125 @@ + + + + +
    + {#if start}{@render start()}{:else}
    {/if} + + {#if groups?.length || showViewToggle} +
    + {#if groups?.length} +
    + + + {#if menuOpen} +
    + {#each groups as g (g.key)} + + {/each} +
    + {/if} +
    + {#if showViewToggle}{/if} + {/if} + {#if showViewToggle} + + + {/if} +
    + {/if} +
    diff --git a/frontend/src/lib/components/Modal.svelte b/frontend/src/lib/components/Modal.svelte index 0402b4cf..eb1bb642 100644 --- a/frontend/src/lib/components/Modal.svelte +++ b/frontend/src/lib/components/Modal.svelte @@ -12,14 +12,60 @@ let { open = $bindable(false), title, onclose, children, footer }: Props = $props(); + let dialogEl = $state(null); + let prevFocus: HTMLElement | null = null; + function close() { open = false; onclose?.(); } - function onkeydown(e: KeyboardEvent) { - if (e.key === 'Escape') close(); + const FOCUSABLE = + 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'; + + function focusables(): HTMLElement[] { + if (!dialogEl) return []; + return Array.from(dialogEl.querySelectorAll(FOCUSABLE)).filter( + (el) => el.offsetParent !== null || el === document.activeElement + ); } + + function onkeydown(e: KeyboardEvent) { + if (e.key === 'Escape') { + close(); + return; + } + // Focus trap: keep Tab cycling inside the dialog. + if (e.key === 'Tab') { + const items = focusables(); + if (items.length === 0) return; + const first = items[0]; + const last = items[items.length - 1]; + const active = document.activeElement as HTMLElement | null; + if (e.shiftKey && active === first) { + e.preventDefault(); + last.focus(); + } else if (!e.shiftKey && active === last) { + e.preventDefault(); + first.focus(); + } + } + } + + // On open: remember the previously focused element and move focus into the + // dialog. On close: restore focus so keyboard users aren't dumped at . + $effect(() => { + if (open) { + prevFocus = (document.activeElement as HTMLElement | null) ?? null; + requestAnimationFrame(() => { + const items = focusables(); + (items[0] ?? dialogEl)?.focus(); + }); + } else if (prevFocus) { + prevFocus.focus(); + prevFocus = null; + } + }); @@ -33,7 +79,14 @@ if (e.target === e.currentTarget) close(); }} > - + + diff --git a/frontend/src/routes/music/+page.svelte b/frontend/src/routes/music/+page.svelte index d2d3eb7c..c8536cf4 100644 --- a/frontend/src/routes/music/+page.svelte +++ b/frontend/src/routes/music/+page.svelte @@ -2,16 +2,27 @@ import { onMount } from 'svelte'; import { fileInlineUrl } from '$lib/api/endpoints/files'; import { + addTracks, createPlaylist, deletePlaylist, listPlaylists, + listShares, listTracks, + removeShare, removeTrack, + renamePlaylist, reorderTracks, + sharePlaylist, + updatePlaylist, + uploadCoverImage, + type MusicShare, type Playlist, type PlaylistItem } from '$lib/api/endpoints/music'; + import { searchFiles } from '$lib/api/endpoints/search'; + import type { FileItem } from '$lib/api/types'; import Icon from '$lib/icons/Icon.svelte'; + import { confirmDialog, promptDialog } from '$lib/stores/dialogs.svelte'; import { t } from '$lib/i18n/index.svelte'; import { ui } from '$lib/stores/ui.svelte'; @@ -20,11 +31,35 @@ let tracks = $state([]); let loading = $state(false); let error = $state(null); - let nowPlaying = $state(null); - // native HTML5 drag-reorder state let dragIndex = $state(null); + // ── Player (independent global queue) ────────────────────────────────────── + let audio = $state(null); + /** Playback queue — independent of the visible `tracks` list. */ + let queue = $state([]); + let currentIndex = $state(-1); + let playing = $state(false); + let currentTime = $state(0); + let duration = $state(0); + let volume = $state(0.7); + let muted = $state(false); + let shuffle = $state(false); + let repeat = $state<'none' | 'all' | 'one'>('none'); + let queueOpen = $state(false); + + const currentTrack = $derived(currentIndex >= 0 ? (queue[currentIndex] ?? null) : null); + + function fmtTime(s: number | null | undefined): string { + if (s == null || !Number.isFinite(s)) return '0:00'; + const m = Math.floor(s / 60); + const sec = Math.floor(s % 60); + return `${m}:${sec.toString().padStart(2, '0')}`; + } + function fmtDuration(s: number | null | undefined): string { + return s ? fmtTime(s) : '-'; + } + async function loadPlaylists() { loading = true; error = null; @@ -40,6 +75,7 @@ async function select(p: Playlist) { current = p; + // NOTE: do NOT touch the player here — browsing a playlist must not stop playback. try { tracks = await listTracks(p.id); } catch (e) { @@ -48,20 +84,65 @@ } async function onCreate() { - const name = prompt(t('music.new_playlist', 'New playlist name')); + const name = await promptDialog({ + title: t('music.new_playlist', 'New playlist'), + confirmText: t('common.create', 'Create') + }); if (!name) return; try { const p = await createPlaylist(name); - playlists = [...playlists, p]; + playlists = [p, ...playlists]; await select(p); + ui.notify(t('music.created', { name: p.name }, 'Created “{{name}}”.'), 'success'); + } catch (e) { + ui.notify(e instanceof Error ? e.message : String(e), 'error'); + } + } + + async function onRenamePlaylist() { + if (!current) return; + const name = await promptDialog({ + title: t('music.rename_playlist', 'Rename playlist'), + defaultValue: current.name, + confirmText: t('common.save', 'Save') + }); + if (!name || name === current.name) return; + try { + await renamePlaylist(current.id, name); + current.name = name; + playlists = playlists.map((p) => (p.id === current!.id ? { ...p, name } : p)); + } catch (e) { + ui.notify(e instanceof Error ? e.message : String(e), 'error'); + } + } + + async function onEditDescription() { + if (!current) return; + const desc = await promptDialog({ + title: t('music.edit_description', 'Edit description'), + defaultValue: current.description ?? '', + confirmText: t('common.save', 'Save') + }); + if (desc === null) return; + try { + await updatePlaylist(current.id, { description: desc || null }); + current.description = desc || null; + playlists = playlists.map((p) => + p.id === current!.id ? { ...p, description: desc || null } : p + ); } catch (e) { ui.notify(e instanceof Error ? e.message : String(e), 'error'); } } async function onDelete(p: Playlist) { - if (!confirm(t('music.confirm_delete', { name: p.name }, 'Delete playlist "{{name}}"?'))) - return; + const ok = await confirmDialog({ + title: t('music.delete_playlist', 'Delete playlist'), + message: t('music.confirm_delete', { name: p.name }, 'Delete playlist "{{name}}"?'), + confirmText: t('common.delete', 'Delete'), + danger: true + }); + if (!ok) return; try { await deletePlaylist(p.id); playlists = playlists.filter((x) => x.id !== p.id); @@ -69,6 +150,7 @@ current = playlists[0] ?? null; tracks = current ? await listTracks(current.id) : []; } + ui.notify(t('music.deleted', { name: p.name }, 'Deleted “{{name}}”.'), 'success'); } catch (e) { ui.notify(e instanceof Error ? e.message : String(e), 'error'); } @@ -79,6 +161,25 @@ try { await removeTrack(current.id, track.file_id); tracks = tracks.filter((x) => x.id !== track.id); + ui.notify(t('music.track_removed', 'Track removed.'), 'success'); + } catch (e) { + ui.notify(e instanceof Error ? e.message : String(e), 'error'); + } + } + + async function onTogglePublic() { + if (!current) return; + const next = !current.is_public; + try { + await updatePlaylist(current.id, { is_public: next }); + current.is_public = next; + playlists = playlists.map((p) => (p.id === current!.id ? { ...p, is_public: next } : p)); + ui.notify( + next + ? t('music.now_public', 'Playlist is now public.') + : t('music.now_private', 'Playlist is now private.'), + 'success' + ); } catch (e) { ui.notify(e instanceof Error ? e.message : String(e), 'error'); } @@ -87,7 +188,6 @@ function onDragStart(i: number) { dragIndex = i; } - function onDragOver(e: DragEvent, i: number) { e.preventDefault(); if (dragIndex === null || dragIndex === i) return; @@ -97,7 +197,6 @@ dragIndex = i; tracks = next; } - async function onDrop() { dragIndex = null; if (!current) return; @@ -106,9 +205,10 @@ current.id, tracks.map((tr) => tr.id) ); + ui.notify(t('music.reordered', 'Playlist reordered.'), 'success'); } catch (e) { ui.notify(e instanceof Error ? e.message : String(e), 'error'); - await select(current); // reload server order on failure + await select(current); } } @@ -116,245 +216,859 @@ return tr.title || tr.file_name || tr.file_id; } + // ── Transport (operates on the independent queue) ────────────────────────── + /** Replace the queue (e.g. when starting playback of the visible playlist). */ + function setQueue(list: PlaylistItem[]) { + queue = [...list]; + } + + function playIndex(i: number) { + if (i < 0 || i >= queue.length) return; + currentIndex = i; + // $effect swaps the src; ensure playback starts. + queueMicrotask(() => audio?.play().catch(() => {})); + } + + /** Play the visible playlist from a given row, seeding the queue from it. */ + function playFromTracks(i: number) { + if (i < 0 || i >= tracks.length) return; + setQueue(tracks); + playIndex(i); + } + + function playAll() { + if (tracks.length) playFromTracks(0); + } + + function shufflePlay() { + if (!tracks.length) return; + const shuffled = [...tracks]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + setQueue(shuffled); + playIndex(0); + } + + function togglePlay() { + if (!audio) return; + if (currentIndex < 0 && queue.length) { + playIndex(0); + return; + } + if (playing) audio.pause(); + else audio.play().catch(() => {}); + } + function next() { + if (!queue.length) return; + if (shuffle) { + playIndex(Math.floor(Math.random() * queue.length)); + return; + } + if (currentIndex + 1 < queue.length) playIndex(currentIndex + 1); + else if (repeat === 'all') playIndex(0); + } + function prev() { + if (!queue.length) return; + if (currentTime > 3 && audio) { + audio.currentTime = 0; + return; + } + // Wrap to the last track when at the start (OLD behavior). + playIndex(currentIndex > 0 ? currentIndex - 1 : queue.length - 1); + } + function onEnded() { + if (repeat === 'one') { + if (audio) audio.currentTime = 0; + audio?.play().catch(() => {}); + return; + } + next(); + } + function seek(e: Event) { + const v = Number((e.target as HTMLInputElement).value); + if (audio) audio.currentTime = v; + } + function applyVolume() { + if (audio) { + audio.volume = volume; + audio.muted = muted; + } + } + function setVolume(e: Event) { + volume = Number((e.target as HTMLInputElement).value); + muted = volume === 0; + applyVolume(); + } + function toggleMute() { + muted = !muted; + applyVolume(); + } + function cycleRepeat() { + repeat = repeat === 'none' ? 'all' : repeat === 'all' ? 'one' : 'none'; + } + + const volumeIcon = $derived(muted || volume === 0 ? 'volume' : 'volume-up'); + + function jumpQueue(i: number) { + playIndex(i); + } + function removeFromQueue(i: number) { + const next = [...queue]; + next.splice(i, 1); + if (i === currentIndex) { + queue = next; + if (next.length === 0) { + audio?.pause(); + currentIndex = -1; + } else { + playIndex(i >= next.length ? 0 : i); + } + } else { + if (i < currentIndex) currentIndex -= 1; + queue = next; + } + } + + /** Live duration backfill: write the real duration into rows/queue lacking it. */ + function onLoadedMetadata() { + duration = audio?.duration ?? 0; + const tr = currentTrack; + if (tr && audio?.duration && !tr.duration_secs) { + const secs = Math.round(audio.duration); + tr.duration_secs = secs; + queue = queue.map((q) => (q.id === tr.id ? { ...q, duration_secs: secs } : q)); + tracks = tracks.map((q) => (q.id === tr.id ? { ...q, duration_secs: secs } : q)); + } + } + + function onAudioError() { + if (!currentTrack) return; + ui.notify( + t('music.playback_error', { name: trackLabel(currentTrack) }, 'Playback error: {{name}}'), + 'error' + ); + playing = false; + } + + // Keep the
    +
    + {#if current} +
    +
    + +
    +

    {current.name}

    +

    + {t('music.track_count', { n: current.track_count }, '{{n}} tracks')} + {#if current.description}· {current.description}{/if} +

    + {#if current.is_public} + + {t('music.public', 'Public')} + + {/if} +
    +
    + +
    + + + + + + + + +
    + +
    + {#if tracks.length === 0} +
    + +

    {t('music.empty_playlist', 'This playlist has no tracks yet.')}

    +
    + {:else} +
    + + # + {t('music.title', 'Title')} + {t('music.artist', 'Artist')} + {t('music.album', 'Album')} + + +
    + {#each tracks as track, i (track.id)} + + +
    playFromTracks(i)} + ondragstart={() => onDragStart(i)} + ondragover={(e) => onDragOver(e, i)} + ondrop={onDrop} + ondragend={() => (dragIndex = null)} + > + + + + + { + e.stopPropagation(); + if (currentTrack?.id === track.id) togglePlay(); + else playFromTracks(i); + }} + > + {#if currentTrack?.id === track.id} + + {:else} + {i + 1} + {/if} + + + + + {trackLabel(track)} + + {track.artist || '—'} + {track.album || '-'} + {fmtDuration(track.duration_secs)} + + + +
    + {/each} + {/if} +
    +
    + {:else} +
    + +

    {t('music.select_playlist', 'Select a playlist.')}

    +

    {t('music.select_hint', 'Pick a playlist to view its tracks.')}

    +
    + {/if} +
    + + {/if} + +{#if currentTrack} +
    +
    +
    + {#if coverUrl(current)} + + {:else} + + {/if} +
    +
    + {trackLabel(currentTrack)} + {currentTrack.artist ?? ''} +
    +
    + +
    +
    + + + + + +
    +
    + {fmtTime(currentTime)} + + {fmtTime(duration)} +
    +
    + +
    + + +
    + +
    +
    +
    + + {#if queueOpen} +
    +
    +

    {t('music.queue', 'Queue')}

    + +
    +
    + {#if queue.length === 0} +
    + +

    {t('music.queue_empty', 'Queue is empty.')}

    +
    + {:else} + {#each queue as qt, i (qt.id)} + + +
    jumpQueue(i)} + > + {i + 1} + + {trackLabel(qt)} + {qt.artist ?? ''} + + {fmtDuration(qt.duration_secs)} + +
    + {/each} + {/if} +
    +
    + {/if} +{/if} + + + + + +{#if addOpen} + + +
    { + if (e.target === e.currentTarget) addOpen = false; + }} + > +
    +
    +

    {t('music.add_tracks', 'Add tracks')}

    + +
    + +
    + {#if addSearching} +
    + + {t('common.loading', 'Loading…')} +
    + {:else if addResults.length === 0} +
    + + {t('music.no_audio', 'No audio files found.')} +
    + {:else} + {#each addResults as f (f.id)} + + {/each} + {/if} +
    + +
    +
    +{/if} + +{#if sharesOpen} + + +
    { + if (e.target === e.currentTarget) sharesOpen = false; + }} + > +
    +
    +

    {t('music.manage_shares', 'Manage shares')}

    + +
    +
    + {#if sharesLoading} +
    + {:else if shares.length === 0} +

    {t('music.no_shares', 'Not shared with anyone yet.')}

    + {:else} + {#each shares as s (s.user_id)} + + {/each} + {/if} +
    +
    + + + +
    +
    +
    +{/if} + diff --git a/frontend/src/routes/nextcloud/error/+page.svelte b/frontend/src/routes/nextcloud/error/+page.svelte index 60c40da8..498161d1 100644 --- a/frontend/src/routes/nextcloud/error/+page.svelte +++ b/frontend/src/routes/nextcloud/error/+page.svelte @@ -3,23 +3,74 @@ import Icon from '$lib/icons/Icon.svelte'; import { t } from '$lib/i18n/index.svelte'; - const reason = $derived(page.url.searchParams.get('reason') ?? ''); + type ErrorAction = 'retry' | 'close'; + interface ErrorView { + title: string; + message: string; + actionLabel: string; + action: ErrorAction; + } + + // Legacy used `?type=`; the rewrite briefly renamed it to `?reason=`. Read + // `type` first and fall back to `reason` so older links keep working. + const errorType = $derived( + page.url.searchParams.get('type') ?? page.url.searchParams.get('reason') ?? 'generic' + ); + + const view = $derived(buildView(errorType)); + + function buildView(type: string): ErrorView { + switch (type) { + case 'invalid-credentials': + return { + title: t('nextcloud.error_invalid_title', 'Login Failed'), + message: t( + 'nextcloud.error_invalid_body', + 'Invalid username or password. Please check your credentials and try again.' + ), + actionLabel: t('common.retry', 'Try Again'), + action: 'retry' + }; + case 'session-expired': + return { + title: t('nextcloud.error_expired_title', 'Session Expired'), + message: t('nextcloud.error_expired_body', 'Your session has expired. Please try again.'), + actionLabel: t('nextcloud.close_window', 'Close Window'), + action: 'close' + }; + case 'not-found': + return { + title: t('nextcloud.error_notfound_title', 'Not Found'), + message: t('nextcloud.error_notfound_body', 'The requested page was not found.'), + actionLabel: t('nextcloud.close_window', 'Close Window'), + action: 'close' + }; + default: + return { + title: t('nextcloud.error_title', 'Error'), + message: t( + 'nextcloud.error_generic_body', + 'An unexpected error occurred. Please try again.' + ), + actionLabel: t('nextcloud.close_window', 'Close Window'), + action: 'close' + }; + } + } + + function onAction() { + if (view.action === 'retry') history.back(); + else window.close(); + } -{t('nextcloud.error_title', 'Something went wrong')} · OxiCloud +{view.title} · OxiCloud
    -

    {t('nextcloud.error_title', 'Something went wrong')}

    -

    - {t( - 'nextcloud.error_body', - 'The connection could not be completed. Please try again from your application.' - )} -

    - {#if reason}

    {reason}

    {/if} +

    {view.title}

    +

    {view.message}

    +
    diff --git a/frontend/src/routes/nextcloud/login/+page.svelte b/frontend/src/routes/nextcloud/login/+page.svelte index 53457a93..2e039f52 100644 --- a/frontend/src/routes/nextcloud/login/+page.svelte +++ b/frontend/src/routes/nextcloud/login/+page.svelte @@ -1,7 +1,7 @@ {t('app.title', 'OxiCloud')} -
    -
    -

    {t('nextcloud.grant_title', 'Grant access')}

    +
    +
    + + +

    {t('nextcloud.grant_title', 'Grant access')}

    +

    + {t('nextcloud.grant_subtitle', 'A Nextcloud client is requesting access to your account.')} +

    {#if !validToken} -

    {t('nextcloud.invalid_token', 'Invalid session token.')}

    + {:else} {#if passwordLoginEnabled} -
    - - - + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    {/if} {#if oidcEnabled} - + {#if passwordLoginEnabled} +
    {t('auth.or', 'or')}
    + {/if} + + {t('nextcloud.sign_in_with', { provider: oidcProvider }, 'Sign in with {{provider}}')} + {/if} {/if}
    -
    - - + diff --git a/frontend/src/routes/nextcloud/success/+page.svelte b/frontend/src/routes/nextcloud/success/+page.svelte index d6437217..ca662698 100644 --- a/frontend/src/routes/nextcloud/success/+page.svelte +++ b/frontend/src/routes/nextcloud/success/+page.svelte @@ -1,6 +1,18 @@ {t('nextcloud.success_title', 'Access granted')} · OxiCloud

    {t('nextcloud.success_title', 'Access granted')}

    {t('nextcloud.success_body', 'You can now return to your application — it is connected.')}

    + diff --git a/frontend/src/routes/photos/+page.svelte b/frontend/src/routes/photos/+page.svelte index 67e8ad87..7f4f2b81 100644 --- a/frontend/src/routes/photos/+page.svelte +++ b/frontend/src/routes/photos/+page.svelte @@ -1,9 +1,19 @@ {t('nav.photos', 'Photos')} · OxiCloud + -

    {t('nav.photos', 'Photos')}

    +
    +

    {t('nav.photos', 'Photos')}

    +
    + {#each MODES as m (m)} + + {/each} +
    +
    + +{#if selected.size > 0} +
    + {t('files.selected_count', { n: selected.size }, '{{n}} selected')} +
    + + + +
    +
    +{/if} {#if error} {:else if items.length === 0 && exhausted} -

    {t('photos.empty', 'No photos yet.')}

    +
    + +

    {t('photos.empty', 'No photos yet.')}

    +

    + {t('photos.empty_hint', 'Photos and videos you upload will appear here, grouped by date.')} +

    +
    {:else} -
      - {#each items as photo (photo.id)} -
    • - - {photo.name} - -
    • - {/each} -
    + {#each groups as group (group.key)} +

    + {group.label} {group.photos.length} +

    +
      + {#each group.photos as photo (photo.id)} +
    • + + +
    • + {/each} +
    + {/each} {/if} {#if loading}

    {t('common.loading', 'Loading…')}

    {/if} +{#if lbItem} + + +{/if} + diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte index 64d77e65..bdbd77e1 100644 --- a/frontend/src/routes/profile/+page.svelte +++ b/frontend/src/routes/profile/+page.svelte @@ -1,9 +1,25 @@ @@ -79,57 +305,390 @@

    {t('nav.profile', 'Profile')}

    {#if session.user} - + +
    +
    + {#if session.user.image && !avatarImgFailed} + {initials} (avatarImgFailed = true)} + /> + {:else} + {initials} + {/if} +
    +

    {session.user.username || session.user.email || '—'}

    +
    {session.user.email}
    + + + {isAdmin ? t('profile.role_admin', 'Administrator') : t('profile.role_user', 'User')} + + {#if isOidc && session.user.image} +

    + {t('profile.photo_managed_by_oidc', 'Photo managed by your identity provider.')} +

    + {/if} +
    + {#if canEditImage} + + {/if} +
    -
    -

    {t('profile.details', 'Profile details')}

    + {#if canEditImage && avatarEditOpen} +
    +
    + + +
    - - - - - + {#if avatarTab === 'url'} + + + {t('profile.photo_url_hint', 'https://, http://, or data:image/…;base64,… accepted')} + + {:else} + + {#if avatarPreview} + {t('profile.avatar', + {/if} + + {t( + 'profile.photo_resize_note', + 'Images larger than 512 × 512 px are automatically resized.' + )} + + {/if} - - +
    + + {#if session.user.image} + + {/if} + +
    +
    + {/if} +
    - {#if session.user.can_edit_image !== false && session.user.auth_provider === 'local'} + +
    +

    {t('profile.account_details', 'Account Details')}

    +
    +
    +
    {t('profile.username', 'Username')}
    +
    {session.user.username || '—'}
    +
    +
    +
    {t('profile.email', 'Email')}
    +
    {session.user.email}
    +
    +
    +
    {t('profile.role', 'Role')}
    +
    + {isAdmin ? t('profile.role_admin', 'Administrator') : t('profile.role_user', 'User')} +
    +
    +
    +
    + + {t('profile.last_login', 'Last Login')} +
    +
    {timeAgo(session.user.last_login_at)}
    +
    +
    +
    + + +
    +

    {t('profile.storage', 'Storage')}

    +
    +
    +
    {formatBytes(session.user.storage_used_bytes)}
    +
    {t('profile.used', 'Used')}
    +
    +
    +
    + {session.user.storage_quota_bytes > 0 + ? formatBytes(session.user.storage_quota_bytes) + : '∞'} +
    +
    {t('profile.quota', 'Quota')}
    +
    +
    +
    + {session.user.storage_quota_bytes > 0 ? `${storagePct}%` : '—'} +
    +
    {t('profile.usage', 'Usage')}
    +
    +
    +
    +
    +
    +
    + + +
    +

    {t('profile.edit_profile', 'Edit Profile')}

    + {#if isOidc} +
    + + + {t( + 'profile.edit_oidc_managed', + 'To change your information (name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.' + )} + +
    + {:else} +
    + + + + + + +
    + {/if} +
    + + + {#if !appPwLoadFailed} +
    +

    {t('profile.app_passwords', 'App Passwords')}

    +

    + {t( + 'profile.app_pw_desc', + 'Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.' + )} +

    + +
    + + +
    + + {#if generated} +
    +
    + {t('profile.new_password_for', 'New password for')} + {generated.label}: +
    +
    + {generated.password} + +
    + + {t( + 'profile.copy_warning', + "Copy this password now. You won't be able to see it again." + )} + +
    + {/if} + + {#if userPasswords.length === 0} +

    {t('profile.no_app_passwords', 'No app passwords yet.')}

    + {:else} + + + + + + + + + + + + {#each userPasswords as p (p.id)} + + + + + + + + {/each} + +
    {t('profile.col_label', 'Label')}{t('profile.col_created', 'Created')}{t('profile.col_last_used', 'Last Used')}{t('profile.col_status', 'Status')}
    {p.label}{formatDate(p.created_at)}{p.last_used_at ? timeAgo(p.last_used_at) : t('profile.never', 'Never')} + {#if p.active !== false} + {t('profile.active', 'Active')} + {:else} + {t('profile.revoked', 'Revoked')} + {/if} + + {#if p.active !== false} + + {/if} +
    + {/if} + + {#if autoPasswords.length > 0} +
    + + {#if autoExpanded} +

    + {t( + 'profile.client_sessions_desc', + 'Auto-generated when you connect a Nextcloud-compatible client.' + )} +

    + + + + + + + + + + + {#each autoPasswords as p (p.id)} + + + + + + + {/each} + +
    {t('profile.col_client', 'Client')}{t('profile.col_created', 'Created')}{t('profile.col_last_used', 'Last Used')}
    {p.label}{formatDate(p.created_at)}{p.last_used_at + ? timeAgo(p.last_used_at) + : t('profile.never', 'Never')} + {#if p.active !== false} + + {/if} +
    + {/if} +
    + {/if} +
    + {/if} + + + {#if showPasswordCard}
    -

    {t('profile.change_password', 'Change password')}

    +

    {t('profile.change_password', 'Change Password')}

    {/if} @@ -140,7 +699,7 @@ diff --git a/frontend/src/routes/recent/+page.svelte b/frontend/src/routes/recent/+page.svelte index 2e442a56..9847f6e6 100644 --- a/frontend/src/routes/recent/+page.svelte +++ b/frontend/src/routes/recent/+page.svelte @@ -1,85 +1,381 @@ {t('nav.recent', 'Recent')} · OxiCloud -

    {t('nav.recent', 'Recent')}

    - - load(false)} + onloadmore={() => load(false, orderByForGroup())} + onopen={open} + onfavorite={toggleFavorite} + showOwner + selectable + {contextActions} + {groupBys} + bind:groupBy + bind:reversed + onreload={(orderBy, rev) => { + cursor = undefined; + load(true, orderBy, rev); + }} + onselectionchange={(ids) => (selectedIds = ids)} > {#snippet toolbar()} - {#if items.length > 0} - + {#if entries.length > 0} + {/if} {/snippet} + {#snippet batchToolbar()} + + + + {/snippet} + - {#each items as item (item.resource.id + item.accessed_at)} - - {/each} - - - + + { + selectedIds = new Set(); + load(true, orderByForGroup()); + }} +/> + diff --git a/frontend/src/routes/s/[token]/+page.svelte b/frontend/src/routes/s/[token]/+page.svelte index 60820f7e..10e32e6e 100644 --- a/frontend/src/routes/s/[token]/+page.svelte +++ b/frontend/src/routes/s/[token]/+page.svelte @@ -14,38 +14,87 @@ } from '$lib/api/endpoints/share'; import { t } from '$lib/i18n/index.svelte'; - type State = 'loading' | 'password' | 'expired' | 'file' | 'folder'; + type State = 'loading' | 'password' | 'expired' | 'invalid' | 'file' | 'folder'; + type Crumb = { id?: string; name: string }; + type ViewMode = 'grid' | 'list'; + const VIEW_KEY = 'oxicloud_share_view'; const token = $derived(page.params.token ?? ''); let view = $state('loading'); let meta = $state(null); let listing = $state(null); let folderId = $state(undefined); - let folderName = $state(''); + let crumbs = $state([]); let pwInput = $state(''); let pwError = $state(''); let busy = $state(false); + let viewMode = $state('grid'); + + // Lightbox over the media files in the current folder + let lightbox = $state(-1); + + function mediaKind(mime: string | undefined): 'image' | 'video' | null { + const m = (mime ?? '').toLowerCase(); + if (m.startsWith('image/')) return 'image'; + if (m.startsWith('video/')) return 'video'; + return null; + } + + const mediaFiles = $derived( + (listing?.files ?? []).filter((f) => mediaKind(f.mime_type) !== null) + ); + + function setViewMode(mode: ViewMode) { + viewMode = mode; + try { + localStorage.setItem(VIEW_KEY, mode); + } catch { + /* storage unavailable — keep in-memory only */ + } + } async function loadMeta() { view = 'loading'; + // Guard a missing/blank token before hitting the API. + if (!token) { + view = 'invalid'; + return; + } try { const r = await getShareMeta(token); if (r.status === 'password') { view = 'password'; } else if (r.status === 'expired') { view = 'expired'; + } else if (r.status === 'invalid') { + view = 'invalid'; } else { meta = r.data; - if (r.data.item_type === 'folder') await openFolder(undefined, r.data.item_name); - else view = 'file'; + if (r.data.item_type === 'folder') { + crumbs = [{ name: r.data.item_name }]; + // Deep-link support: honour an initial #folder= hash. + await openFolder(hashFolderId(), undefined, false); + } else view = 'file'; } } catch { view = 'expired'; } } - async function openFolder(id: string | undefined, name: string) { + /** Parse the `#folder=` fragment from the URL, if present. */ + function hashFolderId(): string | undefined { + if (typeof location === 'undefined') return undefined; + const m = location.hash.match(/[#&]folder=([A-Za-z0-9-]{1,64})/); + return m ? m[1] : undefined; + } + + /** + * Load a folder's contents. When `crumb` is given, push it onto the trail. + * `pushHistory` controls whether we sync the URL hash + push a history entry + * (true for user navigation, false when restoring from popstate / deep link). + */ + async function openFolder(id: string | undefined, crumb?: Crumb, pushHistory = false) { const r = await getShareContents(token, id); if (r.status === 'password') { view = 'password'; @@ -57,8 +106,109 @@ } listing = r.data; folderId = id; - folderName = name; + if (crumb) crumbs = [...crumbs, crumb]; + lightbox = -1; view = 'folder'; + if (pushHistory && typeof history !== 'undefined') { + const hash = id ? `#folder=${encodeURIComponent(id)}` : ''; + history.pushState({ folderId: id }, '', location.pathname + location.search + hash); + } + } + + /** Navigate to a breadcrumb at depth `index` (0 = share root). */ + async function gotoCrumb(index: number) { + const target = crumbs[index]; + crumbs = crumbs.slice(0, index + 1); + await openFolder(target.id, undefined, true); + } + + /** Browser back/forward — re-resolve the folder from the popped state/hash. */ + async function onPopState() { + if (view !== 'folder') return; + await openFolder(hashFolderId(), undefined, false); + } + + /** Append a cache-busting query param to retry a failed media load once. */ + function retrySrc(original: string): string { + const sep = original.indexOf('?') === -1 ? '?' : '&'; + return `${original}${sep}_r=${Date.now()}`; + } + + /** + * Lazy video poster: defer loading until near the viewport, then seek a few + * frames in to render a thumbnail. Retries once with cache-busting on error. + * Ported from publicShare.js wireLazyVideos(). + */ + function lazyVideo(node: HTMLVideoElement, src: string) { + let retried = false; + const start = () => { + node.addEventListener( + 'loadedmetadata', + () => { + const at = Math.min(0.1, (node.duration || 1) * 0.1); + try { + node.currentTime = at; + } catch { + /* seeking unsupported */ + } + }, + { once: true } + ); + node.addEventListener( + 'error', + () => { + if (retried) return; + retried = true; + setTimeout(() => (node.src = retrySrc(src)), 250); + }, + { once: true } + ); + node.src = src; + }; + let obs: IntersectionObserver | null = null; + if (typeof IntersectionObserver !== 'undefined') { + obs = new IntersectionObserver( + (entries) => { + for (const e of entries) { + if (e.isIntersecting) { + start(); + obs?.unobserve(node); + } + } + }, + { rootMargin: '300px' } + ); + obs.observe(node); + } else { + start(); + } + return { destroy: () => obs?.disconnect() }; + } + + /** Retry a failed image load once with cache-busting. Ported from wireImageRetry(). */ + function imageRetry(node: HTMLImageElement) { + let retried = false; + const onError = () => { + if (retried) return; + retried = true; + const original = node.src; + setTimeout(() => (node.src = retrySrc(original)), 250); + }; + node.addEventListener('error', onError); + return { destroy: () => node.removeEventListener('error', onError) }; + } + + function lbPrev() { + if (lightbox > 0) lightbox -= 1; + } + function lbNext() { + if (lightbox >= 0 && lightbox < mediaFiles.length - 1) lightbox += 1; + } + function onKeydown(e: KeyboardEvent) { + if (lightbox < 0) return; + if (e.key === 'Escape') lightbox = -1; + else if (e.key === 'ArrowLeft') lbPrev(); + else if (e.key === 'ArrowRight') lbNext(); } async function submitPassword(e: SubmitEvent) { @@ -80,14 +230,28 @@ } } - onMount(loadMeta); + onMount(() => { + try { + const saved = localStorage.getItem(VIEW_KEY); + if (saved === 'list' || saved === 'grid') viewMode = saved; + } catch { + /* ignore */ + } + void loadMeta(); + }); {meta?.item_name ?? t('share.title', 'Shared')} · OxiCloud +
    {#if view === 'loading'} + {:else if view === 'invalid'} + {:else if view === 'expired'} {:else if view === 'folder' && listing}
    +{#if lightbox >= 0 && mediaFiles[lightbox]} + {@const m = mediaFiles[lightbox]} + + +{/if} + diff --git a/frontend/src/routes/search/+page.svelte b/frontend/src/routes/search/+page.svelte new file mode 100644 index 00000000..7202c39f --- /dev/null +++ b/frontend/src/routes/search/+page.svelte @@ -0,0 +1,398 @@ + + +{t('search.title', 'Search')} · OxiCloud + +
    +

    + {#if query}{t('search.results_for', { q: query }, 'Results for “{{q}}”')}{:else}{t( + 'search.title', + 'Search' + )}{/if} + {#if results?.query_time_ms != null} + ({results.query_time_ms} ms) + {/if} +

    + {#if query} +
    + {#if filesStore.currentFolder} +
    + + +
    + {/if} + + + + + {#if hasFilters} + + {/if} +
    + {/if} +
    + +{#if loading} +
    + +

    + {t('search.searching_for', { q: query }, 'Searching for “{{q}}”…')} +

    +
    +{:else if error} +

    {error}

    +{:else if !query} +
    +

    {t('search.prompt', 'Type a query in the search bar above.')}

    +
    +{:else if isEmpty} +
    + +

    {t('search.no_results', 'No results found for this search')}

    +
    +{:else if results} +
    +
    +
    +
    {t('files.col_name', 'Name')}
    +
    {t('files.col_path', 'Path')}
    +
    {t('files.col_size', 'Size')}
    +
    {t('files.col_modified', 'Modified')}
    +
    + + {#each results.folders as folder (folder.id)} +
    openFolder(folder)} + onkeydown={(e) => e.key === 'Enter' && openFolder(folder)} + > +
    + + {folder.name} +
    +
    {folder.path}
    +
    —
    +
    {formatDate(folder.modified_at)}
    +
    + {/each} + + {#each results.files as file (file.id)} +
    openFile(file)} + onkeydown={(e) => e.key === 'Enter' && openFile(file)} + > +
    + + {file.name} +
    +
    {file.path}
    +
    {file.size != null ? formatBytes(file.size) : ''}
    +
    {formatDate(file.modified_at)}
    +
    + {/each} +
    +
    +{/if} + + diff --git a/frontend/src/routes/shared-with-me/+page.svelte b/frontend/src/routes/shared-with-me/+page.svelte index c8900797..136af52a 100644 --- a/frontend/src/routes/shared-with-me/+page.svelte +++ b/frontend/src/routes/shared-with-me/+page.svelte @@ -1,22 +1,41 @@ {t('nav.shared_with_me', 'Shared with me')} · OxiCloud -

    {t('nav.shared_with_me', 'Shared with me')}

    - - load(false)} -> - {#each items as item (item.resource.id)} - - {/each} - + onopen={open} +/> - + diff --git a/frontend/src/routes/shared/+page.svelte b/frontend/src/routes/shared/+page.svelte index d0507a80..f92ddc4f 100644 --- a/frontend/src/routes/shared/+page.svelte +++ b/frontend/src/routes/shared/+page.svelte @@ -1,22 +1,155 @@ {t('nav.shared', 'Shared')} · OxiCloud + menuFor && closeMenu()} /> -

    {t('nav.shared', 'Shared')}

    +
    +

    {t('nav.shared', 'Shared')}

    + setGroupBy(key as GroupBy)} + ondirection={toggleDirection} + showViewToggle={false} + /> +
    - load(false)} -> - {#each items as item (item.resource.id)} - - {/each} - +{#if error} +
    + +

    {error}

    +
    +{:else if isEmpty} +
    + +

    {t('myshares.emptyStateTitle', "You haven't shared anything yet")}

    +

    + {t('myshares.emptyStateDesc', 'Items you share with others will appear here')} +

    +
    +{:else} +
    + {#each lanes as lane (lane.key)} +
    +
    + {#if lane.header.kind === 'resource'} + {@const laneItem = lane.header.item} + + + {:else} + + + {laneTitle(lane.header)} + + {/if} +
    + +
      + {#each lane.rows as { grant, item } (grant.grant_id)} + {@const tier = expiryTier(grant.expires_at)} +
    • + + + {#if (grant.subject_type === 'user' || grant.subject_type === 'group') && groupBy === 'sharedWith'} + + {:else if grant.subject_type === 'user'} + + {resolveLabel('user', grant.subject_id)} + {:else if grant.subject_type === 'group'} + + {resolveLabel('group', grant.subject_id)} + {:else} + + {#if groupBy === 'sharedWith'} + → + + {/if} + {/if} + + + + {#if grant.subject_type !== 'token'} + + + {roleMeta(grant.role).l} + + {/if} + + + + + {expiryLabel(grant.expires_at)} + + + +
      + + {#if menuFor === grant.grant_id} + + {/if} +
      +
    • + {/each} +
    +
    + {/each} + + {#if cursor} + + {/if} +
    +{/if} + + diff --git a/frontend/src/routes/trash/+page.svelte b/frontend/src/routes/trash/+page.svelte index cb59a99a..a1d4f0c0 100644 --- a/frontend/src/routes/trash/+page.svelte +++ b/frontend/src/routes/trash/+page.svelte @@ -1,62 +1,146 @@ + + diff --git a/frontend/src/lib/components/EmptyState.svelte b/frontend/src/lib/components/EmptyState.svelte new file mode 100644 index 00000000..4403103f --- /dev/null +++ b/frontend/src/lib/components/EmptyState.svelte @@ -0,0 +1,54 @@ + + +
    + {#if icon}{/if} + {#if title}

    {title}

    {/if} + {#if hint}

    {hint}

    {/if} + {@render children?.()} +
    + + diff --git a/frontend/src/lib/components/MoveDialog.svelte b/frontend/src/lib/components/MoveDialog.svelte index 41579cd0..42436d75 100644 --- a/frontend/src/lib/components/MoveDialog.svelte +++ b/frontend/src/lib/components/MoveDialog.svelte @@ -1,4 +1,5 @@ + +
    +
    + {#each placeholders as i (i)} + {#if filesStore.viewMode === 'grid'} +
    +
    +
    +
    +
    + {:else} +
    +
    +
    +
    +
    + {/if} + {/each} +
    +
    diff --git a/frontend/src/lib/components/WopiEditor.svelte b/frontend/src/lib/components/WopiEditor.svelte index f60338e4..8c2cfc7c 100644 --- a/frontend/src/lib/components/WopiEditor.svelte +++ b/frontend/src/lib/components/WopiEditor.svelte @@ -1,8 +1,8 @@ -
    +
    {#each ui.toasts as toast (toast.id)}
    {toast.message} -
    diff --git a/frontend/static/locales/ar.json b/frontend/static/locales/ar.json index a1884660..ed1afb36 100644 --- a/frontend/static/locales/ar.json +++ b/frontend/static/locales/ar.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "لم يعد رابط تسجيل الدخول صالحًا", - "expired_body": "ربما انتهت صلاحية الرابط أو تم استخدامه بالفعل. يمكننا إرسال رابط جديد لك — سيصل إلى صندوق الوارد خلال ثوانٍ.", - "resend_to": "أرسل رابطًا جديدًا إلى {{email}}", - "generic_unavailable": "لم يعد رابط تسجيل الدخول صالحًا. ربما تم استخدامه بالفعل أو انتهت صلاحيته. اطلب رابطًا جديدًا من صفحة تسجيل الدخول.", - "service_unavailable": "تسجيل الدخول عبر الرابط السحري غير مفعّل على هذا الخادم.", - "internal_error": "حدث خطأ أثناء تسجيل الدخول. يُرجى المحاولة مرة أخرى.", - "resend_failure": "حدث خطأ أثناء إرسال الرابط. يُرجى المحاولة مرة أخرى.", - "cross_browser_title": "هل تريد متابعة تسجيل الدخول على هذا الجهاز؟", - "cross_browser_body": "لقد فتحت رابط تسجيل الدخول في متصفح أو جهاز مختلف عن الجهاز الذي طلبته منه.", - "cross_browser_warning": "إذا كنت قد طلبت هذا الرابط، فمن الآمن المتابعة. إذا لم تطلبه، أغلق هذه الصفحة — النقر على «متابعة» سيُسجّل دخول شخص آخر إلى حسابك.", - "cross_browser_continue": "متابعة وتسجيل الدخول", - "resend_confirmation_title": "تحقق من صندوق الوارد", - "resend_confirmation_body": "إذا كان رابط تسجيل الدخول ينتمي إلى حساب نشط، فقد تم للتو إرسال رابط جديد. يُرجى التحقق من صندوق الوارد.", - "return_link": "العودة إلى OxiCloud" - }, - "email": { - "invitation": { - "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", - "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتحه بالنقر على الرابط أدناه:\n{{link}}\n\nيعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_hours}} ساعة.\nإذا لم تكن تتوقع هذه الدعوة، يمكنك تجاهل هذه الرسالة.\n\n— OxiCloud" - }, - "login": { - "subject": "تسجيل الدخول إلى OxiCloud", - "body": "مرحبًا،\n\nاستخدم الرابط أدناه لتسجيل الدخول إلى OxiCloud. يعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_minutes}} دقيقة. افتحه على الجهاز نفسه الذي طلبت منه الرابط.\n\n{{link}}\n\nإذا لم تطلب رابط تسجيل الدخول هذا، يمكنك تجاهل هذه الرسالة — لا حاجة لأي إجراء إضافي.\n\n— OxiCloud" - }, - "kind_file": "ملف", - "kind_folder": "مجلد", - "english_fallback_divider": "--- النسخة الإنجليزية أدناه ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "لم يعد رابط تسجيل الدخول صالحًا", + "expired_body": "ربما انتهت صلاحية الرابط أو تم استخدامه بالفعل. يمكننا إرسال رابط جديد لك — سيصل إلى صندوق الوارد خلال ثوانٍ.", + "resend_to": "أرسل رابطًا جديدًا إلى {{email}}", + "generic_unavailable": "لم يعد رابط تسجيل الدخول صالحًا. ربما تم استخدامه بالفعل أو انتهت صلاحيته. اطلب رابطًا جديدًا من صفحة تسجيل الدخول.", + "service_unavailable": "تسجيل الدخول عبر الرابط السحري غير مفعّل على هذا الخادم.", + "internal_error": "حدث خطأ أثناء تسجيل الدخول. يُرجى المحاولة مرة أخرى.", + "resend_failure": "حدث خطأ أثناء إرسال الرابط. يُرجى المحاولة مرة أخرى.", + "cross_browser_title": "هل تريد متابعة تسجيل الدخول على هذا الجهاز؟", + "cross_browser_body": "لقد فتحت رابط تسجيل الدخول في متصفح أو جهاز مختلف عن الجهاز الذي طلبته منه.", + "cross_browser_warning": "إذا كنت قد طلبت هذا الرابط، فمن الآمن المتابعة. إذا لم تطلبه، أغلق هذه الصفحة — النقر على «متابعة» سيُسجّل دخول شخص آخر إلى حسابك.", + "cross_browser_continue": "متابعة وتسجيل الدخول", + "resend_confirmation_title": "تحقق من صندوق الوارد", + "resend_confirmation_body": "إذا كان رابط تسجيل الدخول ينتمي إلى حساب نشط، فقد تم للتو إرسال رابط جديد. يُرجى التحقق من صندوق الوارد.", + "return_link": "العودة إلى OxiCloud" + }, + "email": { + "invitation": { + "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", + "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتحه بالنقر على الرابط أدناه:\n{{link}}\n\nيعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_hours}} ساعة.\nإذا لم تكن تتوقع هذه الدعوة، يمكنك تجاهل هذه الرسالة.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", - "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتح OxiCloud لعرض مشاركتك الجديدة:\n{{login_link}}\n\nقد تكون لديك مشاركات جديدة أخرى من {{inviter}} — سجّل الدخول لرؤية جميع العناصر المشاركة معك.\n\n— OxiCloud\n\nأنت تتلقى هذه الرسالة لأن لديك حسابًا في OxiCloud وتفضيل إشعارات المشاركة مُفعّل. يمكنك تعطيله من ملفك الشخصي (راسلني عندما يشاركني شخص ما)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "نظام تخزين سحابي بسيط" - }, - "nav": { - "files": "الملفات", - "shared": "مشاركاتي", - "recent": "الأخيرة", - "favorites": "المفضلة", - "photos": "الصور", - "music": "الموسيقى", - "trash": "سلة المهملات", - "sharedwithme": "مشتركة معي" - }, - "photos": { - "empty_state": "لا توجد صور بعد", - "empty_hint": "ارفع صوراً أو مقاطع فيديو لعرضها هنا", - "items_selected": "محدد", - "view_daily": "يوم", - "view_monthly": "شهر", - "view_yearly": "سنة" - }, - "music": { - "create_playlist": "إنشاء قائمة تشغيل", - "playlists": "قوائم التشغيل", - "no_playlists": "لا توجد قوائم تشغيل بعد", - "select_playlist": "اختر قائمة تشغيل", - "select_hint": "اختر قائمة تشغيل من الشريط الجانبي أو أنشئ واحدة جديدة", - "add_tracks": "إضافة مقاطع", - "no_tracks": "لا توجد مقاطع في هذه القائمة", - "unknown_artist": "فنان غير معروف", - "unknown_title": "غير معروف", - "confirm_delete": "هل تريد حذف هذه القائمة؟", - "playlist_name": "اسم القائمة", - "create": "إنشاء", - "delete": "حذف", - "share": "مشاركة", - "edit": "تعديل", - "play_all": "تشغيل الكل", - "shuffle": "عشوائي", - "repeat": "تكرار", - "repeat_one": "تكرار واحد", - "queue": "قائمة الانتظار", - "queue_empty": "قائمة الانتظار فارغة", - "not_playing": "لا يتم التشغيل", - "play": "تشغيل", - "pause": "إيقاف مؤقت", - "previous": "السابق", - "next": "التالي", - "volume": "مستوى الصوت", - "mute": "كتم", - "unmute": "إلغاء الكتم", - "title": "العنوان", - "artist": "الفنان", - "album": "الألبوم", - "tracks": "مقاطع", - "add": "إضافة", - "added": "تمت الإضافة!", - "added_to_playlist": "تمت إضافته إلى القائمة", - "add_to_playlist": "إضافة إلى القائمة", - "load_error": "خطأ في تحميل القوائم", - "add_error": "تعذر إضافة المقاطع", - "no_playlists_yet": "لا توجد قوائم بعد. أنشئ واحدة أولاً!", - "selected_files": "محدد:", - "error": "خطأ", - "search_audio": "البحث عن ملفات صوتية…", - "no_audio_files": "لم يتم العثور على ملفات صوتية", - "selected": "محدد", - "loading": "جارٍ التحميل…", - "search_error": "تعذر تحميل الملفات الصوتية", - "adding": "جارٍ الإضافة…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "البحث في الملفات...", - "new_folder": "مجلد جديد", - "upload": "رفع", - "upload_files": "رفع ملفات", - "upload_folder": "رفع مجلد", - "upload.uploading": "جارٍ الرفع...", - "upload.complete": "{count} / {total} تم رفعها", - "upload.files": "ملفات", - "rename": "إعادة التسمية", - "move": "نقل إلى...", - "move_to": "نقل إلى", - "delete": "حذف", - "download": "تحميل", - "view": "عرض", - "cancel": "إلغاء", - "confirm": "تأكيد", - "share": "مشاركة", - "favorite": "إضافة للمفضلة", - "unfavorite": "إزالة من المفضلة", - "copy": "نسخ", - "notify": "إشعار", - "send": "إرسال", - "clear_recent": "مسح الأخيرة", - "logout": "تسجيل الخروج", - "create": "إنشاء", - "search_btn": "بحث", - "close": "إغلاق", - "delete_permanently": "حذف نهائياً", - "empty_trash": "تفريغ سلة المهملات", - "open_parent_folder": "الانتقال إلى المجلد الأصلي", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "المظهر", - "about": "حول OxiCloud", - "about_description": "منصة تخزين سحابي مبنية بـ Rust و Clean Architecture. سريعة وآمنة وخاصة.", - "admin_panel": "لوحة الإدارة", - "profile": "ملفي الشخصي", - "role_user": "مستخدم", - "theme": { - "light": "فاتح", - "dark": "داكن", - "auto": "مثل النظام" + "login": { + "subject": "تسجيل الدخول إلى OxiCloud", + "body": "مرحبًا،\n\nاستخدم الرابط أدناه لتسجيل الدخول إلى OxiCloud. يعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_minutes}} دقيقة. افتحه على الجهاز نفسه الذي طلبت منه الرابط.\n\n{{link}}\n\nإذا لم تطلب رابط تسجيل الدخول هذا، يمكنك تجاهل هذه الرسالة — لا حاجة لأي إجراء إضافي.\n\n— OxiCloud" }, - "manage_groups": "إدارة المجموعات" + "kind_file": "ملف", + "kind_folder": "مجلد", + "english_fallback_divider": "--- النسخة الإنجليزية أدناه ---" + } }, - "share": { - "dialogTitle": "رابط المشاركة", - "linkLabel": "رابط المشاركة:", - "copyLink": "نسخ", - "permissions": "الصلاحيات:", - "permissionRead": "قراءة", - "permissionWrite": "كتابة", - "permissionReshare": "إعادة مشاركة", - "password": "حماية بكلمة مرور:", - "generatePassword": "توليد", - "expiration": "تاريخ انتهاء الصلاحية:", - "update": "تحديث المشاركة", - "remove": "إزالة المشاركة", - "notifyTitle": "إرسال إشعار", - "notifyEmailLabel": "عنوان البريد الإلكتروني:", - "notifyMessageLabel": "رسالة (اختياري):", - "notifySend": "إرسال الإشعار", - "shareWithOthers": "مشاركة مع آخرين", - "sharePublicly": "مشاركة عامة", - "shareSettings": "إعدادات المشاركة", - "shareCopied": "تم نسخ الرابط إلى الحافظة", - "shareCreated": "تم إنشاء رابط المشاركة بنجاح", - "shareUpdated": "تم تحديث إعدادات المشاركة بنجاح", - "shareRemoved": "تمت إزالة المشاركة بنجاح", - "inviteByEmail": "دعوة عبر البريد الإلكتروني — ستُرسل الدعوة", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "رابط المشاركة", - "share_linkLabel": "رابط المشاركة:", - "share_copyLink": "نسخ", - "share_permissions": "الصلاحيات:", - "share_permissionRead": "قراءة", - "share_permissionWrite": "كتابة", - "share_permissionReshare": "إعادة مشاركة", - "share_password": "حماية بكلمة مرور:", - "share_generatePassword": "توليد", - "share_expiration": "تاريخ انتهاء الصلاحية:", - "share_update": "تحديث المشاركة", - "share_remove": "إزالة المشاركة", - "share_notifyTitle": "إرسال إشعار", - "share_notifyEmailLabel": "عنوان البريد الإلكتروني:", - "share_notifyMessageLabel": "رسالة (اختياري):", - "share_notifySend": "إرسال الإشعار", - "shared": { - "backToFiles": "العودة إلى الملفات", - "pageTitle": "الموارد المشتركة", - "pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", - "filterType": "النوع:", - "filterAll": "الكل", - "filterFiles": "ملفات", - "filterFolders": "مجلدات", - "sortBy": "ترتيب حسب:", - "sortByName": "الاسم", - "sortByDate": "تاريخ المشاركة", - "sortByExpiration": "انتهاء الصلاحية", - "search": "بحث", - "colName": "الاسم", - "colType": "النوع", - "colDateShared": "تاريخ المشاركة", - "colExpiration": "انتهاء الصلاحية", - "colPermissions": "الصلاحيات", - "colPassword": "كلمة المرور", - "colActions": "الإجراءات", - "emptyStateTitle": "لا توجد موارد مشتركة بعد", - "emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", - "goToFiles": "الذهاب إلى الملفات", - "typeFile": "ملف", - "typeFolder": "مجلد", - "noExpiration": "بدون انتهاء صلاحية", - "hasPassword": "نعم", - "noPassword": "لا", - "editShare": "تعديل المشاركة", - "notifyShare": "إشعار شخص ما", - "copyLink": "نسخ الرابط", - "removeShare": "إزالة المشاركة", - "linkCopied": "تم نسخ الرابط إلى الحافظة!", - "linkCopyFailed": "فشل نسخ الرابط", - "itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", - "itemRemoved": "تمت إزالة المشاركة بنجاح", - "invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", - "notificationSent": "تم إرسال الإشعار بنجاح", - "notificationFailed": "فشل إرسال الإشعار", - "shared_backToFiles": "العودة إلى الملفات", - "shared_pageTitle": "الموارد المشتركة", - "shared_pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", - "shared_filterType": "النوع:", - "shared_filterAll": "الكل", - "shared_filterFiles": "ملفات", - "shared_filterFolders": "مجلدات", - "shared_sortBy": "ترتيب حسب:", - "shared_sortByName": "الاسم", - "shared_sortByDate": "تاريخ المشاركة", - "shared_sortByExpiration": "انتهاء الصلاحية", - "shared_search": "بحث", - "shared_colName": "الاسم", - "shared_colType": "النوع", - "shared_colDateShared": "تاريخ المشاركة", - "shared_colExpiration": "انتهاء الصلاحية", - "shared_colPermissions": "الصلاحيات", - "shared_colPassword": "كلمة المرور", - "shared_colActions": "الإجراءات", - "shared_emptyStateTitle": "لا توجد موارد مشتركة بعد", - "shared_emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", - "shared_goToFiles": "الذهاب إلى الملفات", - "shared_typeFile": "ملف", - "shared_typeFolder": "مجلد", - "shared_noExpiration": "بدون انتهاء صلاحية", - "shared_hasPassword": "نعم", - "shared_noPassword": "لا", - "shared_editShare": "تعديل المشاركة", - "shared_notifyShare": "إشعار شخص ما", - "shared_copyLink": "نسخ الرابط", - "shared_removeShare": "إزالة المشاركة", - "shared_linkCopied": "تم نسخ الرابط إلى الحافظة!", - "shared_linkCopyFailed": "فشل نسخ الرابط", - "shared_itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", - "shared_itemRemoved": "تمت إزالة المشاركة بنجاح", - "shared_invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", - "shared_notificationSent": "تم إرسال الإشعار بنجاح", - "shared_notificationFailed": "فشل إرسال الإشعار" - }, - "files": { - "name": "الاسم", - "type": "النوع", - "size": "الحجم", - "modified": "تاريخ التعديل", - "no_files": "لا توجد ملفات في هذا المجلد", - "empty_hint": "ارفع ملفات أو أنشئ مجلدات للبدء", - "loading": "جارٍ تحميل الملفات…", - "view_grid": "عرض شبكي", - "view_list": "عرض قائمة", - "file_types": { - "document": "مستند", - "image": "صورة", - "video": "فيديو", - "audio": "صوت", - "pdf": "PDF", - "text": "نص", - "folder": "مجلد", - "spreadsheet": "جدول بيانات", - "presentation": "عرض تقديمي", - "archive": "أرشيف", - "installer": "مثبّت", - "code": "كود" - }, - "owner": "المالك" - }, - "dialogs": { - "rename_folder": "إعادة تسمية المجلد", - "rename_file": "إعادة تسمية الملف", - "new_name": "الاسم الجديد", - "new_folder_title": "مجلد جديد", - "folder_name": "اسم المجلد", - "folder_placeholder": "مجلدي", - "rename_title": "إعادة التسمية", - "move_file": "نقل الملف", - "move_folder": "نقل المجلد", - "select_destination": "اختر المجلد الوجهة:", - "select_this_folder": "اختيار هذا المجلد", - "go_to_parent": ".. (المجلد الأعلى)", - "no_subfolders": "لا توجد مجلدات فرعية", - "root": "الجذر", - "delete_confirmation": "هل أنت متأكد أنك تريد حذف", - "and_contents": "وجميع محتوياته", - "no_undo": "لا يمكن التراجع عن هذا الإجراء", - "confirm_title": "تأكيد الإجراء", - "confirm_delete": "نقل إلى سلة المهملات", - "confirm_delete_file": "هل أنت متأكد أنك تريد نقل الملف \"{{name}}\" إلى سلة المهملات؟", - "confirm_delete_folder": "هل أنت متأكد أنك تريد نقل المجلد \"{{name}}\" وجميع محتوياته إلى سلة المهملات؟", - "confirm_permanent_delete": "حذف نهائي", - "confirm_permanent_delete_msg": "هل أنت متأكد أنك تريد حذف هذا العنصر نهائياً؟ لا يمكن التراجع عن هذا الإجراء.", - "confirm_empty_trash": "تفريغ سلة المهملات", - "confirm_delete_share": "حذف رابط المشاركة", - "confirm_delete_share_msg": "هل أنت متأكد أنك تريد حذف رابط المشاركة هذا؟", - "share_file": "مشاركة الملف", - "share_folder": "مشاركة المجلد", - "existing_shares": "المشاركات الحالية", - "share_options": "خيارات المشاركة", - "password": "كلمة المرور", - "expiration": "انتهاء الصلاحية", - "permissions": "الصلاحيات", - "generated_link": "الرابط المُنشأ", - "notify": "إرسال إشعار", - "recipient": "المستلم", - "message": "الرسالة", - "move_to_home": "نقل إلى المجلد الرئيسي" - }, - "dropzone": { - "drag_files": "اسحب الملفات هنا أو انقر للاختيار", - "drop_files": "أسقط الملفات للرفع" - }, - "permissions": { - "read": "قراءة", - "write": "كتابة", - "reshare": "إعادة مشاركة" - }, - "errors": { - "file_not_found": "الملف غير موجود", - "folder_not_found": "المجلد غير موجود", - "delete_error": "خطأ في الحذف", - "upload_error": "خطأ في رفع الملف", - "rename_error": "خطأ في إعادة التسمية", - "move_error": "خطأ في النقل", - "empty_name": "لا يمكن أن يكون الاسم فارغاً", - "name_exists": "ملف أو مجلد بهذا الاسم موجود بالفعل", - "generic_error": "حدث خطأ", - "group_name_invalid": "يجب أن يتطابق اسم المجموعة مع صيغة بادئة البريد الإلكتروني (حروف، أرقام، نقطة، شرطة، شرطة سفلية؛ 1–64 حرفًا).", - "group_cycle": "سينشئ هذا العضو مرجعًا دائريًا بين المجموعات.", - "group_depth_exceeded": "تتجاوز عمق التعشيش الحد الأقصى المسموح (8).", - "group_virtual_immutable": "مجموعة «Internal» تدار من قبل النظام ولا يمكن تعديلها.", - "group_not_found": "المجموعة غير موجودة.", - "group_name_taken": "توجد بالفعل مجموعة بهذا الاسم." - }, - "breadcrumb": { - "home": "الرئيسية" - }, - "trash": { - "empty_trash": "تفريغ سلة المهملات", - "empty_state": "سلة المهملات فارغة", - "original_location": "الموقع الأصلي", - "deleted_date": "تاريخ الحذف", - "remaining": "المتبقي", - "actions": "الإجراءات", - "restore": "استعادة", - "delete_permanently": "حذف نهائياً", - "empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً.", - "groupby": { - "remaining_days": "الأيام المتبقية", - "trashed_time": "وقت الحذف" - } - }, - "daysRemaining": { - "expired": "منتهية الصلاحية", - "today": "اليوم", - "tomorrow": "غدًا", - "inDays": "{{count}} يوم" - }, - "expiryChip": { - "never": "لا تنتهي الصلاحية", - "expired": "منتهية الصلاحية", - "today": "تنتهي الصلاحية اليوم", - "tomorrow": "تنتهي الصلاحية غدًا", - "inDays": "تنتهي الصلاحية خلال {{count}} يوم", - "onDate": "تنتهي الصلاحية في {{date}}" - }, - "auth": { - "login_title": "تسجيل الدخول", - "username": "اسم المستخدم", - "username_placeholder": "أدخل اسم المستخدم", - "login_identifier": "اسم المستخدم أو البريد الإلكتروني", - "login_identifier_placeholder": "أدخل اسم المستخدم أو البريد الإلكتروني", - "password": "كلمة المرور", - "password_placeholder": "أدخل كلمة المرور", - "login_button": "تسجيل الدخول", - "no_account": "ليس لديك حساب؟", - "register": "إنشاء حساب", - "admin_setup": "أول مرة؟", - "setup": "إعداد المسؤول", - "register_title": "إنشاء حساب", - "email": "البريد الإلكتروني", - "email_placeholder": "أدخل بريدك الإلكتروني", - "confirm_password": "تأكيد كلمة المرور", - "confirm_password_placeholder": "أكد كلمة المرور", - "register_button": "إنشاء حساب", - "have_account": "لديك حساب بالفعل؟", - "login": "تسجيل الدخول", - "setup_title": "الإعداد الأولي", - "setup_step1": "المسؤول", - "setup_step2": "النظام", - "setup_step3": "مكتمل", - "admin_username": "اسم مستخدم المسؤول", - "admin_email": "بريد المسؤول الإلكتروني", - "admin_password": "كلمة مرور المسؤول", - "create_admin": "إنشاء حساب المسؤول", - "back_to_login": "تم الإعداد مسبقاً؟", - "admin_success": "تم إنشاء حساب المسؤول بنجاح! يمكنك الآن تسجيل الدخول.", - "account_success": "تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول.", - "passwords_mismatch": "كلمات المرور غير متطابقة", - "admin_create_error": "خطأ في إنشاء حساب المسؤول", - "or": "أو", - "sso_login": "تسجيل الدخول عبر SSO", - "sso_login_provider": "تسجيل الدخول عبر {{provider}}", - "magicLinkHint": "ليس لديك كلمة مرور؟ أدخل بريدك الإلكتروني وسنرسل لك رابط تسجيل دخول لمرة واحدة.", - "magicLinkEmailLabel": "عنوان البريد الإلكتروني", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "إرسال رابط تسجيل الدخول", - "magicLinkSent": "إذا كان هناك حساب لهذا البريد الإلكتروني، فسيتم إرسال رابط تسجيل الدخول. تحقق من صندوق الوارد.", - "magicLinkUnavailable": "تسجيل الدخول عبر البريد الإلكتروني غير متاح على هذا الخادم.", - "magicLinkNetworkError": "تعذر الوصول إلى الخادم: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "التخزين", - "calculating": "جارٍ الحساب...", - "used": "{{percentage}}% مستخدم ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "لا يمكن معاينة هذا النوع من الملفات.", - "download_file": "تحميل الملف", - "zoom_in": "تكبير", - "zoom_out": "تصغير", - "zoom_reset": "إعادة تعيين التكبير" - }, - "language_selector": { - "title": "!مرحباً", - "subtitle": "اختر لغتك للمتابعة", - "continue": "متابعة", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "لا توجد مفضلات بعد", - "empty_hint": "ضع نجمة على الملفات أو المجلدات لإضافتها إلى المفضلة", - "add": "إضافة للمفضلة", - "remove": "إزالة من المفضلة", - "added_title": "أُضيف للمفضلة", - "added_msg": "أُضيف للمفضلة", - "removed_title": "أُزيل من المفضلة", - "removed_msg": "أُزيل من المفضلة" - }, - "recent": { - "title": "الأخيرة", - "clear": "مسح الأخيرة", - "accessed": "تم الوصول", - "empty_state": "لا توجد ملفات حديثة", - "empty_hint": "الملفات التي تفتحها ستظهر هنا", - "loadMore": "تحميل المزيد" - }, - "notifications": { - "file_renamed": "تمت إعادة تسمية الملف", - "file_renamed_to": "تمت إعادة تسمية الملف إلى \"{{name}}\"", - "folder_renamed": "تمت إعادة تسمية المجلد", - "folder_renamed_to": "تمت إعادة تسمية المجلد إلى \"{{name}}\"", - "file_uploaded": "تم رفع الملف", - "file_deleted": "تم نقل الملف إلى سلة المهملات", - "folder_deleted": "تم نقل المجلد إلى سلة المهملات", - "item_deleted_permanently": "تم حذف العنصر نهائياً", - "trash_emptied": "تم تفريغ سلة المهملات بنجاح", - "title": "الإشعارات", - "empty": "لا توجد إشعارات", - "link_created": "تم إنشاء الرابط", - "share_success": "تم إنشاء رابط المشاركة بنجاح", - "upload_files_section_title": "التحميل غير متاح هنا", - "upload_files_section_body": "انتقل إلى قسم الملفات لتحميل الملفات" - }, - "batch": { - "one_selected": "عنصر واحد محدد", - "n_selected": "{{count}} عناصر محددة", - "confirm_delete": "هل أنت متأكد أنك تريد نقل {{count}} عنصر إلى سلة المهملات؟", - "move_title": "نقل {{count}} عنصر", - "add_favorites": "إضافة للمفضلة", - "move_copy": "نقل أو نسخ" - }, - "admin": { - "page_title": "لوحة الإدارة", - "back_to_app": "العودة إلى OxiCloud", - "loading": "جارٍ التحميل…", - "access_denied": "الوصول مرفوض", - "access_denied_desc": "صلاحيات المسؤول مطلوبة.", - "sign_in": "تسجيل الدخول", - "tab_dashboard": "لوحة المعلومات", - "tab_users": "المستخدمون", - "tab_oidc": "SSO / OIDC", - "total_users": "إجمالي المستخدمين", - "active_users": "المستخدمون النشطون", - "admins": "المسؤولون", - "version": "الإصدار", - "storage_overview": "نظرة عامة على التخزين", - "used": "مستخدم", - "total_quota": "الحصة الإجمالية", - "usage_pct": "نسبة الاستخدام", - "users_over_80": "مستخدمون >80%", - "users_over_quota": "مستخدمون تجاوزوا الحصة", - "system": "النظام", - "auth_label": "المصادقة", - "oidc_label": "OIDC", - "quotas_label": "الحصص", - "enabled": "مفعّل", - "disabled": "معطّل", - "active": "نشط", - "off": "متوقف", - "allow_registration": "السماح بالتسجيل العام", - "registration_warning": "التسجيل العام معطّل. فقط المسؤولون يمكنهم إنشاء مستخدمين.", - "user_management": "إدارة المستخدمين", - "create_user": "إنشاء مستخدم", - "col_user": "المستخدم", - "col_role": "الدور", - "col_auth": "المصادقة", - "col_status": "الحالة", - "col_storage": "التخزين", - "col_last_login": "آخر دخول", - "col_actions": "الإجراءات", - "loading_users": "جارٍ تحميل المستخدمين…", - "failed_load_users": "فشل التحميل", - "no_users_found": "لم يتم العثور على مستخدمين", - "showing_users": "عرض {{from}}-{{to}} من {{total}}", - "prev": "السابق", - "next": "التالي", - "inactive": "غير نشط", - "you_badge": "(أنت)", - "local": "محلي", - "never": "أبداً", - "just_now": "الآن", - "minutes_ago": "منذ {{n}} دقيقة", - "hours_ago": "منذ {{n}} ساعة", - "days_ago": "منذ {{n}} يوم", - "edit_quota_title": "تعديل الحصة", - "reset_password_title": "إعادة تعيين كلمة المرور", - "toggle_role_title": "تبديل الدور", - "deactivate_title": "تعطيل", - "activate_title": "تفعيل", - "delete_title": "حذف", - "sso_title": "تسجيل الدخول الموحد (OIDC / SSO)", - "enable_sso": "تفعيل مصادقة SSO", - "provider_name": "اسم الموفر", - "issuer_url": "عنوان المُصدر", - "issuer_url_hint": "عنوان مُصدر OpenID Connect", - "auto_discover": "اكتشاف تلقائي", - "discovering": "جارٍ الاكتشاف…", - "client_id": "معرّف العميل", - "client_secret": "سر العميل", - "client_secret_placeholder": "اتركه فارغاً للاحتفاظ بالقيمة", - "secret_configured": "سر العميل مُهيأ بالفعل", - "callback_url": "عنوان الاستدعاء", - "callback_url_hint": "(سجّل في IdP)", - "advanced_settings": "إعدادات متقدمة", - "scopes": "النطاقات", - "auto_provision": "إنشاء تلقائي عند أول دخول", - "admin_groups": "مجموعات المسؤولين", - "admin_groups_hint": "أسماء مجموعات OIDC مفصولة بفواصل", - "disable_password": "تعطيل الدخول بكلمة المرور (OIDC فقط)", - "password_warning": "سيمنع جميع عمليات الدخول بالمرور!", - "test_btn": "اختبار", - "save_btn": "حفظ", - "saving": "جارٍ الحفظ…", - "settings_saved": "تم الحفظ — OIDC الآن {{status}}", - "quota_modal_title": "تحديث حصة التخزين", - "quota_user_label": "المستخدم:", - "new_quota": "حصة جديدة", - "quota_unlimited_hint": "0 لغير محدود", - "cancel": "إلغاء", - "create_user_title": "إنشاء مستخدم جديد", - "username_label": "اسم المستخدم", - "username_placeholder": "اسم_المستخدم", - "username_hint": "3–32 حرفاً", - "password_label": "كلمة المرور", - "password_placeholder": "8 أحرف على الأقل", - "email_label": "البريد", - "email_optional": "(اختياري)", - "email_placeholder": "user@example.com (يُنشأ تلقائياً)", - "role_label": "الدور", - "role_user": "مستخدم", - "role_admin": "مسؤول", - "quota_label": "الحصة", - "creating": "جارٍ الإنشاء…", - "reset_pw_title": "إعادة تعيين كلمة المرور", - "new_password_label": "كلمة مرور جديدة", - "resetting": "جارٍ إعادة التعيين…", - "reset_btn": "إعادة تعيين", - "confirm_role_change": "تغيير الدور إلى {{role}}؟", - "confirm_deactivate": "هل أنت متأكد من التعطيل؟", - "confirm_activate": "هل أنت متأكد من التفعيل؟", - "confirm_delete_user": "حذف المستخدم \"{{name}}\"؟ لا يمكن التراجع!", - "confirm_action": "تأكيد الإجراء", - "confirm_yes": "تأكيد", - "confirm_no": "إلغاء", - "error_username_short": "الاسم 3 أحرف على الأقل", - "error_password_short": "كلمة المرور 8 أحرف على الأقل", - "error_generic": "فشل", - "error_network": "خطأ في الشبكة: {{message}}", - "error_create_user": "فشل إنشاء المستخدم", - "tab_storage": "التخزين", - "storage_title": "إعداد التخزين", - "storage_current_backend": "الواجهة الخلفية الحالية", - "storage_total_blobs": "إجمالي الكتل", - "storage_total_size": "الحجم الإجمالي", - "storage_dedup_ratio": "نسبة إزالة التكرار", - "storage_backend": "الواجهة الخلفية", - "storage_local": "محلي", - "storage_s3": "متوافق مع S3", - "storage_provider_preset": "إعداد مسبق للمزود", - "storage_preset_custom": "مخصص", - "storage_endpoint_url": "رابط نقطة النهاية", - "storage_endpoint_hint": "اتركه فارغاً لـ AWS S3", - "storage_bucket": "الحاوية", - "storage_region": "المنطقة", - "storage_access_key": "مفتاح الوصول", - "storage_secret_key": "المفتاح السري", - "storage_secret_configured": "تم إعداد المفتاح", - "storage_key_placeholder": "أدخل مفتاحاً جديداً", - "storage_path_style": "فرض أسلوب المسار", - "storage_path_style_hint": "مطلوب لـ MinIO وبعض الخدمات المتوافقة مع S3", - "storage_test_connection": "اختبار الاتصال", - "storage_test_success": "نجح الاتصال", - "storage_test_failure": "فشل الاتصال", - "storage_save": "حفظ الإعداد", - "storage_saved": "تم حفظ الإعداد", - "storage_migration": "ترحيل البيانات", - "storage_migration_coming_soon": "أدوات الترحيل قريباً", - "migration_status_label": "حالة الترحيل", - "migration_start": "بدء الترحيل", - "migration_pause": "إيقاف مؤقت", - "migration_resume": "استئناف", - "migration_verify": "التحقق", - "migration_complete": "إكمال", - "migration_started": "بدأ الترحيل", - "migration_paused_msg": "الترحيل متوقف مؤقتاً", - "migration_resumed_msg": "استُؤنف الترحيل", - "migration_completed_msg": "اكتمل الترحيل بنجاح", - "migration_verifying": "جارٍ التحقق...", - "migration_verify_passed": "اجتاز التحقق", - "migration_verify_failed": "فشل التحقق", - "migration_failed_blobs": "كتل فاشلة", - "testing": "جارٍ الاختبار...", - "smtp_disabled": "معطّل (المضيف غير مضبوط)", - "smtp_enabled": "مفعّل", - "smtp_enabled_label": "الحالة", - "smtp_intro": "يتم تكوين SMTP حصريًا عبر متغيرات البيئة (OXICLOUD_SMTP_*). تُقرأ القيم أدناه من الخادم قيد التشغيل — لتغييرها، عدّل البيئة وأعد تشغيل OxiCloud.", - "smtp_not_configured": "SMTP غير مكوَّن على هذا الخادم.", - "smtp_send_failed": "فشل الإرسال.", - "smtp_send_test": "إرسال بريد اختباري", - "smtp_sending": "جارٍ الإرسال…", - "smtp_sent": "تم إرسال البريد الاختباري.", - "smtp_server_code": "رد الخادم", - "smtp_test_intro": "يرسل رسالة تشخيصية محددة مسبقًا إلى المستلم أدناه ويُبلِّغ عن استجابة خادم SMTP لتتمكن من مطابقتها مع سجلات المرحّل الخاص بك.", - "smtp_test_missing_to": "أدخل عنوان المستلم.", - "smtp_test_title": "إرسال بريد اختباري", - "smtp_test_to": "عنوان المستلم", - "smtp_title": "البريد الصادر (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "الملف الشخصي", - "back_to_app": "العودة إلى OxiCloud", - "loading": "جارٍ التحميل…", - "not_authenticated": "غير مُصادق", - "not_authenticated_desc": "سجّل الدخول لعرض ملفك الشخصي.", - "sign_in": "تسجيل الدخول", - "role_admin": "مسؤول", - "role_user": "مستخدم", - "account_details": "تفاصيل الحساب", - "username": "اسم المستخدم", - "email": "البريد الإلكتروني", - "role": "الدور", - "last_login": "آخر دخول", - "storage": "التخزين", - "used": "مستخدم", - "quota": "الحصة", - "usage": "الاستخدام", - "unlimited": "غير محدود", - "app_passwords": "كلمات مرور التطبيقات", - "app_pw_desc": "أنشئ كلمات مرور لعملاء WebDAV و CalDAV و CardDAV. تُعرض كل كلمة مرور مرة واحدة فقط.", - "app_pw_label_placeholder": "التسمية (مثلاً Thunderbird، macOS)", - "generate": "إنشاء", - "generating": "جارٍ الإنشاء…", - "new_password_for": "كلمة مرور جديدة لـ", - "copy_warning": "انسخ كلمة المرور الآن. لن تتمكن من رؤيتها مرة أخرى.", - "copy_to_clipboard": "نسخ إلى الحافظة", - "col_label": "التسمية", - "col_created": "تاريخ الإنشاء", - "col_last_used": "آخر استخدام", - "col_status": "الحالة", - "active": "نشط", - "revoked": "ملغى", - "revoke_title": "إلغاء", - "no_app_passwords": "لا توجد كلمات مرور تطبيقات بعد.", - "client_sessions": "جلسات العميل", - "client_sessions_desc": "تُنشأ تلقائيًا عند اتصال عميل متوافق مع Nextcloud.", - "col_client": "العميل", - "never": "أبداً", - "just_now": "الآن", - "minutes_ago": "منذ {{n}} دقيقة", - "hours_ago": "منذ {{n}} ساعة", - "days_ago": "منذ {{n}} يوم", - "edit_profile": "تعديل الملف الشخصي", - "edit_oidc_managed": "لتغيير معلوماتك (الاسم، الاسم الأول، صورة الملف الشخصي، …)، يرجى تحديثها لدى مزود الهوية. ستظهر تغييراتك عند تسجيل الدخول التالي.", - "username_claim_hint": "2-64 حرفًا، أحرف / أرقام / نقطة / شرطة / شرطة سفلية. بمجرد الاختيار، لا يمكن تغيير اسم المستخدم (عملاء DAV/NextCloud يعتمدون عليه).", - "username_already_claimed": "اسم المستخدم محدد ولا يمكن تغييره (عملاء DAV/NextCloud يعتمدون عليه).", - "given_name": "الاسم الأول", - "family_name": "اسم العائلة", - "notify_on_share": "أرسل لي بريدًا إلكترونيًا عندما يشاركني شخص ما", - "notify_on_share_hint": "عند إلغاء التحديد، ستظل المشاركات تظهر في حسابك — لن تتلقى فقط بريدًا إلكترونيًا بشأنها.", - "save_profile": "حفظ التغييرات", - "profile_saved": "تم تحديث الملف الشخصي", - "profile_no_changes": "لا توجد تغييرات لحفظها.", - "profile_save_failed": "فشل الحفظ", - "username_taken_error": "اسم المستخدم هذا مستخدم بالفعل.", - "username_immutable_error": "اسم المستخدم الخاص بك محدد بالفعل ولا يمكن تغييره هنا. اتصل بالمسؤول إذا كنت بحاجة إلى إعادة التسمية.", - "change_password": "تغيير كلمة المرور", - "current_password": "كلمة المرور الحالية", - "new_password": "كلمة المرور الجديدة", - "min_8_chars": "8 أحرف على الأقل", - "confirm_password": "تأكيد كلمة المرور الجديدة", - "update_password": "تحديث كلمة المرور", - "updating": "جارٍ التحديث…", - "password_updated": "تم تحديث كلمة المرور بنجاح", - "passwords_no_match": "كلمتا المرور غير متطابقتين", - "password_too_short": "يجب أن تكون كلمة المرور 8 أحرف على الأقل", - "password_change_failed": "فشل تغيير كلمة المرور", - "error_network": "خطأ في الشبكة: {{message}}", - "error_label_required": "أدخل تسمية", - "error_create_pw": "فشل إنشاء كلمة المرور", - "confirm_revoke": "إلغاء كلمة المرور \"{{label}}\"؟ ستتوقف العملاء عن العمل.", - "error_revoke": "فشل الإلغاء", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "جارٍ الرفع...", - "files": "ملفات", - "complete": "{{count}} / {{total}} تم الرفع" - }, - "storage_quota_exceeded": "تجاوز حصة التخزين", - "sharedwithme": { - "pageTitle": "مشترك معي", - "pageDescription": "الملفات والمجلدات التي شاركها معك مستخدمون آخرون", - "emptyStateTitle": "لم يُشارك معك أي شيء بعد", - "emptyStateDesc": "ستظهر هنا العناصر التي يشاركها معك مستخدمون آخرون", - "loadMore": "تحميل المزيد", - "sharedBy": "مشترك من قِبل", - "colName": "الاسم", - "colType": "النوع", - "colSharedBy": "مشترك من قِبل", - "colDate": "تاريخ المشاركة", - "colPermissions": "الصلاحيات" - }, - "groupby": { - "none": "لا شيء", - "title": "التجميع حسب", - "owner": "المالك", - "shareDate": "تاريخ المشاركة", - "type": "النوع", - "type.folders": "المجلدات", - "accessedAt": "تاريخ الوصول", - "modifiedAt": "تاريخ التعديل", - "createdAt": "تاريخ الإنشاء", - "size": "الحجم", - "favoriteDate": "تاريخ المفضلة", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "جديد" - }, - "dateBucket": { - "today": "اليوم", - "last7days": "آخر 7 أيام", - "last30days": "آخر 30 يومًا" - }, - "groups": { - "title": "إدارة المجموعات", - "create_button": "إنشاء مجموعة", - "create_dialog_title": "مجموعة جديدة", - "edit_dialog_title": "إعادة تسمية المجموعة", - "name_label": "الاسم", - "name_placeholder": "engineering", - "description_label": "الوصف (اختياري)", - "members_section": "الأعضاء", - "add_member_placeholder": "إضافة مستخدم أو مجموعة…", - "no_members": "لا يوجد أعضاء بعد.", - "remove_member": "إزالة", - "delete_group": "حذف المجموعة", - "delete_confirm": "حذف المجموعة \"{name}\"؟ سيتم إلغاء الصلاحيات المرتبطة بهذه المجموعة.", - "empty_state": "لا توجد مجموعات بعد.", - "load_more": "تحميل المزيد", - "back_to_list": "رجوع", - "loading": "جارٍ التحميل…", - "virtual_badge": "النظام", - "member_count_zero": "لا يوجد أعضاء", - "member_count_one": "عضو واحد", - "member_count_other": "{count} أعضاء", - "delete_confirm_label": "اكتب اسم المجموعة للتأكيد:", - "delete_confirm_mismatch": "اكتب اسم المجموعة كما هو للتأكيد.", - "virtual_internal_name": "داخلي", - "members_loading": "جارٍ تحميل الأعضاء…", - "members_empty": "لا يوجد أعضاء", - "virtual_internal_explanation": "كل مستخدم داخلي على هذا الخادم" - }, - "myshares": { - "copyLink": "نسخ الرابط", - "deleteLink": "حذف الرابط", - "notifyByEmail": "إشعار عبر البريد الإلكتروني", - "notifyFailed": "تعذّر إرسال الإشعار.", - "notifyGroupMembers": "إشعار أعضاء المجموعة", - "notifyRateLimited": "عدد كبير من الإشعارات لهذا المستلم — حاول لاحقًا.", - "removeAccess": "إزالة الوصول", - "resendInvitation": "إعادة إرسال بريد الدعوة" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", + "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتح OxiCloud لعرض مشاركتك الجديدة:\n{{login_link}}\n\nقد تكون لديك مشاركات جديدة أخرى من {{inviter}} — سجّل الدخول لرؤية جميع العناصر المشاركة معك.\n\n— OxiCloud\n\nأنت تتلقى هذه الرسالة لأن لديك حسابًا في OxiCloud وتفضيل إشعارات المشاركة مُفعّل. يمكنك تعطيله من ملفك الشخصي (راسلني عندما يشاركني شخص ما)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "نظام تخزين سحابي بسيط" + }, + "nav": { + "files": "الملفات", + "shared": "مشاركاتي", + "recent": "الأخيرة", + "favorites": "المفضلة", + "photos": "الصور", + "music": "الموسيقى", + "trash": "سلة المهملات", + "sharedwithme": "مشتركة معي", + "profile": "الملف الشخصي", + "shared_with_me": "مشتركة معي" + }, + "photos": { + "empty_state": "لا توجد صور بعد", + "empty_hint": "ارفع صوراً أو مقاطع فيديو لعرضها هنا", + "items_selected": "محدد", + "view_daily": "يوم", + "view_monthly": "شهر", + "view_yearly": "سنة", + "group_by": "التجميع حسب" + }, + "music": { + "create_playlist": "إنشاء قائمة تشغيل", + "playlists": "قوائم التشغيل", + "no_playlists": "لا توجد قوائم تشغيل بعد", + "select_playlist": "اختر قائمة تشغيل", + "select_hint": "اختر قائمة تشغيل من الشريط الجانبي أو أنشئ واحدة جديدة", + "add_tracks": "إضافة مقاطع", + "no_tracks": "لا توجد مقاطع في هذه القائمة", + "unknown_artist": "فنان غير معروف", + "unknown_title": "غير معروف", + "confirm_delete": "هل تريد حذف هذه القائمة؟", + "playlist_name": "اسم القائمة", + "create": "إنشاء", + "delete": "حذف", + "share": "مشاركة", + "edit": "تعديل", + "play_all": "تشغيل الكل", + "shuffle": "عشوائي", + "repeat": "تكرار", + "repeat_one": "تكرار واحد", + "queue": "قائمة الانتظار", + "queue_empty": "قائمة الانتظار فارغة", + "not_playing": "لا يتم التشغيل", + "play": "تشغيل", + "pause": "إيقاف مؤقت", + "previous": "السابق", + "next": "التالي", + "volume": "مستوى الصوت", + "mute": "كتم", + "unmute": "إلغاء الكتم", + "title": "العنوان", + "artist": "الفنان", + "album": "الألبوم", + "tracks": "مقاطع", + "add": "إضافة", + "added": "تمت الإضافة!", + "added_to_playlist": "تمت إضافته إلى القائمة", + "add_to_playlist": "إضافة إلى القائمة", + "load_error": "خطأ في تحميل القوائم", + "add_error": "تعذر إضافة المقاطع", + "no_playlists_yet": "لا توجد قوائم بعد. أنشئ واحدة أولاً!", + "selected_files": "محدد:", + "error": "خطأ", + "search_audio": "البحث عن ملفات صوتية…", + "no_audio_files": "لم يتم العثور على ملفات صوتية", + "selected": "محدد", + "loading": "جارٍ التحميل…", + "search_error": "تعذر تحميل الملفات الصوتية", + "adding": "جارٍ الإضافة…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "السابق" + }, + "actions": { + "search": "البحث في الملفات...", + "new_folder": "مجلد جديد", + "upload": "رفع", + "upload_files": "رفع ملفات", + "upload_folder": "رفع مجلد", + "upload.uploading": "جارٍ الرفع...", + "upload.complete": "{count} / {total} تم رفعها", + "upload.files": "ملفات", + "rename": "إعادة التسمية", + "move": "نقل إلى...", + "move_to": "نقل إلى", + "delete": "حذف", + "download": "تحميل", + "view": "عرض", + "cancel": "إلغاء", + "confirm": "تأكيد", + "share": "مشاركة", + "favorite": "إضافة للمفضلة", + "unfavorite": "إزالة من المفضلة", + "copy": "نسخ", + "notify": "إشعار", + "send": "إرسال", + "clear_recent": "مسح الأخيرة", + "logout": "تسجيل الخروج", + "create": "إنشاء", + "search_btn": "بحث", + "close": "إغلاق", + "delete_permanently": "حذف نهائياً", + "empty_trash": "تفريغ سلة المهملات", + "open_parent_folder": "الانتقال إلى المجلد الأصلي", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "المظهر", + "about": "حول OxiCloud", + "about_description": "منصة تخزين سحابي مبنية بـ Rust و Clean Architecture. سريعة وآمنة وخاصة.", + "admin_panel": "لوحة الإدارة", + "profile": "ملفي الشخصي", + "role_user": "مستخدم", + "theme": { + "light": "فاتح", + "dark": "داكن", + "auto": "مثل النظام" + }, + "manage_groups": "إدارة المجموعات", + "admin": "المسؤول" + }, + "share": { + "dialogTitle": "رابط المشاركة", + "linkLabel": "رابط المشاركة:", + "copyLink": "نسخ", + "permissions": "الصلاحيات:", + "permissionRead": "قراءة", + "permissionWrite": "كتابة", + "permissionReshare": "إعادة مشاركة", + "password": "حماية بكلمة مرور:", + "generatePassword": "توليد", + "expiration": "تاريخ انتهاء الصلاحية:", + "update": "تحديث المشاركة", + "remove": "إزالة المشاركة", + "notifyTitle": "إرسال إشعار", + "notifyEmailLabel": "عنوان البريد الإلكتروني:", + "notifyMessageLabel": "رسالة (اختياري):", + "notifySend": "إرسال الإشعار", + "shareWithOthers": "مشاركة مع آخرين", + "sharePublicly": "مشاركة عامة", + "shareSettings": "إعدادات المشاركة", + "shareCopied": "تم نسخ الرابط إلى الحافظة", + "shareCreated": "تم إنشاء رابط المشاركة بنجاح", + "shareUpdated": "تم تحديث إعدادات المشاركة بنجاح", + "shareRemoved": "تمت إزالة المشاركة بنجاح", + "inviteByEmail": "دعوة عبر البريد الإلكتروني — ستُرسل الدعوة", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "نسخ", + "copy_failed": "Could not copy link", + "download": "تحميل", + "files": "الملفات", + "folders": "مجلدات", + "link_name": "Link name (optional)", + "notifyByEmail": "إشعار عبر البريد الإلكتروني", + "revoke": "Remove", + "role_label": "الدور" + }, + "share_dialogTitle": "رابط المشاركة", + "share_linkLabel": "رابط المشاركة:", + "share_copyLink": "نسخ", + "share_permissions": "الصلاحيات:", + "share_permissionRead": "قراءة", + "share_permissionWrite": "كتابة", + "share_permissionReshare": "إعادة مشاركة", + "share_password": "حماية بكلمة مرور:", + "share_generatePassword": "توليد", + "share_expiration": "تاريخ انتهاء الصلاحية:", + "share_update": "تحديث المشاركة", + "share_remove": "إزالة المشاركة", + "share_notifyTitle": "إرسال إشعار", + "share_notifyEmailLabel": "عنوان البريد الإلكتروني:", + "share_notifyMessageLabel": "رسالة (اختياري):", + "share_notifySend": "إرسال الإشعار", + "shared": { + "backToFiles": "العودة إلى الملفات", + "pageTitle": "الموارد المشتركة", + "pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", + "filterType": "النوع:", + "filterAll": "الكل", + "filterFiles": "ملفات", + "filterFolders": "مجلدات", + "sortBy": "ترتيب حسب:", + "sortByName": "الاسم", + "sortByDate": "تاريخ المشاركة", + "sortByExpiration": "انتهاء الصلاحية", + "search": "بحث", + "colName": "الاسم", + "colType": "النوع", + "colDateShared": "تاريخ المشاركة", + "colExpiration": "انتهاء الصلاحية", + "colPermissions": "الصلاحيات", + "colPassword": "كلمة المرور", + "colActions": "الإجراءات", + "emptyStateTitle": "لا توجد موارد مشتركة بعد", + "emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", + "goToFiles": "الذهاب إلى الملفات", + "typeFile": "ملف", + "typeFolder": "مجلد", + "noExpiration": "بدون انتهاء صلاحية", + "hasPassword": "نعم", + "noPassword": "لا", + "editShare": "تعديل المشاركة", + "notifyShare": "إشعار شخص ما", + "copyLink": "نسخ الرابط", + "removeShare": "إزالة المشاركة", + "linkCopied": "تم نسخ الرابط إلى الحافظة!", + "linkCopyFailed": "فشل نسخ الرابط", + "itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", + "itemRemoved": "تمت إزالة المشاركة بنجاح", + "invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", + "notificationSent": "تم إرسال الإشعار بنجاح", + "notificationFailed": "فشل إرسال الإشعار", + "shared_backToFiles": "العودة إلى الملفات", + "shared_pageTitle": "الموارد المشتركة", + "shared_pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", + "shared_filterType": "النوع:", + "shared_filterAll": "الكل", + "shared_filterFiles": "ملفات", + "shared_filterFolders": "مجلدات", + "shared_sortBy": "ترتيب حسب:", + "shared_sortByName": "الاسم", + "shared_sortByDate": "تاريخ المشاركة", + "shared_sortByExpiration": "انتهاء الصلاحية", + "shared_search": "بحث", + "shared_colName": "الاسم", + "shared_colType": "النوع", + "shared_colDateShared": "تاريخ المشاركة", + "shared_colExpiration": "انتهاء الصلاحية", + "shared_colPermissions": "الصلاحيات", + "shared_colPassword": "كلمة المرور", + "shared_colActions": "الإجراءات", + "shared_emptyStateTitle": "لا توجد موارد مشتركة بعد", + "shared_emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", + "shared_goToFiles": "الذهاب إلى الملفات", + "shared_typeFile": "ملف", + "shared_typeFolder": "مجلد", + "shared_noExpiration": "بدون انتهاء صلاحية", + "shared_hasPassword": "نعم", + "shared_noPassword": "لا", + "shared_editShare": "تعديل المشاركة", + "shared_notifyShare": "إشعار شخص ما", + "shared_copyLink": "نسخ الرابط", + "shared_removeShare": "إزالة المشاركة", + "shared_linkCopied": "تم نسخ الرابط إلى الحافظة!", + "shared_linkCopyFailed": "فشل نسخ الرابط", + "shared_itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", + "shared_itemRemoved": "تمت إزالة المشاركة بنجاح", + "shared_invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", + "shared_notificationSent": "تم إرسال الإشعار بنجاح", + "shared_notificationFailed": "فشل إرسال الإشعار" + }, + "files": { + "name": "الاسم", + "type": "النوع", + "size": "الحجم", + "modified": "تاريخ التعديل", + "no_files": "لا توجد ملفات في هذا المجلد", + "empty_hint": "ارفع ملفات أو أنشئ مجلدات للبدء", + "loading": "جارٍ تحميل الملفات…", + "view_grid": "عرض شبكي", + "view_list": "عرض قائمة", + "file_types": { + "document": "مستند", + "image": "صورة", + "video": "فيديو", + "audio": "صوت", + "pdf": "PDF", + "text": "نص", + "folder": "مجلد", + "spreadsheet": "جدول بيانات", + "presentation": "عرض تقديمي", + "archive": "أرشيف", + "installer": "مثبّت", + "code": "كود" + }, + "owner": "المالك", + "add_favorites": "إضافة للمفضلة", + "added_favorites": "أُضيف للمفضلة", + "col_name": "الاسم", + "col_owner": "المالك", + "col_size": "الحجم", + "col_type": "النوع", + "copy": "نسخ", + "edit": "تعديل", + "file": "ملف", + "folder": "مجلد", + "new_folder": "مجلد جديد", + "share": "مشاركة", + "view": "عرض" + }, + "dialogs": { + "rename_folder": "إعادة تسمية المجلد", + "rename_file": "إعادة تسمية الملف", + "new_name": "الاسم الجديد", + "new_folder_title": "مجلد جديد", + "folder_name": "اسم المجلد", + "folder_placeholder": "مجلدي", + "rename_title": "إعادة التسمية", + "move_file": "نقل الملف", + "move_folder": "نقل المجلد", + "select_destination": "اختر المجلد الوجهة:", + "select_this_folder": "اختيار هذا المجلد", + "go_to_parent": ".. (المجلد الأعلى)", + "no_subfolders": "لا توجد مجلدات فرعية", + "root": "الجذر", + "delete_confirmation": "هل أنت متأكد أنك تريد حذف", + "and_contents": "وجميع محتوياته", + "no_undo": "لا يمكن التراجع عن هذا الإجراء", + "confirm_title": "تأكيد الإجراء", + "confirm_delete": "نقل إلى سلة المهملات", + "confirm_delete_file": "هل أنت متأكد أنك تريد نقل الملف \"{{name}}\" إلى سلة المهملات؟", + "confirm_delete_folder": "هل أنت متأكد أنك تريد نقل المجلد \"{{name}}\" وجميع محتوياته إلى سلة المهملات؟", + "confirm_permanent_delete": "حذف نهائي", + "confirm_permanent_delete_msg": "هل أنت متأكد أنك تريد حذف هذا العنصر نهائياً؟ لا يمكن التراجع عن هذا الإجراء.", + "confirm_empty_trash": "تفريغ سلة المهملات", + "confirm_delete_share": "حذف رابط المشاركة", + "confirm_delete_share_msg": "هل أنت متأكد أنك تريد حذف رابط المشاركة هذا؟", + "share_file": "مشاركة الملف", + "share_folder": "مشاركة المجلد", + "existing_shares": "المشاركات الحالية", + "share_options": "خيارات المشاركة", + "password": "كلمة المرور", + "expiration": "انتهاء الصلاحية", + "permissions": "الصلاحيات", + "generated_link": "الرابط المُنشأ", + "notify": "إرسال إشعار", + "recipient": "المستلم", + "message": "الرسالة", + "move_to_home": "نقل إلى المجلد الرئيسي" + }, + "dropzone": { + "drag_files": "اسحب الملفات هنا أو انقر للاختيار", + "drop_files": "أسقط الملفات للرفع" + }, + "permissions": { + "read": "قراءة", + "write": "كتابة", + "reshare": "إعادة مشاركة" + }, + "errors": { + "file_not_found": "الملف غير موجود", + "folder_not_found": "المجلد غير موجود", + "delete_error": "خطأ في الحذف", + "upload_error": "خطأ في رفع الملف", + "rename_error": "خطأ في إعادة التسمية", + "move_error": "خطأ في النقل", + "empty_name": "لا يمكن أن يكون الاسم فارغاً", + "name_exists": "ملف أو مجلد بهذا الاسم موجود بالفعل", + "generic_error": "حدث خطأ", + "group_name_invalid": "يجب أن يتطابق اسم المجموعة مع صيغة بادئة البريد الإلكتروني (حروف، أرقام، نقطة، شرطة، شرطة سفلية؛ 1–64 حرفًا).", + "group_cycle": "سينشئ هذا العضو مرجعًا دائريًا بين المجموعات.", + "group_depth_exceeded": "تتجاوز عمق التعشيش الحد الأقصى المسموح (8).", + "group_virtual_immutable": "مجموعة «Internal» تدار من قبل النظام ولا يمكن تعديلها.", + "group_not_found": "المجموعة غير موجودة.", + "group_name_taken": "توجد بالفعل مجموعة بهذا الاسم." + }, + "breadcrumb": { + "home": "الرئيسية" + }, + "trash": { + "empty_trash": "تفريغ سلة المهملات", + "empty_state": "سلة المهملات فارغة", + "original_location": "الموقع الأصلي", + "deleted_date": "تاريخ الحذف", + "remaining": "المتبقي", + "actions": "الإجراءات", + "restore": "استعادة", + "delete_permanently": "حذف نهائياً", + "empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً.", + "groupby": { + "remaining_days": "الأيام المتبقية", + "trashed_time": "وقت الحذف" + }, + "delete": "حذف نهائياً", + "empty_action": "تفريغ سلة المهملات" + }, + "daysRemaining": { + "expired": "منتهية الصلاحية", + "today": "اليوم", + "tomorrow": "غدًا", + "inDays": "{{count}} يوم" + }, + "expiryChip": { + "never": "لا تنتهي الصلاحية", + "expired": "منتهية الصلاحية", + "today": "تنتهي الصلاحية اليوم", + "tomorrow": "تنتهي الصلاحية غدًا", + "inDays": "تنتهي الصلاحية خلال {{count}} يوم", + "onDate": "تنتهي الصلاحية في {{date}}" + }, + "auth": { + "login_title": "تسجيل الدخول", + "username": "اسم المستخدم", + "username_placeholder": "أدخل اسم المستخدم", + "login_identifier": "اسم المستخدم أو البريد الإلكتروني", + "login_identifier_placeholder": "أدخل اسم المستخدم أو البريد الإلكتروني", + "password": "كلمة المرور", + "password_placeholder": "أدخل كلمة المرور", + "login_button": "تسجيل الدخول", + "no_account": "ليس لديك حساب؟", + "register": "إنشاء حساب", + "admin_setup": "أول مرة؟", + "setup": "إعداد المسؤول", + "register_title": "إنشاء حساب", + "email": "البريد الإلكتروني", + "email_placeholder": "أدخل بريدك الإلكتروني", + "confirm_password": "تأكيد كلمة المرور", + "confirm_password_placeholder": "أكد كلمة المرور", + "register_button": "إنشاء حساب", + "have_account": "لديك حساب بالفعل؟", + "login": "تسجيل الدخول", + "setup_title": "الإعداد الأولي", + "setup_step1": "المسؤول", + "setup_step2": "النظام", + "setup_step3": "مكتمل", + "admin_username": "اسم مستخدم المسؤول", + "admin_email": "بريد المسؤول الإلكتروني", + "admin_password": "كلمة مرور المسؤول", + "create_admin": "إنشاء حساب المسؤول", + "back_to_login": "تم الإعداد مسبقاً؟", + "admin_success": "تم إنشاء حساب المسؤول بنجاح! يمكنك الآن تسجيل الدخول.", + "account_success": "تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول.", + "passwords_mismatch": "كلمات المرور غير متطابقة", + "admin_create_error": "خطأ في إنشاء حساب المسؤول", + "or": "أو", + "sso_login": "تسجيل الدخول عبر SSO", + "sso_login_provider": "تسجيل الدخول عبر {{provider}}", + "magicLinkHint": "ليس لديك كلمة مرور؟ أدخل بريدك الإلكتروني وسنرسل لك رابط تسجيل دخول لمرة واحدة.", + "magicLinkEmailLabel": "عنوان البريد الإلكتروني", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "إرسال رابط تسجيل الدخول", + "magicLinkSent": "إذا كان هناك حساب لهذا البريد الإلكتروني، فسيتم إرسال رابط تسجيل الدخول. تحقق من صندوق الوارد.", + "magicLinkUnavailable": "تسجيل الدخول عبر البريد الإلكتروني غير متاح على هذا الخادم.", + "magicLinkNetworkError": "تعذر الوصول إلى الخادم: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "عنوان البريد الإلكتروني", + "magic_hint": "ليس لديك كلمة مرور؟ أدخل بريدك الإلكتروني وسنرسل لك رابط تسجيل دخول لمرة واحدة.", + "magic_unavailable": "تسجيل الدخول عبر البريد الإلكتروني غير متاح على هذا الخادم.", + "passwords_match": "Passwords match", + "sign_in": "تسجيل الدخول" + }, + "storage": { + "title": "التخزين", + "calculating": "جارٍ الحساب...", + "used": "{{percentage}}% مستخدم ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "لا يمكن معاينة هذا النوع من الملفات.", + "download_file": "تحميل الملف", + "zoom_in": "تكبير", + "zoom_out": "تصغير", + "zoom_reset": "إعادة تعيين التكبير" + }, + "language_selector": { + "title": "!مرحباً", + "subtitle": "اختر لغتك للمتابعة", + "continue": "متابعة", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "لا توجد مفضلات بعد", + "empty_hint": "ضع نجمة على الملفات أو المجلدات لإضافتها إلى المفضلة", + "add": "إضافة للمفضلة", + "remove": "إزالة من المفضلة", + "added_title": "أُضيف للمفضلة", + "added_msg": "أُضيف للمفضلة", + "removed_title": "أُزيل من المفضلة", + "removed_msg": "أُزيل من المفضلة" + }, + "recent": { + "title": "الأخيرة", + "clear": "مسح الأخيرة", + "accessed": "تم الوصول", + "empty_state": "لا توجد ملفات حديثة", + "empty_hint": "الملفات التي تفتحها ستظهر هنا", + "loadMore": "تحميل المزيد" + }, + "notifications": { + "file_renamed": "تمت إعادة تسمية الملف", + "file_renamed_to": "تمت إعادة تسمية الملف إلى \"{{name}}\"", + "folder_renamed": "تمت إعادة تسمية المجلد", + "folder_renamed_to": "تمت إعادة تسمية المجلد إلى \"{{name}}\"", + "file_uploaded": "تم رفع الملف", + "file_deleted": "تم نقل الملف إلى سلة المهملات", + "folder_deleted": "تم نقل المجلد إلى سلة المهملات", + "item_deleted_permanently": "تم حذف العنصر نهائياً", + "trash_emptied": "تم تفريغ سلة المهملات بنجاح", + "title": "الإشعارات", + "empty": "لا توجد إشعارات", + "link_created": "تم إنشاء الرابط", + "share_success": "تم إنشاء رابط المشاركة بنجاح", + "upload_files_section_title": "التحميل غير متاح هنا", + "upload_files_section_body": "انتقل إلى قسم الملفات لتحميل الملفات" + }, + "batch": { + "one_selected": "عنصر واحد محدد", + "n_selected": "{{count}} عناصر محددة", + "confirm_delete": "هل أنت متأكد أنك تريد نقل {{count}} عنصر إلى سلة المهملات؟", + "move_title": "نقل {{count}} عنصر", + "add_favorites": "إضافة للمفضلة", + "move_copy": "نقل أو نسخ" + }, + "admin": { + "page_title": "لوحة الإدارة", + "back_to_app": "العودة إلى OxiCloud", + "loading": "جارٍ التحميل…", + "access_denied": "الوصول مرفوض", + "access_denied_desc": "صلاحيات المسؤول مطلوبة.", + "sign_in": "تسجيل الدخول", + "tab_dashboard": "لوحة المعلومات", + "tab_users": "المستخدمون", + "tab_oidc": "SSO / OIDC", + "total_users": "إجمالي المستخدمين", + "active_users": "المستخدمون النشطون", + "admins": "المسؤولون", + "version": "الإصدار", + "storage_overview": "نظرة عامة على التخزين", + "used": "مستخدم", + "total_quota": "الحصة الإجمالية", + "usage_pct": "نسبة الاستخدام", + "users_over_80": "مستخدمون >80%", + "users_over_quota": "مستخدمون تجاوزوا الحصة", + "system": "النظام", + "auth_label": "المصادقة", + "oidc_label": "OIDC", + "quotas_label": "الحصص", + "enabled": "مفعّل", + "disabled": "معطّل", + "active": "نشط", + "off": "متوقف", + "allow_registration": "السماح بالتسجيل العام", + "registration_warning": "التسجيل العام معطّل. فقط المسؤولون يمكنهم إنشاء مستخدمين.", + "user_management": "إدارة المستخدمين", + "create_user": "إنشاء مستخدم", + "col_user": "المستخدم", + "col_role": "الدور", + "col_auth": "المصادقة", + "col_status": "الحالة", + "col_storage": "التخزين", + "col_last_login": "آخر دخول", + "col_actions": "الإجراءات", + "loading_users": "جارٍ تحميل المستخدمين…", + "failed_load_users": "فشل التحميل", + "no_users_found": "لم يتم العثور على مستخدمين", + "showing_users": "عرض {{from}}-{{to}} من {{total}}", + "prev": "السابق", + "next": "التالي", + "inactive": "غير نشط", + "you_badge": "(أنت)", + "local": "محلي", + "never": "أبداً", + "just_now": "الآن", + "minutes_ago": "منذ {{n}} دقيقة", + "hours_ago": "منذ {{n}} ساعة", + "days_ago": "منذ {{n}} يوم", + "edit_quota_title": "تعديل الحصة", + "reset_password_title": "إعادة تعيين كلمة المرور", + "toggle_role_title": "تبديل الدور", + "deactivate_title": "تعطيل", + "activate_title": "تفعيل", + "delete_title": "حذف", + "sso_title": "تسجيل الدخول الموحد (OIDC / SSO)", + "enable_sso": "تفعيل مصادقة SSO", + "provider_name": "اسم الموفر", + "issuer_url": "عنوان المُصدر", + "issuer_url_hint": "عنوان مُصدر OpenID Connect", + "auto_discover": "اكتشاف تلقائي", + "discovering": "جارٍ الاكتشاف…", + "client_id": "معرّف العميل", + "client_secret": "سر العميل", + "client_secret_placeholder": "اتركه فارغاً للاحتفاظ بالقيمة", + "secret_configured": "سر العميل مُهيأ بالفعل", + "callback_url": "عنوان الاستدعاء", + "callback_url_hint": "(سجّل في IdP)", + "advanced_settings": "إعدادات متقدمة", + "scopes": "النطاقات", + "auto_provision": "إنشاء تلقائي عند أول دخول", + "admin_groups": "مجموعات المسؤولين", + "admin_groups_hint": "أسماء مجموعات OIDC مفصولة بفواصل", + "disable_password": "تعطيل الدخول بكلمة المرور (OIDC فقط)", + "password_warning": "سيمنع جميع عمليات الدخول بالمرور!", + "test_btn": "اختبار", + "save_btn": "حفظ", + "saving": "جارٍ الحفظ…", + "settings_saved": "تم الحفظ — OIDC الآن {{status}}", + "quota_modal_title": "تحديث حصة التخزين", + "quota_user_label": "المستخدم:", + "new_quota": "حصة جديدة", + "quota_unlimited_hint": "0 لغير محدود", + "cancel": "إلغاء", + "create_user_title": "إنشاء مستخدم جديد", + "username_label": "اسم المستخدم", + "username_placeholder": "اسم_المستخدم", + "username_hint": "3–32 حرفاً", + "password_label": "كلمة المرور", + "password_placeholder": "8 أحرف على الأقل", + "email_label": "البريد", + "email_optional": "(اختياري)", + "email_placeholder": "user@example.com (يُنشأ تلقائياً)", + "role_label": "الدور", + "role_user": "مستخدم", + "role_admin": "مسؤول", + "quota_label": "الحصة", + "creating": "جارٍ الإنشاء…", + "reset_pw_title": "إعادة تعيين كلمة المرور", + "new_password_label": "كلمة مرور جديدة", + "resetting": "جارٍ إعادة التعيين…", + "reset_btn": "إعادة تعيين", + "confirm_role_change": "تغيير الدور إلى {{role}}؟", + "confirm_deactivate": "هل أنت متأكد من التعطيل؟", + "confirm_activate": "هل أنت متأكد من التفعيل؟", + "confirm_delete_user": "حذف المستخدم \"{{name}}\"؟ لا يمكن التراجع!", + "confirm_action": "تأكيد الإجراء", + "confirm_yes": "تأكيد", + "confirm_no": "إلغاء", + "error_username_short": "الاسم 3 أحرف على الأقل", + "error_password_short": "كلمة المرور 8 أحرف على الأقل", + "error_generic": "فشل", + "error_network": "خطأ في الشبكة: {{message}}", + "error_create_user": "فشل إنشاء المستخدم", + "tab_storage": "التخزين", + "storage_title": "إعداد التخزين", + "storage_current_backend": "الواجهة الخلفية الحالية", + "storage_total_blobs": "إجمالي الكتل", + "storage_total_size": "الحجم الإجمالي", + "storage_dedup_ratio": "نسبة إزالة التكرار", + "storage_backend": "الواجهة الخلفية", + "storage_local": "محلي", + "storage_s3": "متوافق مع S3", + "storage_provider_preset": "إعداد مسبق للمزود", + "storage_preset_custom": "مخصص", + "storage_endpoint_url": "رابط نقطة النهاية", + "storage_endpoint_hint": "اتركه فارغاً لـ AWS S3", + "storage_bucket": "الحاوية", + "storage_region": "المنطقة", + "storage_access_key": "مفتاح الوصول", + "storage_secret_key": "المفتاح السري", + "storage_secret_configured": "تم إعداد المفتاح", + "storage_key_placeholder": "أدخل مفتاحاً جديداً", + "storage_path_style": "فرض أسلوب المسار", + "storage_path_style_hint": "مطلوب لـ MinIO وبعض الخدمات المتوافقة مع S3", + "storage_test_connection": "اختبار الاتصال", + "storage_test_success": "نجح الاتصال", + "storage_test_failure": "فشل الاتصال", + "storage_save": "حفظ الإعداد", + "storage_saved": "تم حفظ الإعداد", + "storage_migration": "ترحيل البيانات", + "storage_migration_coming_soon": "أدوات الترحيل قريباً", + "migration_status_label": "حالة الترحيل", + "migration_start": "بدء الترحيل", + "migration_pause": "إيقاف مؤقت", + "migration_resume": "استئناف", + "migration_verify": "التحقق", + "migration_complete": "إكمال", + "migration_started": "بدأ الترحيل", + "migration_paused_msg": "الترحيل متوقف مؤقتاً", + "migration_resumed_msg": "استُؤنف الترحيل", + "migration_completed_msg": "اكتمل الترحيل بنجاح", + "migration_verifying": "جارٍ التحقق...", + "migration_verify_passed": "اجتاز التحقق", + "migration_verify_failed": "فشل التحقق", + "migration_failed_blobs": "كتل فاشلة", + "testing": "جارٍ الاختبار...", + "smtp_disabled": "معطّل (المضيف غير مضبوط)", + "smtp_enabled": "مفعّل", + "smtp_enabled_label": "الحالة", + "smtp_intro": "يتم تكوين SMTP حصريًا عبر متغيرات البيئة (OXICLOUD_SMTP_*). تُقرأ القيم أدناه من الخادم قيد التشغيل — لتغييرها، عدّل البيئة وأعد تشغيل OxiCloud.", + "smtp_not_configured": "SMTP غير مكوَّن على هذا الخادم.", + "smtp_send_failed": "فشل الإرسال.", + "smtp_send_test": "إرسال بريد اختباري", + "smtp_sending": "جارٍ الإرسال…", + "smtp_sent": "تم إرسال البريد الاختباري.", + "smtp_server_code": "رد الخادم", + "smtp_test_intro": "يرسل رسالة تشخيصية محددة مسبقًا إلى المستلم أدناه ويُبلِّغ عن استجابة خادم SMTP لتتمكن من مطابقتها مع سجلات المرحّل الخاص بك.", + "smtp_test_missing_to": "أدخل عنوان المستلم.", + "smtp_test_title": "إرسال بريد اختباري", + "smtp_test_to": "عنوان المستلم", + "smtp_title": "البريد الصادر (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "المسؤولون", + "confirm_role": "تغيير الدور إلى {{role}}؟", + "dashboard": "لوحة المعلومات", + "email": "البريد", + "mig_complete": "إكمال", + "mig_pause": "إيقاف مؤقت", + "mig_resume": "استئناف", + "mig_verify_failed": "فشل التحقق", + "mig_verify_passed": "اجتاز التحقق", + "mig_verifying": "جارٍ التحقق...", + "oidc_auto_provision": "إنشاء تلقائي عند أول دخول", + "oidc_callback": "عنوان الاستدعاء", + "oidc_client_id": "معرّف العميل", + "oidc_disable_pw": "تعطيل الدخول بكلمة المرور (OIDC فقط)", + "oidc_issuer": "عنوان المُصدر", + "oidc_scopes": "النطاقات", + "password": "كلمة المرور", + "quotas": "الحصص", + "reset_pw_for": "كلمة مرور جديدة لـ", + "role": "الدور", + "smtp_fail": "فشل الإرسال.", + "smtp_send": "إرسال", + "smtp_test": "إرسال بريد اختباري", + "smtp_user_state": "المصادقة", + "status": "الحالة", + "storage": "التخزين", + "storage_endpoint": "رابط نقطة النهاية", + "storage_tab": "التخزين", + "time_min_ago": "منذ {{n}} دقيقة", + "title": "مسؤول", + "user": "المستخدم", + "username": "اسم المستخدم", + "users": "المستخدمون" + }, + "profile": { + "page_title": "الملف الشخصي", + "back_to_app": "العودة إلى OxiCloud", + "loading": "جارٍ التحميل…", + "not_authenticated": "غير مُصادق", + "not_authenticated_desc": "سجّل الدخول لعرض ملفك الشخصي.", + "sign_in": "تسجيل الدخول", + "role_admin": "مسؤول", + "role_user": "مستخدم", + "account_details": "تفاصيل الحساب", + "username": "اسم المستخدم", + "email": "البريد الإلكتروني", + "role": "الدور", + "last_login": "آخر دخول", + "storage": "التخزين", + "used": "مستخدم", + "quota": "الحصة", + "usage": "الاستخدام", + "unlimited": "غير محدود", + "app_passwords": "كلمات مرور التطبيقات", + "app_pw_desc": "أنشئ كلمات مرور لعملاء WebDAV و CalDAV و CardDAV. تُعرض كل كلمة مرور مرة واحدة فقط.", + "app_pw_label_placeholder": "التسمية (مثلاً Thunderbird، macOS)", + "generate": "إنشاء", + "generating": "جارٍ الإنشاء…", + "new_password_for": "كلمة مرور جديدة لـ", + "copy_warning": "انسخ كلمة المرور الآن. لن تتمكن من رؤيتها مرة أخرى.", + "copy_to_clipboard": "نسخ إلى الحافظة", + "col_label": "التسمية", + "col_created": "تاريخ الإنشاء", + "col_last_used": "آخر استخدام", + "col_status": "الحالة", + "active": "نشط", + "revoked": "ملغى", + "revoke_title": "إلغاء", + "no_app_passwords": "لا توجد كلمات مرور تطبيقات بعد.", + "client_sessions": "جلسات العميل", + "client_sessions_desc": "تُنشأ تلقائيًا عند اتصال عميل متوافق مع Nextcloud.", + "col_client": "العميل", + "never": "أبداً", + "just_now": "الآن", + "minutes_ago": "منذ {{n}} دقيقة", + "hours_ago": "منذ {{n}} ساعة", + "days_ago": "منذ {{n}} يوم", + "edit_profile": "تعديل الملف الشخصي", + "edit_oidc_managed": "لتغيير معلوماتك (الاسم، الاسم الأول، صورة الملف الشخصي، …)، يرجى تحديثها لدى مزود الهوية. ستظهر تغييراتك عند تسجيل الدخول التالي.", + "username_claim_hint": "2-64 حرفًا، أحرف / أرقام / نقطة / شرطة / شرطة سفلية. بمجرد الاختيار، لا يمكن تغيير اسم المستخدم (عملاء DAV/NextCloud يعتمدون عليه).", + "username_already_claimed": "اسم المستخدم محدد ولا يمكن تغييره (عملاء DAV/NextCloud يعتمدون عليه).", + "given_name": "الاسم الأول", + "family_name": "اسم العائلة", + "notify_on_share": "أرسل لي بريدًا إلكترونيًا عندما يشاركني شخص ما", + "notify_on_share_hint": "عند إلغاء التحديد، ستظل المشاركات تظهر في حسابك — لن تتلقى فقط بريدًا إلكترونيًا بشأنها.", + "save_profile": "حفظ التغييرات", + "profile_saved": "تم تحديث الملف الشخصي", + "profile_no_changes": "لا توجد تغييرات لحفظها.", + "profile_save_failed": "فشل الحفظ", + "username_taken_error": "اسم المستخدم هذا مستخدم بالفعل.", + "username_immutable_error": "اسم المستخدم الخاص بك محدد بالفعل ولا يمكن تغييره هنا. اتصل بالمسؤول إذا كنت بحاجة إلى إعادة التسمية.", + "change_password": "تغيير كلمة المرور", + "current_password": "كلمة المرور الحالية", + "new_password": "كلمة المرور الجديدة", + "min_8_chars": "8 أحرف على الأقل", + "confirm_password": "تأكيد كلمة المرور الجديدة", + "update_password": "تحديث كلمة المرور", + "updating": "جارٍ التحديث…", + "password_updated": "تم تحديث كلمة المرور بنجاح", + "passwords_no_match": "كلمتا المرور غير متطابقتين", + "password_too_short": "يجب أن تكون كلمة المرور 8 أحرف على الأقل", + "password_change_failed": "فشل تغيير كلمة المرور", + "error_network": "خطأ في الشبكة: {{message}}", + "error_label_required": "أدخل تسمية", + "error_create_pw": "فشل إنشاء كلمة المرور", + "confirm_revoke": "إلغاء كلمة المرور \"{{label}}\"؟ ستتوقف العملاء عن العمل.", + "error_revoke": "فشل الإلغاء", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "كلمتا المرور غير متطابقتين" + }, + "upload": { + "uploading": "جارٍ الرفع...", + "files": "ملفات", + "complete": "{{count}} / {{total}} تم الرفع" + }, + "storage_quota_exceeded": "تجاوز حصة التخزين", + "sharedwithme": { + "pageTitle": "مشترك معي", + "pageDescription": "الملفات والمجلدات التي شاركها معك مستخدمون آخرون", + "emptyStateTitle": "لم يُشارك معك أي شيء بعد", + "emptyStateDesc": "ستظهر هنا العناصر التي يشاركها معك مستخدمون آخرون", + "loadMore": "تحميل المزيد", + "sharedBy": "مشترك من قِبل", + "colName": "الاسم", + "colType": "النوع", + "colSharedBy": "مشترك من قِبل", + "colDate": "تاريخ المشاركة", + "colPermissions": "الصلاحيات" + }, + "groupby": { + "none": "لا شيء", + "title": "التجميع حسب", + "owner": "المالك", + "shareDate": "تاريخ المشاركة", + "type": "النوع", + "type.folders": "المجلدات", + "accessedAt": "تاريخ الوصول", + "modifiedAt": "تاريخ التعديل", + "createdAt": "تاريخ الإنشاء", + "size": "الحجم", + "favoriteDate": "تاريخ المفضلة", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "جديد", + "folders": "مجلدات" + }, + "dateBucket": { + "today": "اليوم", + "last7days": "آخر 7 أيام", + "last30days": "آخر 30 يومًا", + "unknown": "غير معروف" + }, + "groups": { + "title": "إدارة المجموعات", + "create_button": "إنشاء مجموعة", + "create_dialog_title": "مجموعة جديدة", + "edit_dialog_title": "إعادة تسمية المجموعة", + "name_label": "الاسم", + "name_placeholder": "engineering", + "description_label": "الوصف (اختياري)", + "members_section": "الأعضاء", + "add_member_placeholder": "إضافة مستخدم أو مجموعة…", + "no_members": "لا يوجد أعضاء بعد.", + "remove_member": "إزالة", + "delete_group": "حذف المجموعة", + "delete_confirm": "حذف المجموعة \"{name}\"؟ سيتم إلغاء الصلاحيات المرتبطة بهذه المجموعة.", + "empty_state": "لا توجد مجموعات بعد.", + "load_more": "تحميل المزيد", + "back_to_list": "رجوع", + "loading": "جارٍ التحميل…", + "virtual_badge": "النظام", + "member_count_zero": "لا يوجد أعضاء", + "member_count_one": "عضو واحد", + "member_count_other": "{count} أعضاء", + "delete_confirm_label": "اكتب اسم المجموعة للتأكيد:", + "delete_confirm_mismatch": "اكتب اسم المجموعة كما هو للتأكيد.", + "virtual_internal_name": "داخلي", + "members_loading": "جارٍ تحميل الأعضاء…", + "members_empty": "لا يوجد أعضاء", + "virtual_internal_explanation": "كل مستخدم داخلي على هذا الخادم", + "create": "إنشاء مجموعة", + "empty": "لا توجد مجموعات بعد.", + "members": "الأعضاء" + }, + "myshares": { + "copyLink": "نسخ الرابط", + "deleteLink": "حذف الرابط", + "notifyByEmail": "إشعار عبر البريد الإلكتروني", + "notifyFailed": "تعذّر إرسال الإشعار.", + "notifyGroupMembers": "إشعار أعضاء المجموعة", + "notifyRateLimited": "عدد كبير من الإشعارات لهذا المستلم — حاول لاحقًا.", + "removeAccess": "إزالة الوصول", + "resendInvitation": "إعادة إرسال بريد الدعوة", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "صوت", + "code": "كود", + "text": "نص" + }, + "common": { + "add": "إضافة", + "cancel": "إلغاء", + "clear": "Clear", + "close": "إغلاق", + "confirm": "تأكيد", + "copy": "نسخ", + "create": "إنشاء", + "delete": "حذف", + "download": "تحميل", + "load_more": "تحميل المزيد", + "loading": "جارٍ التحميل…", + "next": "التالي", + "no": "لا", + "previous": "السابق", + "remove": "Remove", + "rename": "إعادة التسمية", + "save": "حفظ", + "search": "بحث", + "yes": "نعم" + }, + "device": { + "continue": "متابعة", + "unknown": "غير معروف" + }, + "expiryBucket": { + "expired": "منتهية الصلاحية", + "noExpiry": "بدون انتهاء صلاحية", + "today": "اليوم", + "tomorrow": "غدًا" + }, + "nextcloud": { + "error_title": "خطأ", + "sign_in_with": "تسجيل الدخول عبر {{provider}}" + }, + "search": { + "size_label": "الحجم", + "title": "بحث", + "type": { + "audio": "صوت" + }, + "type_label": "النوع" + }, + "sizeBucket": { + "folders": "مجلدات" + }, + "view": { + "grid": "عرض شبكي", + "list": "عرض قائمة" + } } diff --git a/frontend/static/locales/de.json b/frontend/static/locales/de.json index a4771f23..f9b9ee2d 100644 --- a/frontend/static/locales/de.json +++ b/frontend/static/locales/de.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Dieser Anmeldelink ist nicht mehr gültig", - "expired_body": "Der Link ist möglicherweise abgelaufen oder wurde bereits verwendet. Wir können Ihnen einen neuen senden — er wird in wenigen Sekunden in Ihrem Posteingang sein.", - "resend_to": "Neuen Link an {{email}} senden", - "generic_unavailable": "Dieser Anmeldelink ist nicht mehr gültig. Er wurde möglicherweise bereits verwendet oder ist abgelaufen. Fordern Sie auf der Anmeldeseite einen neuen Link an.", - "service_unavailable": "Die Magic-Link-Anmeldung ist auf diesem Server nicht aktiviert.", - "internal_error": "Bei der Anmeldung ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", - "resend_failure": "Beim Senden des Links ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", - "cross_browser_title": "Anmeldung auf diesem Gerät fortsetzen?", - "cross_browser_body": "Sie haben diesen Anmeldelink in einem anderen Browser oder Gerät geöffnet als dem, von dem aus Sie ihn angefordert haben.", - "cross_browser_warning": "Wenn Sie diesen Link angefordert haben, können Sie sicher fortfahren. Falls nicht, schließen Sie diese Seite — ein Klick auf Weiter würde jemand anderen in Ihrem Konto anmelden.", - "cross_browser_continue": "Fortfahren und anmelden", - "resend_confirmation_title": "Prüfen Sie Ihren Posteingang", - "resend_confirmation_body": "Falls der Anmeldelink zu einem aktiven Konto gehörte, wurde gerade ein neuer Link gesendet. Bitte prüfen Sie Ihren Posteingang.", - "return_link": "Zurück zu OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", - "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie ihn, indem Sie auf den folgenden Link klicken:\n{{link}}\n\nDer Link kann nur einmal verwendet werden und läuft in {{ttl_hours}} Stunden ab.\nFalls Sie diese Einladung nicht erwartet haben, können Sie diese Nachricht ignorieren.\n\n— OxiCloud" - }, - "login": { - "subject": "Anmeldung bei OxiCloud", - "body": "Hallo,\n\nVerwenden Sie den Link unten, um sich bei OxiCloud anzumelden. Der Link kann nur einmal verwendet werden und läuft in {{ttl_minutes}} Minuten ab. Öffnen Sie ihn auf demselben Gerät, von dem aus Sie ihn angefordert haben.\n\n{{link}}\n\nFalls Sie diesen Anmeldelink nicht angefordert haben, können Sie diese Nachricht ignorieren — es ist keine weitere Aktion erforderlich.\n\n— OxiCloud" - }, - "kind_file": "Datei", - "kind_folder": "Ordner", - "english_fallback_divider": "--- Englische Version unten ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Dieser Anmeldelink ist nicht mehr gültig", + "expired_body": "Der Link ist möglicherweise abgelaufen oder wurde bereits verwendet. Wir können Ihnen einen neuen senden — er wird in wenigen Sekunden in Ihrem Posteingang sein.", + "resend_to": "Neuen Link an {{email}} senden", + "generic_unavailable": "Dieser Anmeldelink ist nicht mehr gültig. Er wurde möglicherweise bereits verwendet oder ist abgelaufen. Fordern Sie auf der Anmeldeseite einen neuen Link an.", + "service_unavailable": "Die Magic-Link-Anmeldung ist auf diesem Server nicht aktiviert.", + "internal_error": "Bei der Anmeldung ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", + "resend_failure": "Beim Senden des Links ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", + "cross_browser_title": "Anmeldung auf diesem Gerät fortsetzen?", + "cross_browser_body": "Sie haben diesen Anmeldelink in einem anderen Browser oder Gerät geöffnet als dem, von dem aus Sie ihn angefordert haben.", + "cross_browser_warning": "Wenn Sie diesen Link angefordert haben, können Sie sicher fortfahren. Falls nicht, schließen Sie diese Seite — ein Klick auf Weiter würde jemand anderen in Ihrem Konto anmelden.", + "cross_browser_continue": "Fortfahren und anmelden", + "resend_confirmation_title": "Prüfen Sie Ihren Posteingang", + "resend_confirmation_body": "Falls der Anmeldelink zu einem aktiven Konto gehörte, wurde gerade ein neuer Link gesendet. Bitte prüfen Sie Ihren Posteingang.", + "return_link": "Zurück zu OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", + "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie ihn, indem Sie auf den folgenden Link klicken:\n{{link}}\n\nDer Link kann nur einmal verwendet werden und läuft in {{ttl_hours}} Stunden ab.\nFalls Sie diese Einladung nicht erwartet haben, können Sie diese Nachricht ignorieren.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", - "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie OxiCloud, um Ihre neue Freigabe zu sehen:\n{{login_link}}\n\nMöglicherweise gibt es weitere neue Freigaben von {{inviter}} — melden Sie sich an, um alle Ihre freigegebenen Elemente zu sehen.\n\n— OxiCloud\n\nSie erhalten diese Nachricht, weil Sie ein OxiCloud-Konto haben und die Benachrichtigung über Freigaben aktiviert ist. Sie können sie in Ihrem Profil deaktivieren (Per E-Mail benachrichtigen, wenn jemand mit mir teilt)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalistisches Cloud-Speichersystem" - }, - "nav": { - "files": "Dateien", - "shared": "Freigaben", - "recent": "Zuletzt verwendet", - "favorites": "Favoriten", - "photos": "Fotos", - "music": "Musik", - "trash": "Papierkorb", - "sharedwithme": "Mit mir geteilt" - }, - "photos": { - "empty_state": "Noch keine Fotos", - "empty_hint": "Laden Sie Bilder oder Videos hoch, um sie hier zu sehen", - "items_selected": "ausgewählt", - "view_daily": "Tag", - "view_monthly": "Monat", - "view_yearly": "Jahr" - }, - "music": { - "create_playlist": "Playlist erstellen", - "playlists": "Playlists", - "no_playlists": "Noch keine Playlists", - "select_playlist": "Playlist auswählen", - "select_hint": "Wählen Sie eine Playlist aus der Seitenleiste oder erstellen Sie eine neue", - "add_tracks": "Titel hinzufügen", - "no_tracks": "Keine Titel in dieser Playlist", - "unknown_artist": "Unbekannter Künstler", - "unknown_title": "Unbekannt", - "confirm_delete": "Diese Playlist löschen?", - "playlist_name": "Playlist-Name", - "create": "Erstellen", - "delete": "Löschen", - "share": "Teilen", - "edit": "Bearbeiten", - "play_all": "Alle abspielen", - "shuffle": "Zufällig", - "repeat": "Wiederholen", - "repeat_one": "Einen wiederholen", - "queue": "Warteschlange", - "queue_empty": "Warteschlange ist leer", - "not_playing": "Nicht abspielend", - "play": "Abspielen", - "pause": "Pause", - "previous": "Zurück", - "next": "Weiter", - "volume": "Lautstärke", - "mute": "Stumm", - "unmute": "Ton ein", - "title": "Titel", - "artist": "Künstler", - "album": "Album", - "tracks": "Titel", - "add": "Hinzufügen", - "added": "Hinzugefügt!", - "added_to_playlist": "zur Playlist hinzugefügt", - "add_to_playlist": "Zur Playlist hinzufügen", - "load_error": "Fehler beim Laden der Playlists", - "add_error": "Tracks konnten nicht hinzugefügt werden", - "no_playlists_yet": "Noch keine Playlists. Erstellen Sie zuerst eine!", - "selected_files": "Ausgewählt:", - "error": "Fehler", - "search_audio": "Audiodateien suchen…", - "no_audio_files": "Keine Audiodateien gefunden", - "selected": "ausgewählt", - "loading": "Wird geladen…", - "search_error": "Audiodateien konnten nicht geladen werden", - "adding": "Wird hinzugefügt…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Dateien suchen...", - "new_folder": "Neuer Ordner", - "upload": "Hochladen", - "upload_files": "Dateien hochladen", - "upload_folder": "Ordner hochladen", - "upload.uploading": "Wird hochgeladen...", - "upload.complete": "{count} / {total} hochgeladen", - "upload.files": "Dateien", - "rename": "Umbenennen", - "move": "Verschieben nach...", - "move_to": "Verschieben nach", - "delete": "Löschen", - "download": "Herunterladen", - "view": "Anzeigen", - "cancel": "Abbrechen", - "confirm": "Bestätigen", - "share": "Teilen", - "favorite": "Zu Favoriten hinzufügen", - "unfavorite": "Aus Favoriten entfernen", - "copy": "Kopieren", - "notify": "Benachrichtigen", - "send": "Senden", - "clear_recent": "Zuletzt verwendete löschen", - "logout": "Abmelden", - "create": "Erstellen", - "search_btn": "Suchen", - "close": "Schließen", - "delete_permanently": "Endgültig löschen", - "empty_trash": "Papierkorb leeren", - "open_parent_folder": "Zum übergeordneten Ordner", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Erscheinungsbild", - "about": "Über OxiCloud", - "about_description": "Cloud-Speicherplattform mit Rust und Clean Architecture. Schnell, sicher und privat.", - "admin_panel": "Admin-Panel", - "profile": "Mein Profil", - "role_user": "Benutzer", - "theme": { - "light": "Hell", - "dark": "Dunkel", - "auto": "Wie System" + "login": { + "subject": "Anmeldung bei OxiCloud", + "body": "Hallo,\n\nVerwenden Sie den Link unten, um sich bei OxiCloud anzumelden. Der Link kann nur einmal verwendet werden und läuft in {{ttl_minutes}} Minuten ab. Öffnen Sie ihn auf demselben Gerät, von dem aus Sie ihn angefordert haben.\n\n{{link}}\n\nFalls Sie diesen Anmeldelink nicht angefordert haben, können Sie diese Nachricht ignorieren — es ist keine weitere Aktion erforderlich.\n\n— OxiCloud" }, - "manage_groups": "Gruppen verwalten" + "kind_file": "Datei", + "kind_folder": "Ordner", + "english_fallback_divider": "--- Englische Version unten ---" + } }, - "share": { - "dialogTitle": "Link teilen", - "linkLabel": "Geteilter Link:", - "copyLink": "Kopieren", - "permissions": "Berechtigungen:", - "permissionRead": "Lesen", - "permissionWrite": "Schreiben", - "permissionReshare": "Weiterteilen", - "password": "Passwortschutz:", - "generatePassword": "Generieren", - "expiration": "Ablaufdatum:", - "update": "Freigabe aktualisieren", - "remove": "Freigabe entfernen", - "notifyTitle": "Benachrichtigung senden", - "notifyEmailLabel": "E-Mail-Adresse:", - "notifyMessageLabel": "Nachricht (optional):", - "notifySend": "Benachrichtigung senden", - "shareWithOthers": "Mit anderen teilen", - "sharePublicly": "Öffentlich teilen", - "shareSettings": "Freigabeeinstellungen", - "shareCopied": "Link in Zwischenablage kopiert", - "shareCreated": "Freigabelink erfolgreich erstellt", - "shareUpdated": "Freigabeeinstellungen aktualisiert", - "shareRemoved": "Freigabe erfolgreich entfernt", - "inviteByEmail": "Per E-Mail einladen — Einladung wird gesendet", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link teilen", - "share_linkLabel": "Geteilter Link:", - "share_copyLink": "Kopieren", - "share_permissions": "Berechtigungen:", - "share_permissionRead": "Lesen", - "share_permissionWrite": "Schreiben", - "share_permissionReshare": "Weiterteilen", - "share_password": "Passwortschutz:", - "share_generatePassword": "Generieren", - "share_expiration": "Ablaufdatum:", - "share_update": "Freigabe aktualisieren", - "share_remove": "Freigabe entfernen", - "share_notifyTitle": "Benachrichtigung senden", - "share_notifyEmailLabel": "E-Mail-Adresse:", - "share_notifyMessageLabel": "Nachricht (optional):", - "share_notifySend": "Benachrichtigung senden", - "shared": { - "backToFiles": "Zurück zu Dateien", - "pageTitle": "Geteilte Ressourcen", - "pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", - "filterType": "Typ:", - "filterAll": "Alle", - "filterFiles": "Dateien", - "filterFolders": "Ordner", - "sortBy": "Sortieren nach:", - "sortByName": "Name", - "sortByDate": "Freigabedatum", - "sortByExpiration": "Ablaufdatum", - "search": "Suchen", - "colName": "Name", - "colType": "Typ", - "colDateShared": "Freigabedatum", - "colExpiration": "Ablaufdatum", - "colPermissions": "Berechtigungen", - "colPassword": "Passwort", - "colActions": "Aktionen", - "emptyStateTitle": "Noch keine geteilten Ressourcen", - "emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", - "goToFiles": "Zu Dateien gehen", - "typeFile": "Datei", - "typeFolder": "Ordner", - "noExpiration": "Kein Ablaufdatum", - "hasPassword": "Ja", - "noPassword": "Nein", - "editShare": "Freigabe bearbeiten", - "notifyShare": "Jemanden benachrichtigen", - "copyLink": "Link kopieren", - "removeShare": "Freigabe entfernen", - "linkCopied": "Link in Zwischenablage kopiert!", - "linkCopyFailed": "Link konnte nicht kopiert werden", - "itemUpdated": "Freigabeeinstellungen aktualisiert", - "itemRemoved": "Freigabe erfolgreich entfernt", - "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", - "notificationSent": "Benachrichtigung erfolgreich gesendet", - "notificationFailed": "Benachrichtigung konnte nicht gesendet werden", - "shared_backToFiles": "Zurück zu Dateien", - "shared_pageTitle": "Geteilte Ressourcen", - "shared_pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", - "shared_filterType": "Typ:", - "shared_filterAll": "Alle", - "shared_filterFiles": "Dateien", - "shared_filterFolders": "Ordner", - "shared_sortBy": "Sortieren nach:", - "shared_sortByName": "Name", - "shared_sortByDate": "Freigabedatum", - "shared_sortByExpiration": "Ablaufdatum", - "shared_search": "Suchen", - "shared_colName": "Name", - "shared_colType": "Typ", - "shared_colDateShared": "Freigabedatum", - "shared_colExpiration": "Ablaufdatum", - "shared_colPermissions": "Berechtigungen", - "shared_colPassword": "Passwort", - "shared_colActions": "Aktionen", - "shared_emptyStateTitle": "Noch keine geteilten Ressourcen", - "shared_emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", - "shared_goToFiles": "Zu Dateien gehen", - "shared_typeFile": "Datei", - "shared_typeFolder": "Ordner", - "shared_noExpiration": "Kein Ablaufdatum", - "shared_hasPassword": "Ja", - "shared_noPassword": "Nein", - "shared_editShare": "Freigabe bearbeiten", - "shared_notifyShare": "Jemanden benachrichtigen", - "shared_copyLink": "Link kopieren", - "shared_removeShare": "Freigabe entfernen", - "shared_linkCopied": "Link in Zwischenablage kopiert!", - "shared_linkCopyFailed": "Link konnte nicht kopiert werden", - "shared_itemUpdated": "Freigabeeinstellungen aktualisiert", - "shared_itemRemoved": "Freigabe erfolgreich entfernt", - "shared_invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", - "shared_notificationSent": "Benachrichtigung erfolgreich gesendet", - "shared_notificationFailed": "Benachrichtigung konnte nicht gesendet werden" - }, - "files": { - "name": "Name", - "type": "Typ", - "size": "Größe", - "modified": "Geändert", - "no_files": "Keine Dateien in diesem Ordner", - "empty_hint": "Laden Sie Dateien hoch oder erstellen Sie Ordner, um loszulegen", - "loading": "Dateien werden geladen…", - "view_grid": "Rasteransicht", - "view_list": "Listenansicht", - "file_types": { - "document": "Dokument", - "image": "Bild", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Text", - "folder": "Ordner", - "spreadsheet": "Tabelle", - "presentation": "Präsentation", - "archive": "Archiv", - "installer": "Installationsdatei", - "code": "Code" - }, - "owner": "Eigentümer" - }, - "dialogs": { - "rename_folder": "Ordner umbenennen", - "rename_file": "Datei umbenennen", - "new_name": "Neuer Name", - "new_folder_title": "Neuer Ordner", - "folder_name": "Ordnername", - "folder_placeholder": "Mein Ordner", - "rename_title": "Umbenennen", - "move_file": "Datei verschieben", - "move_folder": "Ordner verschieben", - "select_destination": "Zielordner auswählen:", - "root": "Stammverzeichnis", - "delete_confirmation": "Sind Sie sicher, dass Sie löschen möchten", - "and_contents": "und den gesamten Inhalt", - "no_undo": "Diese Aktion kann nicht rückgängig gemacht werden", - "confirm_title": "Aktion bestätigen", - "confirm_delete": "In Papierkorb verschieben", - "confirm_delete_file": "Sind Sie sicher, dass Sie die Datei \"{{name}}\" in den Papierkorb verschieben möchten?", - "confirm_delete_folder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und seinen gesamten Inhalt in den Papierkorb verschieben möchten?", - "confirm_permanent_delete": "Endgültig löschen", - "confirm_permanent_delete_msg": "Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", - "confirm_empty_trash": "Papierkorb leeren", - "confirm_delete_share": "Freigabelink löschen", - "confirm_delete_share_msg": "Sind Sie sicher, dass Sie diesen Freigabelink löschen möchten?", - "share_file": "Datei teilen", - "share_folder": "Ordner teilen", - "existing_shares": "Bestehende Freigaben", - "share_options": "Freigabeoptionen", - "password": "Passwort", - "expiration": "Ablaufdatum", - "permissions": "Berechtigungen", - "generated_link": "Generierter Link", - "notify": "Benachrichtigung senden", - "recipient": "Empfänger", - "message": "Nachricht", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "In den Home-Ordner verschieben" - }, - "dropzone": { - "drag_files": "Dateien hierher ziehen oder klicken zum Auswählen", - "drop_files": "Dateien zum Hochladen ablegen" - }, - "permissions": { - "read": "Lesen", - "write": "Schreiben", - "reshare": "Weiterteilen" - }, - "errors": { - "file_not_found": "Datei nicht gefunden", - "folder_not_found": "Ordner nicht gefunden", - "delete_error": "Fehler beim Löschen", - "upload_error": "Fehler beim Hochladen", - "rename_error": "Fehler beim Umbenennen", - "move_error": "Fehler beim Verschieben", - "empty_name": "Der Name darf nicht leer sein", - "name_exists": "Eine Datei oder ein Ordner mit diesem Namen existiert bereits", - "generic_error": "Ein Fehler ist aufgetreten", - "group_name_invalid": "Der Gruppenname muss dem E-Mail-Präfix-Format entsprechen (Buchstaben, Ziffern, Punkt, Bindestrich, Unterstrich; 1–64 Zeichen).", - "group_cycle": "Dieses Mitglied würde einen Gruppen-Zirkelbezug erzeugen.", - "group_depth_exceeded": "Die Verschachtelungstiefe überschreitet das zulässige Maximum (8).", - "group_virtual_immutable": "Die Gruppe „Internal“ wird vom System verwaltet und kann nicht geändert werden.", - "group_not_found": "Gruppe nicht gefunden.", - "group_name_taken": "Eine Gruppe mit diesem Namen existiert bereits." - }, - "breadcrumb": { - "home": "Startseite" - }, - "trash": { - "empty_trash": "Papierkorb leeren", - "empty_state": "Der Papierkorb ist leer", - "original_location": "Ursprünglicher Speicherort", - "deleted_date": "Löschdatum", - "remaining": "Verbleibend", - "actions": "Aktionen", - "restore": "Wiederherstellen", - "delete_permanently": "Endgültig löschen", - "empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht.", - "groupby": { - "remaining_days": "Verbleibende Tage", - "trashed_time": "Löschzeit" - } - }, - "daysRemaining": { - "expired": "Abgelaufen", - "today": "Heute", - "tomorrow": "Morgen", - "inDays": "{{count}} Tage" - }, - "expiryChip": { - "never": "Läuft nie ab", - "expired": "Abgelaufen", - "today": "Läuft heute ab", - "tomorrow": "Läuft morgen ab", - "inDays": "Läuft in {{count}} Tagen ab", - "onDate": "Läuft am {{date}} ab" - }, - "auth": { - "login_title": "Anmelden", - "username": "Benutzername", - "username_placeholder": "Geben Sie Ihren Benutzernamen ein", - "login_identifier": "Benutzername oder E-Mail", - "login_identifier_placeholder": "Geben Sie Ihren Benutzernamen oder Ihre E-Mail-Adresse ein", - "password": "Passwort", - "password_placeholder": "Geben Sie Ihr Passwort ein", - "login_button": "Anmelden", - "no_account": "Kein Konto?", - "register": "Registrieren", - "admin_setup": "Erstmalig?", - "setup": "Administrator einrichten", - "register_title": "Konto erstellen", - "email": "E-Mail", - "email_placeholder": "Geben Sie Ihre E-Mail ein", - "confirm_password": "Passwort bestätigen", - "confirm_password_placeholder": "Bestätigen Sie Ihr Passwort", - "register_button": "Konto erstellen", - "have_account": "Bereits ein Konto?", - "login": "Anmelden", - "setup_title": "Ersteinrichtung", - "setup_step1": "Admin", - "setup_step2": "System", - "setup_step3": "Abgeschlossen", - "admin_username": "Admin-Benutzername", - "admin_email": "Admin-E-Mail", - "admin_password": "Admin-Passwort", - "create_admin": "Administrator erstellen", - "back_to_login": "Bereits eingerichtet?", - "admin_success": "Administratorkonto erfolgreich erstellt! Sie können sich jetzt anmelden.", - "account_success": "Konto erfolgreich erstellt! Sie können sich jetzt anmelden.", - "passwords_mismatch": "Die Passwörter stimmen nicht überein", - "admin_create_error": "Fehler beim Erstellen des Administratorkontos", - "or": "oder", - "sso_login": "Mit SSO anmelden", - "sso_login_provider": "Mit {{provider}} anmelden", - "magicLinkHint": "Kein Passwort? Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen einmaligen Anmeldelink.", - "magicLinkEmailLabel": "E-Mail-Adresse", - "magicLinkEmailPlaceholder": "sie@beispiel.de", - "magicLinkSubmit": "Anmeldelink senden", - "magicLinkSent": "Wenn für diese E-Mail-Adresse ein Konto besteht, wurde ein Anmeldelink gesendet. Überprüfen Sie Ihren Posteingang.", - "magicLinkUnavailable": "Die Anmeldung per E-Mail ist auf diesem Server nicht verfügbar.", - "magicLinkNetworkError": "Server nicht erreichbar: {{message}}", - "magicLinkToggle": "Kein Passwort? Anmeldelink per E-Mail", - "passwordsMatch": "Passwörter stimmen überein", - "capsLock": "Feststelltaste aktiv" - }, - "storage": { - "title": "Speicher", - "calculating": "Berechnung...", - "used": "{{percentage}}% verwendet ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Dieser Dateityp kann nicht in der Vorschau angezeigt werden.", - "download_file": "Datei herunterladen", - "zoom_in": "Vergrößern", - "zoom_out": "Verkleinern", - "zoom_reset": "Zoom zurücksetzen" - }, - "language_selector": { - "title": "Willkommen!", - "subtitle": "Wählen Sie Ihre Sprache, um fortzufahren", - "continue": "Weiter", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Noch keine Favoriten", - "empty_hint": "Markieren Sie Dateien oder Ordner mit einem Stern, um sie zu Ihren Favoriten hinzuzufügen", - "add": "Zu Favoriten hinzufügen", - "remove": "Aus Favoriten entfernen", - "added_title": "Zu Favoriten hinzugefügt", - "added_msg": "zu Favoriten hinzugefügt", - "removed_title": "Aus Favoriten entfernt", - "removed_msg": "aus Favoriten entfernt" - }, - "recent": { - "title": "Zuletzt verwendet", - "clear": "Zuletzt verwendete löschen", - "accessed": "Zugegriffen", - "empty_state": "Keine zuletzt verwendeten Dateien", - "empty_hint": "Dateien, die Sie öffnen, werden hier angezeigt", - "loadMore": "Mehr laden" - }, - "notifications": { - "file_renamed": "Datei umbenannt", - "file_renamed_to": "Datei umbenannt in \"{{name}}\"", - "folder_renamed": "Ordner umbenannt", - "folder_renamed_to": "Ordner umbenannt in \"{{name}}\"", - "file_uploaded": "Datei hochgeladen", - "file_deleted": "Datei in Papierkorb verschoben", - "folder_deleted": "Ordner in Papierkorb verschoben", - "item_deleted_permanently": "Element endgültig gelöscht", - "trash_emptied": "Papierkorb erfolgreich geleert", - "title": "Benachrichtigungen", - "empty": "Keine Benachrichtigungen", - "link_created": "Link erstellt", - "share_success": "Freigabelink erfolgreich erstellt", - "upload_files_section_title": "Upload hier nicht verfügbar", - "upload_files_section_body": "Wechseln Sie zum Abschnitt Dateien, um Dateien hochzuladen" - }, - "batch": { - "one_selected": "1 Element ausgewählt", - "n_selected": "{{count}} Elemente ausgewählt", - "confirm_delete": "Möchten Sie wirklich {{count}} Elemente in den Papierkorb verschieben?", - "move_title": "{{count}} Element(e) verschieben", - "add_favorites": "Zu Favoriten hinzufügen", - "move_copy": "Verschieben oder kopieren" - }, - "admin": { - "page_title": "Admin-Panel", - "back_to_app": "Zurück zu OxiCloud", - "loading": "Laden…", - "access_denied": "Zugriff verweigert", - "access_denied_desc": "Administratorrechte erforderlich.", - "sign_in": "Anmelden", - "tab_dashboard": "Dashboard", - "tab_users": "Benutzer", - "tab_oidc": "SSO / OIDC", - "total_users": "Benutzer gesamt", - "active_users": "Aktive Benutzer", - "admins": "Admins", - "version": "Version", - "storage_overview": "Speicherübersicht", - "used": "Verwendet", - "total_quota": "Gesamtkontingent", - "usage_pct": "Nutzung %", - "users_over_80": "Benutzer >80% Kontingent", - "users_over_quota": "Benutzer über Kontingent", - "system": "System", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Kontingente", - "enabled": "Aktiviert", - "disabled": "Deaktiviert", - "active": "Aktiv", - "off": "Aus", - "allow_registration": "Öffentliche Selbstregistrierung erlauben", - "registration_warning": "Öffentliche Registrierung ist deaktiviert. Nur Admins können neue Benutzer erstellen.", - "user_management": "Benutzerverwaltung", - "create_user": "Benutzer erstellen", - "col_user": "Benutzer", - "col_role": "Rolle", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Speicher", - "col_last_login": "Letzter Login", - "col_actions": "Aktionen", - "loading_users": "Benutzer werden geladen…", - "failed_load_users": "Laden fehlgeschlagen", - "no_users_found": "Keine Benutzer gefunden", - "showing_users": "Zeige {{from}}-{{to}} von {{total}}", - "prev": "Zurück", - "next": "Weiter", - "inactive": "Inaktiv", - "you_badge": "(du)", - "local": "Lokal", - "never": "Nie", - "just_now": "Gerade eben", - "minutes_ago": "vor {{n}}Min", - "hours_ago": "vor {{n}}Std", - "days_ago": "vor {{n}}T", - "edit_quota_title": "Kontingent bearbeiten", - "reset_password_title": "Passwort zurücksetzen", - "toggle_role_title": "Rolle wechseln", - "deactivate_title": "Deaktivieren", - "activate_title": "Aktivieren", - "delete_title": "Löschen", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "SSO-Authentifizierung aktivieren", - "provider_name": "Anbietername", - "issuer_url": "Aussteller-URL", - "issuer_url_hint": "OpenID Connect Aussteller-URL Ihres Identitätsanbieters", - "auto_discover": "Auto-Erkennung", - "discovering": "Erkennung…", - "client_id": "Client-ID", - "client_secret": "Client-Secret", - "client_secret_placeholder": "Leer lassen für aktuellen Wert", - "secret_configured": "Ein Client-Secret ist bereits konfiguriert", - "callback_url": "Callback-URL", - "callback_url_hint": "(bei IdP registrieren)", - "advanced_settings": "Erweiterte Einstellungen", - "scopes": "Scopes", - "auto_provision": "Benutzer bei erstem Login automatisch anlegen", - "admin_groups": "Admin-Gruppen", - "admin_groups_hint": "Kommagetrennte OIDC-Gruppennamen für Admin-Rolle", - "disable_password": "Passwort-Login deaktivieren (nur OIDC)", - "password_warning": "Dies verhindert ALLE passwortbasierten Anmeldungen!", - "test_btn": "Testen", - "save_btn": "Speichern", - "saving": "Speichern…", - "settings_saved": "Einstellungen gespeichert — OIDC ist jetzt {{status}}", - "quota_modal_title": "Speicherkontingent aktualisieren", - "quota_user_label": "Benutzer:", - "new_quota": "Neues Kontingent", - "quota_unlimited_hint": "0 für unbegrenzt", - "cancel": "Abbrechen", - "create_user_title": "Neuen Benutzer erstellen", - "username_label": "Benutzername", - "username_placeholder": "maxmuster", - "username_hint": "3–32 Zeichen", - "password_label": "Passwort", - "password_placeholder": "Min. 8 Zeichen", - "email_label": "E-Mail", - "email_optional": "(optional)", - "email_placeholder": "benutzer@beispiel.de (automatisch wenn leer)", - "role_label": "Rolle", - "role_user": "Benutzer", - "role_admin": "Admin", - "quota_label": "Kontingent", - "creating": "Erstellen…", - "reset_pw_title": "Passwort zurücksetzen", - "new_password_label": "Neues Passwort", - "resetting": "Zurücksetzen…", - "reset_btn": "Zurücksetzen", - "confirm_role_change": "Rolle zu {{role}} ändern?", - "confirm_deactivate": "Diesen Benutzer wirklich deaktivieren?", - "confirm_activate": "Diesen Benutzer wirklich aktivieren?", - "confirm_delete_user": "Benutzer \"{{name}}\" LÖSCHEN? Kann nicht rückgängig gemacht werden!", - "confirm_action": "Aktion bestätigen", - "confirm_yes": "Bestätigen", - "confirm_no": "Abbrechen", - "error_username_short": "Benutzername muss mindestens 3 Zeichen haben", - "error_password_short": "Passwort muss mindestens 8 Zeichen haben", - "error_generic": "Fehlgeschlagen", - "error_network": "Netzwerkfehler: {{message}}", - "error_create_user": "Benutzer erstellen fehlgeschlagen", - "tab_storage": "Speicher", - "storage_title": "Speicherkonfiguration", - "storage_current_backend": "Aktuelles Backend", - "storage_total_blobs": "Gesamt-Blobs", - "storage_total_size": "Gesamtgröße", - "storage_dedup_ratio": "Deduplizierungsrate", - "storage_backend": "Backend", - "storage_local": "Lokal", - "storage_s3": "S3-kompatibel", - "storage_provider_preset": "Anbieter-Voreinstellung", - "storage_preset_custom": "Benutzerdefiniert", - "storage_endpoint_url": "Endpunkt-URL", - "storage_endpoint_hint": "Leer lassen für AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Region", - "storage_access_key": "Zugriffsschlüssel", - "storage_secret_key": "Geheimschlüssel", - "storage_secret_configured": "Schlüssel konfiguriert", - "storage_key_placeholder": "Neuen Schlüssel eingeben", - "storage_path_style": "Pfadstil erzwingen", - "storage_path_style_hint": "Erforderlich für MinIO und einige S3-kompatible Dienste", - "storage_test_connection": "Verbindung testen", - "storage_test_success": "Verbindung erfolgreich", - "storage_test_failure": "Verbindung fehlgeschlagen", - "storage_save": "Konfiguration speichern", - "storage_saved": "Konfiguration gespeichert", - "storage_migration": "Datenmigration", - "storage_migration_coming_soon": "Migrationstools demnächst verfügbar", - "migration_status_label": "Migrationsstatus", - "migration_start": "Migration starten", - "migration_pause": "Pausieren", - "migration_resume": "Fortsetzen", - "migration_verify": "Verifizieren", - "migration_complete": "Abschließen", - "migration_started": "Migration gestartet", - "migration_paused_msg": "Migration pausiert", - "migration_resumed_msg": "Migration fortgesetzt", - "migration_completed_msg": "Migration erfolgreich abgeschlossen", - "migration_verifying": "Wird verifiziert...", - "migration_verify_passed": "Verifizierung erfolgreich", - "migration_verify_failed": "Verifizierung fehlgeschlagen", - "migration_failed_blobs": "Fehlgeschlagene Blobs", - "testing": "Wird getestet...", - "smtp_disabled": "Deaktiviert (Host nicht gesetzt)", - "smtp_enabled": "Aktiviert", - "smtp_enabled_label": "Status", - "smtp_intro": "SMTP wird ausschließlich über Umgebungsvariablen (OXICLOUD_SMTP_*) konfiguriert. Die folgenden Werte werden aus dem laufenden Server gelesen — zum Ändern bearbeiten Sie die Umgebung und starten OxiCloud neu.", - "smtp_not_configured": "SMTP ist auf diesem Server nicht konfiguriert.", - "smtp_send_failed": "Senden fehlgeschlagen.", - "smtp_send_test": "Test-E-Mail senden", - "smtp_sending": "Senden …", - "smtp_sent": "Test-E-Mail gesendet.", - "smtp_server_code": "Server antwortete", - "smtp_test_intro": "Sendet eine fest einprogrammierte Diagnosenachricht an den unten angegebenen Empfänger und meldet die Antwort des SMTP-Servers, sodass Sie sie mit Ihren Relay-Protokollen abgleichen können.", - "smtp_test_missing_to": "Geben Sie eine Empfängeradresse ein.", - "smtp_test_title": "Test-E-Mail senden", - "smtp_test_to": "Empfängeradresse", - "smtp_title": "Ausgehende E-Mail (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profil", - "back_to_app": "Zurück zu OxiCloud", - "loading": "Laden…", - "not_authenticated": "Nicht authentifiziert", - "not_authenticated_desc": "Bitte melden Sie sich an, um Ihr Profil anzuzeigen.", - "sign_in": "Anmelden", - "role_admin": "Administrator", - "role_user": "Benutzer", - "account_details": "Kontodetails", - "username": "Benutzername", - "email": "E-Mail", - "role": "Rolle", - "last_login": "Letzter Login", - "storage": "Speicher", - "used": "Verwendet", - "quota": "Kontingent", - "usage": "Nutzung", - "unlimited": "Unbegrenzt", - "app_passwords": "App-Passwörter", - "app_pw_desc": "Passwörter für WebDAV-, CalDAV- und CardDAV-Clients generieren. Jedes Passwort wird nur einmal angezeigt.", - "app_pw_label_placeholder": "Bezeichnung (z.B. Thunderbird, macOS)", - "generate": "Generieren", - "generating": "Generieren…", - "new_password_for": "Neues Passwort für", - "copy_warning": "Kopieren Sie dieses Passwort jetzt. Sie können es nicht erneut anzeigen.", - "copy_to_clipboard": "In Zwischenablage kopieren", - "col_label": "Bezeichnung", - "col_created": "Erstellt", - "col_last_used": "Zuletzt verwendet", - "col_status": "Status", - "active": "Aktiv", - "revoked": "Widerrufen", - "revoke_title": "Widerrufen", - "no_app_passwords": "Noch keine App-Passwörter.", - "client_sessions": "Client-Sitzungen", - "client_sessions_desc": "Automatisch generiert beim Verbinden eines Nextcloud-kompatiblen Clients.", - "col_client": "Client", - "never": "Nie", - "just_now": "Gerade eben", - "minutes_ago": "vor {{n}} Min", - "hours_ago": "vor {{n}} Std", - "days_ago": "vor {{n}} Tagen", - "edit_profile": "Profil bearbeiten", - "edit_oidc_managed": "Um Ihre Informationen (Name, Vorname, Profilbild, …) zu ändern, aktualisieren Sie sie bitte bei Ihrem Identity-Provider. Ihre Änderungen erscheinen bei der nächsten Anmeldung.", - "username_claim_hint": "2–64 Zeichen, Buchstaben / Ziffern / Punkt / Bindestrich / Unterstrich. Nach der Wahl kann der Benutzername nicht mehr geändert werden (DAV/NextCloud-Clients hängen davon ab).", - "username_already_claimed": "Benutzername ist gesetzt und kann nicht geändert werden (DAV/NextCloud-Clients hängen davon ab).", - "given_name": "Vorname", - "family_name": "Nachname", - "notify_on_share": "Mich per E-Mail benachrichtigen, wenn jemand mit mir teilt", - "notify_on_share_hint": "Wenn deaktiviert, werden Freigaben weiterhin in deinem Konto angezeigt — du erhältst nur keine E-Mail dazu.", - "save_profile": "Änderungen speichern", - "profile_saved": "Profil aktualisiert", - "profile_no_changes": "Keine Änderungen zu speichern.", - "profile_save_failed": "Speichern fehlgeschlagen", - "username_taken_error": "Dieser Benutzername ist bereits vergeben.", - "username_immutable_error": "Ihr Benutzername ist bereits gesetzt und kann hier nicht geändert werden. Wenden Sie sich an einen Administrator, wenn Sie umbenennen möchten.", - "change_password": "Passwort ändern", - "current_password": "Aktuelles Passwort", - "new_password": "Neues Passwort", - "min_8_chars": "Mindestens 8 Zeichen", - "confirm_password": "Neues Passwort bestätigen", - "update_password": "Passwort aktualisieren", - "updating": "Aktualisierung…", - "password_updated": "Passwort erfolgreich aktualisiert", - "passwords_no_match": "Passwörter stimmen nicht überein", - "password_too_short": "Passwort muss mindestens 8 Zeichen haben", - "password_change_failed": "Passwort ändern fehlgeschlagen", - "error_network": "Netzwerkfehler: {{message}}", - "error_label_required": "Bitte Bezeichnung eingeben", - "error_create_pw": "App-Passwort erstellen fehlgeschlagen", - "confirm_revoke": "App-Passwort \"{{label}}\" widerrufen? Clients werden nicht mehr funktionieren.", - "error_revoke": "Widerrufen fehlgeschlagen", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Wird hochgeladen...", - "files": "Dateien", - "complete": "{{count}} / {{total}} hochgeladen" - }, - "storage_quota_exceeded": "Speicherplatz erschöpft", - "sharedwithme": { - "pageTitle": "Mit mir geteilt", - "pageDescription": "Dateien und Ordner, die andere Benutzer mit Ihnen geteilt haben", - "emptyStateTitle": "Noch nichts mit Ihnen geteilt", - "emptyStateDesc": "Elemente, die andere Benutzer mit Ihnen teilen, erscheinen hier", - "loadMore": "Mehr laden", - "sharedBy": "Geteilt von", - "colName": "Name", - "colType": "Typ", - "colSharedBy": "Geteilt von", - "colDate": "Datum der Freigabe", - "colPermissions": "Berechtigungen" - }, - "groupby": { - "none": "Keine", - "title": "Gruppieren nach", - "owner": "Eigentümer", - "shareDate": "Freigabedatum", - "type": "Typ", - "type.folders": "Ordner", - "accessedAt": "Zugriffsdatum", - "modifiedAt": "Änderungsdatum", - "createdAt": "Erstellungsdatum", - "size": "Größe", - "favoriteDate": "Datum der Markierung", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Neu" - }, - "dateBucket": { - "today": "Heute", - "last7days": "Letzte 7 Tage", - "last30days": "Letzte 30 Tage" - }, - "groups": { - "title": "Gruppen verwalten", - "create_button": "Gruppe erstellen", - "create_dialog_title": "Neue Gruppe", - "edit_dialog_title": "Gruppe umbenennen", - "name_label": "Name", - "name_placeholder": "engineering", - "description_label": "Beschreibung (optional)", - "members_section": "Mitglieder", - "add_member_placeholder": "Benutzer oder Gruppe hinzufügen…", - "no_members": "Noch keine Mitglieder.", - "remove_member": "Entfernen", - "delete_group": "Gruppe löschen", - "delete_confirm": "Die Gruppe „{name}\" löschen? Auf diese Gruppe verweisende Berechtigungen werden widerrufen.", - "empty_state": "Noch keine Gruppen.", - "load_more": "Mehr laden", - "back_to_list": "Zurück", - "loading": "Wird geladen…", - "virtual_badge": "System", - "member_count_zero": "Keine Mitglieder", - "member_count_one": "1 Mitglied", - "member_count_other": "{count} Mitglieder", - "delete_confirm_label": "Tippe den Gruppennamen zur Bestätigung ein:", - "delete_confirm_mismatch": "Tippe den Gruppennamen exakt zur Bestätigung ein.", - "virtual_internal_name": "Intern", - "members_loading": "Mitglieder werden geladen…", - "members_empty": "Keine Mitglieder", - "virtual_internal_explanation": "Jeder interne Benutzer auf diesem Server" - }, - "myshares": { - "copyLink": "Link kopieren", - "deleteLink": "Link löschen", - "notifyByEmail": "Per E-Mail benachrichtigen", - "notifyFailed": "Benachrichtigung konnte nicht gesendet werden.", - "notifyGroupMembers": "Gruppenmitglieder benachrichtigen", - "notifyRateLimited": "Zu viele Benachrichtigungen für diesen Empfänger — versuchen Sie es später erneut.", - "removeAccess": "Zugriff entfernen", - "resendInvitation": "Einladungs-E-Mail erneut senden" - }, - "sort": { - "asc": "aufsteigend", - "desc": "absteigend" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", + "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie OxiCloud, um Ihre neue Freigabe zu sehen:\n{{login_link}}\n\nMöglicherweise gibt es weitere neue Freigaben von {{inviter}} — melden Sie sich an, um alle Ihre freigegebenen Elemente zu sehen.\n\n— OxiCloud\n\nSie erhalten diese Nachricht, weil Sie ein OxiCloud-Konto haben und die Benachrichtigung über Freigaben aktiviert ist. Sie können sie in Ihrem Profil deaktivieren (Per E-Mail benachrichtigen, wenn jemand mit mir teilt)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalistisches Cloud-Speichersystem" + }, + "nav": { + "files": "Dateien", + "shared": "Freigaben", + "recent": "Zuletzt verwendet", + "favorites": "Favoriten", + "photos": "Fotos", + "music": "Musik", + "trash": "Papierkorb", + "sharedwithme": "Mit mir geteilt", + "profile": "Profil", + "shared_with_me": "Mit mir geteilt" + }, + "photos": { + "empty_state": "Noch keine Fotos", + "empty_hint": "Laden Sie Bilder oder Videos hoch, um sie hier zu sehen", + "items_selected": "ausgewählt", + "view_daily": "Tag", + "view_monthly": "Monat", + "view_yearly": "Jahr", + "group_by": "Gruppieren nach" + }, + "music": { + "create_playlist": "Playlist erstellen", + "playlists": "Playlists", + "no_playlists": "Noch keine Playlists", + "select_playlist": "Playlist auswählen", + "select_hint": "Wählen Sie eine Playlist aus der Seitenleiste oder erstellen Sie eine neue", + "add_tracks": "Titel hinzufügen", + "no_tracks": "Keine Titel in dieser Playlist", + "unknown_artist": "Unbekannter Künstler", + "unknown_title": "Unbekannt", + "confirm_delete": "Diese Playlist löschen?", + "playlist_name": "Playlist-Name", + "create": "Erstellen", + "delete": "Löschen", + "share": "Teilen", + "edit": "Bearbeiten", + "play_all": "Alle abspielen", + "shuffle": "Zufällig", + "repeat": "Wiederholen", + "repeat_one": "Einen wiederholen", + "queue": "Warteschlange", + "queue_empty": "Warteschlange ist leer", + "not_playing": "Nicht abspielend", + "play": "Abspielen", + "pause": "Pause", + "previous": "Zurück", + "next": "Weiter", + "volume": "Lautstärke", + "mute": "Stumm", + "unmute": "Ton ein", + "title": "Titel", + "artist": "Künstler", + "album": "Album", + "tracks": "Titel", + "add": "Hinzufügen", + "added": "Hinzugefügt!", + "added_to_playlist": "zur Playlist hinzugefügt", + "add_to_playlist": "Zur Playlist hinzufügen", + "load_error": "Fehler beim Laden der Playlists", + "add_error": "Tracks konnten nicht hinzugefügt werden", + "no_playlists_yet": "Noch keine Playlists. Erstellen Sie zuerst eine!", + "selected_files": "Ausgewählt:", + "error": "Fehler", + "search_audio": "Audiodateien suchen…", + "no_audio_files": "Keine Audiodateien gefunden", + "selected": "ausgewählt", + "loading": "Wird geladen…", + "search_error": "Audiodateien konnten nicht geladen werden", + "adding": "Wird hinzugefügt…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "Zurück" + }, + "actions": { + "search": "Dateien suchen...", + "new_folder": "Neuer Ordner", + "upload": "Hochladen", + "upload_files": "Dateien hochladen", + "upload_folder": "Ordner hochladen", + "upload.uploading": "Wird hochgeladen...", + "upload.complete": "{count} / {total} hochgeladen", + "upload.files": "Dateien", + "rename": "Umbenennen", + "move": "Verschieben nach...", + "move_to": "Verschieben nach", + "delete": "Löschen", + "download": "Herunterladen", + "view": "Anzeigen", + "cancel": "Abbrechen", + "confirm": "Bestätigen", + "share": "Teilen", + "favorite": "Zu Favoriten hinzufügen", + "unfavorite": "Aus Favoriten entfernen", + "copy": "Kopieren", + "notify": "Benachrichtigen", + "send": "Senden", + "clear_recent": "Zuletzt verwendete löschen", + "logout": "Abmelden", + "create": "Erstellen", + "search_btn": "Suchen", + "close": "Schließen", + "delete_permanently": "Endgültig löschen", + "empty_trash": "Papierkorb leeren", + "open_parent_folder": "Zum übergeordneten Ordner", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Erscheinungsbild", + "about": "Über OxiCloud", + "about_description": "Cloud-Speicherplattform mit Rust und Clean Architecture. Schnell, sicher und privat.", + "admin_panel": "Admin-Panel", + "profile": "Mein Profil", + "role_user": "Benutzer", + "theme": { + "light": "Hell", + "dark": "Dunkel", + "auto": "Wie System" + }, + "manage_groups": "Gruppen verwalten", + "admin": "Admin" + }, + "share": { + "dialogTitle": "Link teilen", + "linkLabel": "Geteilter Link:", + "copyLink": "Kopieren", + "permissions": "Berechtigungen:", + "permissionRead": "Lesen", + "permissionWrite": "Schreiben", + "permissionReshare": "Weiterteilen", + "password": "Passwortschutz:", + "generatePassword": "Generieren", + "expiration": "Ablaufdatum:", + "update": "Freigabe aktualisieren", + "remove": "Freigabe entfernen", + "notifyTitle": "Benachrichtigung senden", + "notifyEmailLabel": "E-Mail-Adresse:", + "notifyMessageLabel": "Nachricht (optional):", + "notifySend": "Benachrichtigung senden", + "shareWithOthers": "Mit anderen teilen", + "sharePublicly": "Öffentlich teilen", + "shareSettings": "Freigabeeinstellungen", + "shareCopied": "Link in Zwischenablage kopiert", + "shareCreated": "Freigabelink erfolgreich erstellt", + "shareUpdated": "Freigabeeinstellungen aktualisiert", + "shareRemoved": "Freigabe erfolgreich entfernt", + "inviteByEmail": "Per E-Mail einladen — Einladung wird gesendet", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Kopieren", + "copy_failed": "Could not copy link", + "download": "Herunterladen", + "files": "Dateien", + "folders": "Ordner", + "link_name": "Link name (optional)", + "notifyByEmail": "Per E-Mail benachrichtigen", + "revoke": "Remove", + "role_label": "Rolle" + }, + "share_dialogTitle": "Link teilen", + "share_linkLabel": "Geteilter Link:", + "share_copyLink": "Kopieren", + "share_permissions": "Berechtigungen:", + "share_permissionRead": "Lesen", + "share_permissionWrite": "Schreiben", + "share_permissionReshare": "Weiterteilen", + "share_password": "Passwortschutz:", + "share_generatePassword": "Generieren", + "share_expiration": "Ablaufdatum:", + "share_update": "Freigabe aktualisieren", + "share_remove": "Freigabe entfernen", + "share_notifyTitle": "Benachrichtigung senden", + "share_notifyEmailLabel": "E-Mail-Adresse:", + "share_notifyMessageLabel": "Nachricht (optional):", + "share_notifySend": "Benachrichtigung senden", + "shared": { + "backToFiles": "Zurück zu Dateien", + "pageTitle": "Geteilte Ressourcen", + "pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", + "filterType": "Typ:", + "filterAll": "Alle", + "filterFiles": "Dateien", + "filterFolders": "Ordner", + "sortBy": "Sortieren nach:", + "sortByName": "Name", + "sortByDate": "Freigabedatum", + "sortByExpiration": "Ablaufdatum", + "search": "Suchen", + "colName": "Name", + "colType": "Typ", + "colDateShared": "Freigabedatum", + "colExpiration": "Ablaufdatum", + "colPermissions": "Berechtigungen", + "colPassword": "Passwort", + "colActions": "Aktionen", + "emptyStateTitle": "Noch keine geteilten Ressourcen", + "emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", + "goToFiles": "Zu Dateien gehen", + "typeFile": "Datei", + "typeFolder": "Ordner", + "noExpiration": "Kein Ablaufdatum", + "hasPassword": "Ja", + "noPassword": "Nein", + "editShare": "Freigabe bearbeiten", + "notifyShare": "Jemanden benachrichtigen", + "copyLink": "Link kopieren", + "removeShare": "Freigabe entfernen", + "linkCopied": "Link in Zwischenablage kopiert!", + "linkCopyFailed": "Link konnte nicht kopiert werden", + "itemUpdated": "Freigabeeinstellungen aktualisiert", + "itemRemoved": "Freigabe erfolgreich entfernt", + "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", + "notificationSent": "Benachrichtigung erfolgreich gesendet", + "notificationFailed": "Benachrichtigung konnte nicht gesendet werden", + "shared_backToFiles": "Zurück zu Dateien", + "shared_pageTitle": "Geteilte Ressourcen", + "shared_pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", + "shared_filterType": "Typ:", + "shared_filterAll": "Alle", + "shared_filterFiles": "Dateien", + "shared_filterFolders": "Ordner", + "shared_sortBy": "Sortieren nach:", + "shared_sortByName": "Name", + "shared_sortByDate": "Freigabedatum", + "shared_sortByExpiration": "Ablaufdatum", + "shared_search": "Suchen", + "shared_colName": "Name", + "shared_colType": "Typ", + "shared_colDateShared": "Freigabedatum", + "shared_colExpiration": "Ablaufdatum", + "shared_colPermissions": "Berechtigungen", + "shared_colPassword": "Passwort", + "shared_colActions": "Aktionen", + "shared_emptyStateTitle": "Noch keine geteilten Ressourcen", + "shared_emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", + "shared_goToFiles": "Zu Dateien gehen", + "shared_typeFile": "Datei", + "shared_typeFolder": "Ordner", + "shared_noExpiration": "Kein Ablaufdatum", + "shared_hasPassword": "Ja", + "shared_noPassword": "Nein", + "shared_editShare": "Freigabe bearbeiten", + "shared_notifyShare": "Jemanden benachrichtigen", + "shared_copyLink": "Link kopieren", + "shared_removeShare": "Freigabe entfernen", + "shared_linkCopied": "Link in Zwischenablage kopiert!", + "shared_linkCopyFailed": "Link konnte nicht kopiert werden", + "shared_itemUpdated": "Freigabeeinstellungen aktualisiert", + "shared_itemRemoved": "Freigabe erfolgreich entfernt", + "shared_invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", + "shared_notificationSent": "Benachrichtigung erfolgreich gesendet", + "shared_notificationFailed": "Benachrichtigung konnte nicht gesendet werden" + }, + "files": { + "name": "Name", + "type": "Typ", + "size": "Größe", + "modified": "Geändert", + "no_files": "Keine Dateien in diesem Ordner", + "empty_hint": "Laden Sie Dateien hoch oder erstellen Sie Ordner, um loszulegen", + "loading": "Dateien werden geladen…", + "view_grid": "Rasteransicht", + "view_list": "Listenansicht", + "file_types": { + "document": "Dokument", + "image": "Bild", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Text", + "folder": "Ordner", + "spreadsheet": "Tabelle", + "presentation": "Präsentation", + "archive": "Archiv", + "installer": "Installationsdatei", + "code": "Code" + }, + "owner": "Eigentümer", + "add_favorites": "Zu Favoriten hinzufügen", + "added_favorites": "Zu Favoriten hinzugefügt", + "col_name": "Name", + "col_owner": "Eigentümer", + "col_size": "Größe", + "col_type": "Typ", + "copy": "Kopieren", + "edit": "Bearbeiten", + "file": "Datei", + "folder": "Ordner", + "new_folder": "Neuer Ordner", + "share": "Teilen", + "view": "Anzeigen" + }, + "dialogs": { + "rename_folder": "Ordner umbenennen", + "rename_file": "Datei umbenennen", + "new_name": "Neuer Name", + "new_folder_title": "Neuer Ordner", + "folder_name": "Ordnername", + "folder_placeholder": "Mein Ordner", + "rename_title": "Umbenennen", + "move_file": "Datei verschieben", + "move_folder": "Ordner verschieben", + "select_destination": "Zielordner auswählen:", + "root": "Stammverzeichnis", + "delete_confirmation": "Sind Sie sicher, dass Sie löschen möchten", + "and_contents": "und den gesamten Inhalt", + "no_undo": "Diese Aktion kann nicht rückgängig gemacht werden", + "confirm_title": "Aktion bestätigen", + "confirm_delete": "In Papierkorb verschieben", + "confirm_delete_file": "Sind Sie sicher, dass Sie die Datei \"{{name}}\" in den Papierkorb verschieben möchten?", + "confirm_delete_folder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und seinen gesamten Inhalt in den Papierkorb verschieben möchten?", + "confirm_permanent_delete": "Endgültig löschen", + "confirm_permanent_delete_msg": "Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm_empty_trash": "Papierkorb leeren", + "confirm_delete_share": "Freigabelink löschen", + "confirm_delete_share_msg": "Sind Sie sicher, dass Sie diesen Freigabelink löschen möchten?", + "share_file": "Datei teilen", + "share_folder": "Ordner teilen", + "existing_shares": "Bestehende Freigaben", + "share_options": "Freigabeoptionen", + "password": "Passwort", + "expiration": "Ablaufdatum", + "permissions": "Berechtigungen", + "generated_link": "Generierter Link", + "notify": "Benachrichtigung senden", + "recipient": "Empfänger", + "message": "Nachricht", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "In den Home-Ordner verschieben" + }, + "dropzone": { + "drag_files": "Dateien hierher ziehen oder klicken zum Auswählen", + "drop_files": "Dateien zum Hochladen ablegen" + }, + "permissions": { + "read": "Lesen", + "write": "Schreiben", + "reshare": "Weiterteilen" + }, + "errors": { + "file_not_found": "Datei nicht gefunden", + "folder_not_found": "Ordner nicht gefunden", + "delete_error": "Fehler beim Löschen", + "upload_error": "Fehler beim Hochladen", + "rename_error": "Fehler beim Umbenennen", + "move_error": "Fehler beim Verschieben", + "empty_name": "Der Name darf nicht leer sein", + "name_exists": "Eine Datei oder ein Ordner mit diesem Namen existiert bereits", + "generic_error": "Ein Fehler ist aufgetreten", + "group_name_invalid": "Der Gruppenname muss dem E-Mail-Präfix-Format entsprechen (Buchstaben, Ziffern, Punkt, Bindestrich, Unterstrich; 1–64 Zeichen).", + "group_cycle": "Dieses Mitglied würde einen Gruppen-Zirkelbezug erzeugen.", + "group_depth_exceeded": "Die Verschachtelungstiefe überschreitet das zulässige Maximum (8).", + "group_virtual_immutable": "Die Gruppe „Internal“ wird vom System verwaltet und kann nicht geändert werden.", + "group_not_found": "Gruppe nicht gefunden.", + "group_name_taken": "Eine Gruppe mit diesem Namen existiert bereits." + }, + "breadcrumb": { + "home": "Startseite" + }, + "trash": { + "empty_trash": "Papierkorb leeren", + "empty_state": "Der Papierkorb ist leer", + "original_location": "Ursprünglicher Speicherort", + "deleted_date": "Löschdatum", + "remaining": "Verbleibend", + "actions": "Aktionen", + "restore": "Wiederherstellen", + "delete_permanently": "Endgültig löschen", + "empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht.", + "groupby": { + "remaining_days": "Verbleibende Tage", + "trashed_time": "Löschzeit" + }, + "delete": "Endgültig löschen", + "empty_action": "Papierkorb leeren" + }, + "daysRemaining": { + "expired": "Abgelaufen", + "today": "Heute", + "tomorrow": "Morgen", + "inDays": "{{count}} Tage" + }, + "expiryChip": { + "never": "Läuft nie ab", + "expired": "Abgelaufen", + "today": "Läuft heute ab", + "tomorrow": "Läuft morgen ab", + "inDays": "Läuft in {{count}} Tagen ab", + "onDate": "Läuft am {{date}} ab" + }, + "auth": { + "login_title": "Anmelden", + "username": "Benutzername", + "username_placeholder": "Geben Sie Ihren Benutzernamen ein", + "login_identifier": "Benutzername oder E-Mail", + "login_identifier_placeholder": "Geben Sie Ihren Benutzernamen oder Ihre E-Mail-Adresse ein", + "password": "Passwort", + "password_placeholder": "Geben Sie Ihr Passwort ein", + "login_button": "Anmelden", + "no_account": "Kein Konto?", + "register": "Registrieren", + "admin_setup": "Erstmalig?", + "setup": "Administrator einrichten", + "register_title": "Konto erstellen", + "email": "E-Mail", + "email_placeholder": "Geben Sie Ihre E-Mail ein", + "confirm_password": "Passwort bestätigen", + "confirm_password_placeholder": "Bestätigen Sie Ihr Passwort", + "register_button": "Konto erstellen", + "have_account": "Bereits ein Konto?", + "login": "Anmelden", + "setup_title": "Ersteinrichtung", + "setup_step1": "Admin", + "setup_step2": "System", + "setup_step3": "Abgeschlossen", + "admin_username": "Admin-Benutzername", + "admin_email": "Admin-E-Mail", + "admin_password": "Admin-Passwort", + "create_admin": "Administrator erstellen", + "back_to_login": "Bereits eingerichtet?", + "admin_success": "Administratorkonto erfolgreich erstellt! Sie können sich jetzt anmelden.", + "account_success": "Konto erfolgreich erstellt! Sie können sich jetzt anmelden.", + "passwords_mismatch": "Die Passwörter stimmen nicht überein", + "admin_create_error": "Fehler beim Erstellen des Administratorkontos", + "or": "oder", + "sso_login": "Mit SSO anmelden", + "sso_login_provider": "Mit {{provider}} anmelden", + "magicLinkHint": "Kein Passwort? Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen einmaligen Anmeldelink.", + "magicLinkEmailLabel": "E-Mail-Adresse", + "magicLinkEmailPlaceholder": "sie@beispiel.de", + "magicLinkSubmit": "Anmeldelink senden", + "magicLinkSent": "Wenn für diese E-Mail-Adresse ein Konto besteht, wurde ein Anmeldelink gesendet. Überprüfen Sie Ihren Posteingang.", + "magicLinkUnavailable": "Die Anmeldung per E-Mail ist auf diesem Server nicht verfügbar.", + "magicLinkNetworkError": "Server nicht erreichbar: {{message}}", + "magicLinkToggle": "Kein Passwort? Anmeldelink per E-Mail", + "passwordsMatch": "Passwörter stimmen überein", + "capsLock": "Feststelltaste aktiv", + "caps_lock": "Feststelltaste aktiv", + "magic_email_label": "E-Mail-Adresse", + "magic_hint": "Kein Passwort? Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen einmaligen Anmeldelink.", + "magic_unavailable": "Die Anmeldung per E-Mail ist auf diesem Server nicht verfügbar.", + "passwords_match": "Passwörter stimmen überein", + "sign_in": "Anmelden" + }, + "storage": { + "title": "Speicher", + "calculating": "Berechnung...", + "used": "{{percentage}}% verwendet ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Dieser Dateityp kann nicht in der Vorschau angezeigt werden.", + "download_file": "Datei herunterladen", + "zoom_in": "Vergrößern", + "zoom_out": "Verkleinern", + "zoom_reset": "Zoom zurücksetzen" + }, + "language_selector": { + "title": "Willkommen!", + "subtitle": "Wählen Sie Ihre Sprache, um fortzufahren", + "continue": "Weiter", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Noch keine Favoriten", + "empty_hint": "Markieren Sie Dateien oder Ordner mit einem Stern, um sie zu Ihren Favoriten hinzuzufügen", + "add": "Zu Favoriten hinzufügen", + "remove": "Aus Favoriten entfernen", + "added_title": "Zu Favoriten hinzugefügt", + "added_msg": "zu Favoriten hinzugefügt", + "removed_title": "Aus Favoriten entfernt", + "removed_msg": "aus Favoriten entfernt" + }, + "recent": { + "title": "Zuletzt verwendet", + "clear": "Zuletzt verwendete löschen", + "accessed": "Zugegriffen", + "empty_state": "Keine zuletzt verwendeten Dateien", + "empty_hint": "Dateien, die Sie öffnen, werden hier angezeigt", + "loadMore": "Mehr laden" + }, + "notifications": { + "file_renamed": "Datei umbenannt", + "file_renamed_to": "Datei umbenannt in \"{{name}}\"", + "folder_renamed": "Ordner umbenannt", + "folder_renamed_to": "Ordner umbenannt in \"{{name}}\"", + "file_uploaded": "Datei hochgeladen", + "file_deleted": "Datei in Papierkorb verschoben", + "folder_deleted": "Ordner in Papierkorb verschoben", + "item_deleted_permanently": "Element endgültig gelöscht", + "trash_emptied": "Papierkorb erfolgreich geleert", + "title": "Benachrichtigungen", + "empty": "Keine Benachrichtigungen", + "link_created": "Link erstellt", + "share_success": "Freigabelink erfolgreich erstellt", + "upload_files_section_title": "Upload hier nicht verfügbar", + "upload_files_section_body": "Wechseln Sie zum Abschnitt Dateien, um Dateien hochzuladen" + }, + "batch": { + "one_selected": "1 Element ausgewählt", + "n_selected": "{{count}} Elemente ausgewählt", + "confirm_delete": "Möchten Sie wirklich {{count}} Elemente in den Papierkorb verschieben?", + "move_title": "{{count}} Element(e) verschieben", + "add_favorites": "Zu Favoriten hinzufügen", + "move_copy": "Verschieben oder kopieren" + }, + "admin": { + "page_title": "Admin-Panel", + "back_to_app": "Zurück zu OxiCloud", + "loading": "Laden…", + "access_denied": "Zugriff verweigert", + "access_denied_desc": "Administratorrechte erforderlich.", + "sign_in": "Anmelden", + "tab_dashboard": "Dashboard", + "tab_users": "Benutzer", + "tab_oidc": "SSO / OIDC", + "total_users": "Benutzer gesamt", + "active_users": "Aktive Benutzer", + "admins": "Admins", + "version": "Version", + "storage_overview": "Speicherübersicht", + "used": "Verwendet", + "total_quota": "Gesamtkontingent", + "usage_pct": "Nutzung %", + "users_over_80": "Benutzer >80% Kontingent", + "users_over_quota": "Benutzer über Kontingent", + "system": "System", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Kontingente", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "active": "Aktiv", + "off": "Aus", + "allow_registration": "Öffentliche Selbstregistrierung erlauben", + "registration_warning": "Öffentliche Registrierung ist deaktiviert. Nur Admins können neue Benutzer erstellen.", + "user_management": "Benutzerverwaltung", + "create_user": "Benutzer erstellen", + "col_user": "Benutzer", + "col_role": "Rolle", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Speicher", + "col_last_login": "Letzter Login", + "col_actions": "Aktionen", + "loading_users": "Benutzer werden geladen…", + "failed_load_users": "Laden fehlgeschlagen", + "no_users_found": "Keine Benutzer gefunden", + "showing_users": "Zeige {{from}}-{{to}} von {{total}}", + "prev": "Zurück", + "next": "Weiter", + "inactive": "Inaktiv", + "you_badge": "(du)", + "local": "Lokal", + "never": "Nie", + "just_now": "Gerade eben", + "minutes_ago": "vor {{n}}Min", + "hours_ago": "vor {{n}}Std", + "days_ago": "vor {{n}}T", + "edit_quota_title": "Kontingent bearbeiten", + "reset_password_title": "Passwort zurücksetzen", + "toggle_role_title": "Rolle wechseln", + "deactivate_title": "Deaktivieren", + "activate_title": "Aktivieren", + "delete_title": "Löschen", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "SSO-Authentifizierung aktivieren", + "provider_name": "Anbietername", + "issuer_url": "Aussteller-URL", + "issuer_url_hint": "OpenID Connect Aussteller-URL Ihres Identitätsanbieters", + "auto_discover": "Auto-Erkennung", + "discovering": "Erkennung…", + "client_id": "Client-ID", + "client_secret": "Client-Secret", + "client_secret_placeholder": "Leer lassen für aktuellen Wert", + "secret_configured": "Ein Client-Secret ist bereits konfiguriert", + "callback_url": "Callback-URL", + "callback_url_hint": "(bei IdP registrieren)", + "advanced_settings": "Erweiterte Einstellungen", + "scopes": "Scopes", + "auto_provision": "Benutzer bei erstem Login automatisch anlegen", + "admin_groups": "Admin-Gruppen", + "admin_groups_hint": "Kommagetrennte OIDC-Gruppennamen für Admin-Rolle", + "disable_password": "Passwort-Login deaktivieren (nur OIDC)", + "password_warning": "Dies verhindert ALLE passwortbasierten Anmeldungen!", + "test_btn": "Testen", + "save_btn": "Speichern", + "saving": "Speichern…", + "settings_saved": "Einstellungen gespeichert — OIDC ist jetzt {{status}}", + "quota_modal_title": "Speicherkontingent aktualisieren", + "quota_user_label": "Benutzer:", + "new_quota": "Neues Kontingent", + "quota_unlimited_hint": "0 für unbegrenzt", + "cancel": "Abbrechen", + "create_user_title": "Neuen Benutzer erstellen", + "username_label": "Benutzername", + "username_placeholder": "maxmuster", + "username_hint": "3–32 Zeichen", + "password_label": "Passwort", + "password_placeholder": "Min. 8 Zeichen", + "email_label": "E-Mail", + "email_optional": "(optional)", + "email_placeholder": "benutzer@beispiel.de (automatisch wenn leer)", + "role_label": "Rolle", + "role_user": "Benutzer", + "role_admin": "Admin", + "quota_label": "Kontingent", + "creating": "Erstellen…", + "reset_pw_title": "Passwort zurücksetzen", + "new_password_label": "Neues Passwort", + "resetting": "Zurücksetzen…", + "reset_btn": "Zurücksetzen", + "confirm_role_change": "Rolle zu {{role}} ändern?", + "confirm_deactivate": "Diesen Benutzer wirklich deaktivieren?", + "confirm_activate": "Diesen Benutzer wirklich aktivieren?", + "confirm_delete_user": "Benutzer \"{{name}}\" LÖSCHEN? Kann nicht rückgängig gemacht werden!", + "confirm_action": "Aktion bestätigen", + "confirm_yes": "Bestätigen", + "confirm_no": "Abbrechen", + "error_username_short": "Benutzername muss mindestens 3 Zeichen haben", + "error_password_short": "Passwort muss mindestens 8 Zeichen haben", + "error_generic": "Fehlgeschlagen", + "error_network": "Netzwerkfehler: {{message}}", + "error_create_user": "Benutzer erstellen fehlgeschlagen", + "tab_storage": "Speicher", + "storage_title": "Speicherkonfiguration", + "storage_current_backend": "Aktuelles Backend", + "storage_total_blobs": "Gesamt-Blobs", + "storage_total_size": "Gesamtgröße", + "storage_dedup_ratio": "Deduplizierungsrate", + "storage_backend": "Backend", + "storage_local": "Lokal", + "storage_s3": "S3-kompatibel", + "storage_provider_preset": "Anbieter-Voreinstellung", + "storage_preset_custom": "Benutzerdefiniert", + "storage_endpoint_url": "Endpunkt-URL", + "storage_endpoint_hint": "Leer lassen für AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Region", + "storage_access_key": "Zugriffsschlüssel", + "storage_secret_key": "Geheimschlüssel", + "storage_secret_configured": "Schlüssel konfiguriert", + "storage_key_placeholder": "Neuen Schlüssel eingeben", + "storage_path_style": "Pfadstil erzwingen", + "storage_path_style_hint": "Erforderlich für MinIO und einige S3-kompatible Dienste", + "storage_test_connection": "Verbindung testen", + "storage_test_success": "Verbindung erfolgreich", + "storage_test_failure": "Verbindung fehlgeschlagen", + "storage_save": "Konfiguration speichern", + "storage_saved": "Konfiguration gespeichert", + "storage_migration": "Datenmigration", + "storage_migration_coming_soon": "Migrationstools demnächst verfügbar", + "migration_status_label": "Migrationsstatus", + "migration_start": "Migration starten", + "migration_pause": "Pausieren", + "migration_resume": "Fortsetzen", + "migration_verify": "Verifizieren", + "migration_complete": "Abschließen", + "migration_started": "Migration gestartet", + "migration_paused_msg": "Migration pausiert", + "migration_resumed_msg": "Migration fortgesetzt", + "migration_completed_msg": "Migration erfolgreich abgeschlossen", + "migration_verifying": "Wird verifiziert...", + "migration_verify_passed": "Verifizierung erfolgreich", + "migration_verify_failed": "Verifizierung fehlgeschlagen", + "migration_failed_blobs": "Fehlgeschlagene Blobs", + "testing": "Wird getestet...", + "smtp_disabled": "Deaktiviert (Host nicht gesetzt)", + "smtp_enabled": "Aktiviert", + "smtp_enabled_label": "Status", + "smtp_intro": "SMTP wird ausschließlich über Umgebungsvariablen (OXICLOUD_SMTP_*) konfiguriert. Die folgenden Werte werden aus dem laufenden Server gelesen — zum Ändern bearbeiten Sie die Umgebung und starten OxiCloud neu.", + "smtp_not_configured": "SMTP ist auf diesem Server nicht konfiguriert.", + "smtp_send_failed": "Senden fehlgeschlagen.", + "smtp_send_test": "Test-E-Mail senden", + "smtp_sending": "Senden …", + "smtp_sent": "Test-E-Mail gesendet.", + "smtp_server_code": "Server antwortete", + "smtp_test_intro": "Sendet eine fest einprogrammierte Diagnosenachricht an den unten angegebenen Empfänger und meldet die Antwort des SMTP-Servers, sodass Sie sie mit Ihren Relay-Protokollen abgleichen können.", + "smtp_test_missing_to": "Geben Sie eine Empfängeradresse ein.", + "smtp_test_title": "Test-E-Mail senden", + "smtp_test_to": "Empfängeradresse", + "smtp_title": "Ausgehende E-Mail (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Admins", + "confirm_role": "Rolle zu {{role}} ändern?", + "dashboard": "Dashboard", + "email": "E-Mail", + "mig_complete": "Abschließen", + "mig_pause": "Pausieren", + "mig_resume": "Fortsetzen", + "mig_verify_failed": "Verifizierung fehlgeschlagen", + "mig_verify_passed": "Verifizierung erfolgreich", + "mig_verifying": "Wird verifiziert...", + "oidc_auto_provision": "Benutzer bei erstem Login automatisch anlegen", + "oidc_callback": "Callback-URL", + "oidc_client_id": "Client-ID", + "oidc_disable_pw": "Passwort-Login deaktivieren (nur OIDC)", + "oidc_issuer": "Aussteller-URL", + "oidc_scopes": "Scopes", + "password": "Passwort", + "quotas": "Kontingente", + "reset_pw_for": "Neues Passwort für", + "role": "Rolle", + "smtp_fail": "Senden fehlgeschlagen.", + "smtp_send": "Senden", + "smtp_test": "Test-E-Mail senden", + "smtp_user_state": "Auth", + "status": "Status", + "storage": "Speicher", + "storage_endpoint": "Endpunkt-URL", + "storage_tab": "Speicher", + "time_min_ago": "vor {{n}} Min", + "title": "Admin", + "user": "Benutzer", + "username": "Benutzername", + "users": "Benutzer" + }, + "profile": { + "page_title": "Profil", + "back_to_app": "Zurück zu OxiCloud", + "loading": "Laden…", + "not_authenticated": "Nicht authentifiziert", + "not_authenticated_desc": "Bitte melden Sie sich an, um Ihr Profil anzuzeigen.", + "sign_in": "Anmelden", + "role_admin": "Administrator", + "role_user": "Benutzer", + "account_details": "Kontodetails", + "username": "Benutzername", + "email": "E-Mail", + "role": "Rolle", + "last_login": "Letzter Login", + "storage": "Speicher", + "used": "Verwendet", + "quota": "Kontingent", + "usage": "Nutzung", + "unlimited": "Unbegrenzt", + "app_passwords": "App-Passwörter", + "app_pw_desc": "Passwörter für WebDAV-, CalDAV- und CardDAV-Clients generieren. Jedes Passwort wird nur einmal angezeigt.", + "app_pw_label_placeholder": "Bezeichnung (z.B. Thunderbird, macOS)", + "generate": "Generieren", + "generating": "Generieren…", + "new_password_for": "Neues Passwort für", + "copy_warning": "Kopieren Sie dieses Passwort jetzt. Sie können es nicht erneut anzeigen.", + "copy_to_clipboard": "In Zwischenablage kopieren", + "col_label": "Bezeichnung", + "col_created": "Erstellt", + "col_last_used": "Zuletzt verwendet", + "col_status": "Status", + "active": "Aktiv", + "revoked": "Widerrufen", + "revoke_title": "Widerrufen", + "no_app_passwords": "Noch keine App-Passwörter.", + "client_sessions": "Client-Sitzungen", + "client_sessions_desc": "Automatisch generiert beim Verbinden eines Nextcloud-kompatiblen Clients.", + "col_client": "Client", + "never": "Nie", + "just_now": "Gerade eben", + "minutes_ago": "vor {{n}} Min", + "hours_ago": "vor {{n}} Std", + "days_ago": "vor {{n}} Tagen", + "edit_profile": "Profil bearbeiten", + "edit_oidc_managed": "Um Ihre Informationen (Name, Vorname, Profilbild, …) zu ändern, aktualisieren Sie sie bitte bei Ihrem Identity-Provider. Ihre Änderungen erscheinen bei der nächsten Anmeldung.", + "username_claim_hint": "2–64 Zeichen, Buchstaben / Ziffern / Punkt / Bindestrich / Unterstrich. Nach der Wahl kann der Benutzername nicht mehr geändert werden (DAV/NextCloud-Clients hängen davon ab).", + "username_already_claimed": "Benutzername ist gesetzt und kann nicht geändert werden (DAV/NextCloud-Clients hängen davon ab).", + "given_name": "Vorname", + "family_name": "Nachname", + "notify_on_share": "Mich per E-Mail benachrichtigen, wenn jemand mit mir teilt", + "notify_on_share_hint": "Wenn deaktiviert, werden Freigaben weiterhin in deinem Konto angezeigt — du erhältst nur keine E-Mail dazu.", + "save_profile": "Änderungen speichern", + "profile_saved": "Profil aktualisiert", + "profile_no_changes": "Keine Änderungen zu speichern.", + "profile_save_failed": "Speichern fehlgeschlagen", + "username_taken_error": "Dieser Benutzername ist bereits vergeben.", + "username_immutable_error": "Ihr Benutzername ist bereits gesetzt und kann hier nicht geändert werden. Wenden Sie sich an einen Administrator, wenn Sie umbenennen möchten.", + "change_password": "Passwort ändern", + "current_password": "Aktuelles Passwort", + "new_password": "Neues Passwort", + "min_8_chars": "Mindestens 8 Zeichen", + "confirm_password": "Neues Passwort bestätigen", + "update_password": "Passwort aktualisieren", + "updating": "Aktualisierung…", + "password_updated": "Passwort erfolgreich aktualisiert", + "passwords_no_match": "Passwörter stimmen nicht überein", + "password_too_short": "Passwort muss mindestens 8 Zeichen haben", + "password_change_failed": "Passwort ändern fehlgeschlagen", + "error_network": "Netzwerkfehler: {{message}}", + "error_label_required": "Bitte Bezeichnung eingeben", + "error_create_pw": "App-Passwort erstellen fehlgeschlagen", + "confirm_revoke": "App-Passwort \"{{label}}\" widerrufen? Clients werden nicht mehr funktionieren.", + "error_revoke": "Widerrufen fehlgeschlagen", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Passwörter stimmen nicht überein" + }, + "upload": { + "uploading": "Wird hochgeladen...", + "files": "Dateien", + "complete": "{{count}} / {{total}} hochgeladen" + }, + "storage_quota_exceeded": "Speicherplatz erschöpft", + "sharedwithme": { + "pageTitle": "Mit mir geteilt", + "pageDescription": "Dateien und Ordner, die andere Benutzer mit Ihnen geteilt haben", + "emptyStateTitle": "Noch nichts mit Ihnen geteilt", + "emptyStateDesc": "Elemente, die andere Benutzer mit Ihnen teilen, erscheinen hier", + "loadMore": "Mehr laden", + "sharedBy": "Geteilt von", + "colName": "Name", + "colType": "Typ", + "colSharedBy": "Geteilt von", + "colDate": "Datum der Freigabe", + "colPermissions": "Berechtigungen" + }, + "groupby": { + "none": "Keine", + "title": "Gruppieren nach", + "owner": "Eigentümer", + "shareDate": "Freigabedatum", + "type": "Typ", + "type.folders": "Ordner", + "accessedAt": "Zugriffsdatum", + "modifiedAt": "Änderungsdatum", + "createdAt": "Erstellungsdatum", + "size": "Größe", + "favoriteDate": "Datum der Markierung", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Neu", + "folders": "Ordner" + }, + "dateBucket": { + "today": "Heute", + "last7days": "Letzte 7 Tage", + "last30days": "Letzte 30 Tage", + "unknown": "Unbekannt" + }, + "groups": { + "title": "Gruppen verwalten", + "create_button": "Gruppe erstellen", + "create_dialog_title": "Neue Gruppe", + "edit_dialog_title": "Gruppe umbenennen", + "name_label": "Name", + "name_placeholder": "engineering", + "description_label": "Beschreibung (optional)", + "members_section": "Mitglieder", + "add_member_placeholder": "Benutzer oder Gruppe hinzufügen…", + "no_members": "Noch keine Mitglieder.", + "remove_member": "Entfernen", + "delete_group": "Gruppe löschen", + "delete_confirm": "Die Gruppe „{name}\" löschen? Auf diese Gruppe verweisende Berechtigungen werden widerrufen.", + "empty_state": "Noch keine Gruppen.", + "load_more": "Mehr laden", + "back_to_list": "Zurück", + "loading": "Wird geladen…", + "virtual_badge": "System", + "member_count_zero": "Keine Mitglieder", + "member_count_one": "1 Mitglied", + "member_count_other": "{count} Mitglieder", + "delete_confirm_label": "Tippe den Gruppennamen zur Bestätigung ein:", + "delete_confirm_mismatch": "Tippe den Gruppennamen exakt zur Bestätigung ein.", + "virtual_internal_name": "Intern", + "members_loading": "Mitglieder werden geladen…", + "members_empty": "Keine Mitglieder", + "virtual_internal_explanation": "Jeder interne Benutzer auf diesem Server", + "create": "Gruppe erstellen", + "empty": "Noch keine Gruppen.", + "members": "Mitglieder" + }, + "myshares": { + "copyLink": "Link kopieren", + "deleteLink": "Link löschen", + "notifyByEmail": "Per E-Mail benachrichtigen", + "notifyFailed": "Benachrichtigung konnte nicht gesendet werden.", + "notifyGroupMembers": "Gruppenmitglieder benachrichtigen", + "notifyRateLimited": "Zu viele Benachrichtigungen für diesen Empfänger — versuchen Sie es später erneut.", + "removeAccess": "Zugriff entfernen", + "resendInvitation": "Einladungs-E-Mail erneut senden", + "publicLinks": "Public links" + }, + "sort": { + "asc": "aufsteigend", + "desc": "absteigend" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Audio", + "code": "Code", + "text": "Text" + }, + "common": { + "add": "Hinzufügen", + "cancel": "Abbrechen", + "clear": "Clear", + "close": "Schließen", + "confirm": "Bestätigen", + "copy": "Kopieren", + "create": "Erstellen", + "delete": "Löschen", + "download": "Herunterladen", + "load_more": "Mehr laden", + "loading": "Wird geladen…", + "next": "Weiter", + "no": "Nein", + "previous": "Zurück", + "remove": "Remove", + "rename": "Umbenennen", + "save": "Speichern", + "search": "Suchen", + "yes": "Ja" + }, + "device": { + "continue": "Weiter", + "unknown": "Unbekannt" + }, + "expiryBucket": { + "expired": "Abgelaufen", + "noExpiry": "Kein Ablaufdatum", + "today": "Heute", + "tomorrow": "Morgen" + }, + "nextcloud": { + "error_title": "Fehler", + "sign_in_with": "Mit {{provider}} anmelden" + }, + "search": { + "size_label": "Größe", + "title": "Suchen", + "type": { + "audio": "Audio" + }, + "type_label": "Typ" + }, + "sizeBucket": { + "folders": "Ordner" + }, + "view": { + "grid": "Rasteransicht", + "list": "Listenansicht" + } } diff --git a/frontend/static/locales/en.json b/frontend/static/locales/en.json index c829bf57..b653245e 100644 --- a/frontend/static/locales/en.json +++ b/frontend/static/locales/en.json @@ -1,1027 +1,1481 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "This sign-in link is no longer valid", - "expired_body": "The link may have expired or already been used. We can send you a fresh one — it'll arrive in your inbox in a few seconds.", - "resend_to": "Send a fresh link to {{email}}", - "generic_unavailable": "This sign-in link is no longer valid. It may have already been used or expired. Request a fresh link from the login page.", - "service_unavailable": "Magic-link sign-in is not enabled on this server.", - "internal_error": "Something went wrong while signing you in. Please try again.", - "resend_failure": "Something went wrong while sending the link. Please try again.", - "cross_browser_title": "Continue signing in on this device?", - "cross_browser_body": "You opened this sign-in link in a different browser or device than the one where you requested it.", - "cross_browser_warning": "If you requested this link, it's safe to continue. If you didn't, close this page — clicking Continue would sign someone else into your account.", - "cross_browser_continue": "Continue and sign in", - "resend_confirmation_title": "Check your inbox", - "resend_confirmation_body": "If the sign-in link belonged to an active account, a fresh link has just been sent. Please check your inbox.", - "return_link": "Return to OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", - "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen it by clicking the link below:\n{{link}}\n\nThe link works once and expires in {{ttl_hours}} hours.\nIf you didn't expect this invitation, you can safely ignore this message.\n\n— OxiCloud" - }, - "login": { - "subject": "Sign in to OxiCloud", - "body": "Hello,\n\nUse the link below to sign in to OxiCloud. The link works once and expires in {{ttl_minutes}} minutes. Open it on the same device where you requested it.\n\n{{link}}\n\nIf you didn't request this sign-in link, you can safely ignore this message — no further action is needed.\n\n— OxiCloud" - }, - "kind_file": "file", - "kind_folder": "folder", - "english_fallback_divider": "--- English version below ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "This sign-in link is no longer valid", + "expired_body": "The link may have expired or already been used. We can send you a fresh one — it'll arrive in your inbox in a few seconds.", + "resend_to": "Send a fresh link to {{email}}", + "generic_unavailable": "This sign-in link is no longer valid. It may have already been used or expired. Request a fresh link from the login page.", + "service_unavailable": "Magic-link sign-in is not enabled on this server.", + "internal_error": "Something went wrong while signing you in. Please try again.", + "resend_failure": "Something went wrong while sending the link. Please try again.", + "cross_browser_title": "Continue signing in on this device?", + "cross_browser_body": "You opened this sign-in link in a different browser or device than the one where you requested it.", + "cross_browser_warning": "If you requested this link, it's safe to continue. If you didn't, close this page — clicking Continue would sign someone else into your account.", + "cross_browser_continue": "Continue and sign in", + "resend_confirmation_title": "Check your inbox", + "resend_confirmation_body": "If the sign-in link belonged to an active account, a fresh link has just been sent. Please check your inbox.", + "return_link": "Return to OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", + "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen it by clicking the link below:\n{{link}}\n\nThe link works once and expires in {{ttl_hours}} hours.\nIf you didn't expect this invitation, you can safely ignore this message.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", - "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen OxiCloud to see your new share:\n{{login_link}}\n\nYou may have additional new shares from {{inviter}} — sign in to see all your shared items.\n\n— OxiCloud\n\nYou're receiving this message because you have an OxiCloud account and your share-notification preference is on. You can turn it off in your profile (Email me when someone shares with me)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalist cloud storage system" - }, - "myshares": { - "resendInvitation": "Resend invitation email", - "notifyByEmail": "Notify by email", - "notifyGroupMembers": "Notify group members", - "notifyRateLimited": "Too many notifications for this recipient — try again later.", - "notifyFailed": "Could not send notification.", - "removeAccess": "Remove access", - "copyLink": "Copy link", - "deleteLink": "Delete link" - }, - "nav": { - "files": "Files", - "shared": "My shares", - "sharedwithme": "Shared with me", - "recent": "Recent", - "favorites": "Favorites", - "photos": "Photos", - "music": "Music", - "trash": "Trash" - }, - "photos": { - "empty_state": "No photos yet", - "empty_hint": "Upload images or videos to see them here", - "items_selected": "selected", - "view_daily": "Day", - "view_monthly": "Month", - "view_yearly": "Year" - }, - "music": { - "create_playlist": "Create Playlist", - "playlists": "Playlists", - "no_playlists": "No playlists yet", - "empty_hint": "Create your first playlist to start organizing your music", - "select_playlist": "Select a playlist", - "select_hint": "Choose a playlist from the sidebar or create a new one", - "add_tracks": "Add Tracks", - "add_to_playlist": "Add to Playlist", - "add": "Add", - "added": "Added!", - "added_to_playlist": "added to playlist", - "load_error": "Error loading playlists", - "add_error": "Could not add tracks to playlist", - "no_playlists_yet": "No playlists yet. Create one first!", - "selected_files": "Selected:", - "no_tracks": "No tracks in this playlist", - "unknown_artist": "Unknown Artist", - "unknown_title": "Unknown", - "confirm_delete": "Delete this playlist?", - "playlist_name": "Playlist name", - "create": "Create", - "delete": "Delete", - "share": "Share", - "edit": "Edit", - "play_all": "Play All", - "shuffle": "Shuffle", - "repeat": "Repeat", - "repeat_one": "Repeat One", - "queue": "Queue", - "queue_empty": "Queue is empty", - "not_playing": "Not playing", - "play": "Play", - "pause": "Pause", - "previous": "Previous", - "next": "Next", - "volume": "Volume", - "mute": "Mute", - "unmute": "Unmute", - "title": "Title", - "artist": "Artist", - "album": "Album", - "tracks": "tracks", - "share_with_user": "User ID or email", - "playback_error": "Playback failed", - "error": "Error", - "remove": "Remove", - "track_removed": "Track removed", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "remove_share": "Remove share", - "can_write": "Can edit", - "read_only": "Read only", - "public": "Public", - "private": "Private", - "toggle_public": "Visibility", - "make_public": "Make public", - "make_private": "Make private", - "set_cover": "Set cover", - "cover_updated": "Cover updated", - "search_audio": "Search audio files…", - "no_audio_files": "No audio files found", - "selected": "selected", - "loading": "Loading…", - "search_error": "Could not load audio files", - "adding": "Adding…" - }, - "actions": { - "search": "Search files...", - "new_folder": "New folder", - "upload": "Upload", - "upload_files": "Upload files", - "upload_folder": "Upload folder", - "upload.uploading": "Uploading...", - "upload.complete": "{count} / {total} uploaded", - "upload.files": "files", - "rename": "Rename", - "move": "Move to...", - "move_to": "Move to", - "delete": "Delete", - "download": "Download", - "view": "View", - "cancel": "Cancel", - "confirm": "Confirm", - "share": "Share", - "favorite": "Add to favorites", - "unfavorite": "Remove from favorites", - "copy": "Copy", - "notify": "Notify", - "send": "Send", - "clear_recent": "Clear recent", - "logout": "Log out", - "create": "Create", - "search_btn": "Search", - "close": "Close", - "delete_permanently": "Delete permanently", - "empty_trash": "Empty trash", - "open_parent_folder": "Go to parent folder", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Appearance", - "about": "About OxiCloud", - "about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private.", - "admin_panel": "Admin Panel", - "profile": "My Profile", - "role_user": "User", - "theme": { - "light": "Light", - "dark": "Dark", - "auto": "Like OS" + "login": { + "subject": "Sign in to OxiCloud", + "body": "Hello,\n\nUse the link below to sign in to OxiCloud. The link works once and expires in {{ttl_minutes}} minutes. Open it on the same device where you requested it.\n\n{{link}}\n\nIf you didn't request this sign-in link, you can safely ignore this message — no further action is needed.\n\n— OxiCloud" }, - "manage_groups": "Manage groups" + "kind_file": "file", + "kind_folder": "folder", + "english_fallback_divider": "--- English version below ---" + } }, - "share": { - "dialogTitle": "Share Link", - "linkLabel": "Share Link:", - "copyLink": "Copy", - "permissions": "Permissions:", - "permissionRead": "Read", - "permissionWrite": "Write", - "permissionReshare": "Reshare", - "password": "Password Protection:", - "generatePassword": "Generate", - "expiration": "Expiration Date:", - "update": "Update Share", - "remove": "Remove Share", - "notifyTitle": "Send Notification", - "notifyEmailLabel": "Email Address:", - "notifyMessageLabel": "Message (optional):", - "notifySend": "Send Notification", - "shareWithOthers": "Share with others", - "sharePublicly": "Share publicly", - "shareSettings": "Sharing settings", - "shareCopied": "Link copied to clipboard", - "shareCreated": "Share link created successfully", - "shareUpdated": "Share settings updated successfully", - "shareRemoved": "Share removed successfully", - "inviteByEmail": "Invite by email — invitation will be sent", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Share Link", - "share_linkLabel": "Share Link:", - "share_copyLink": "Copy", - "share_permissions": "Permissions:", - "share_permissionRead": "Read", - "share_permissionWrite": "Write", - "share_permissionReshare": "Reshare", - "share_password": "Password Protection:", - "share_generatePassword": "Generate", - "share_expiration": "Expiration Date:", - "share_update": "Update Share", - "share_remove": "Remove Share", - "share_notifyTitle": "Send Notification", - "share_notifyEmailLabel": "Email Address:", - "share_notifyMessageLabel": "Message (optional):", - "share_notifySend": "Send Notification", - "shared": { - "backToFiles": "Back to Files", - "pageTitle": "Shared Resources", - "pageDescription": "Manage your shared files and folders", - "filterType": "Type:", - "filterAll": "All", - "filterFiles": "Files", - "filterFolders": "Folders", - "sortBy": "Sort by:", - "sortByName": "Name", - "sortByDate": "Date shared", - "sortByExpiration": "Expiration", - "search": "Search", - "colName": "Name", - "colType": "Type", - "colDateShared": "Date Shared", - "colExpiration": "Expiration", - "colPermissions": "Permissions", - "colPassword": "Password", - "colActions": "Actions", - "emptyStateTitle": "No shared resources yet", - "emptyStateDesc": "When you share files or folders, they will appear here", - "goToFiles": "Go to Files", - "typeFile": "File", - "typeFolder": "Folder", - "noExpiration": "No expiration", - "hasPassword": "Yes", - "noPassword": "No", - "editShare": "Edit Share", - "notifyShare": "Notify Someone", - "copyLink": "Copy Link", - "removeShare": "Remove Share", - "linkCopied": "Link copied to clipboard!", - "linkCopyFailed": "Failed to copy link", - "itemUpdated": "Share settings updated successfully", - "itemRemoved": "Share removed successfully", - "invalidEmail": "Please enter a valid email address", - "notificationSent": "Notification sent successfully", - "notificationFailed": "Failed to send notification", - "shared_backToFiles": "Back to Files", - "shared_pageTitle": "Shared Resources", - "shared_pageDescription": "Manage your shared files and folders", - "shared_filterType": "Type:", - "shared_filterAll": "All", - "shared_filterFiles": "Files", - "shared_filterFolders": "Folders", - "shared_sortBy": "Sort by:", - "shared_sortByName": "Name", - "shared_sortByDate": "Date shared", - "shared_sortByExpiration": "Expiration", - "shared_search": "Search", - "shared_colName": "Name", - "shared_colType": "Type", - "shared_colDateShared": "Date Shared", - "shared_colExpiration": "Expiration", - "shared_colPermissions": "Permissions", - "shared_colPassword": "Password", - "shared_colActions": "Actions", - "shared_emptyStateTitle": "No shared resources yet", - "shared_emptyStateDesc": "When you share files or folders, they will appear here", - "shared_goToFiles": "Go to Files", - "shared_typeFile": "File", - "shared_typeFolder": "Folder", - "shared_noExpiration": "No expiration", - "shared_hasPassword": "Yes", - "shared_noPassword": "No", - "shared_editShare": "Edit Share", - "shared_notifyShare": "Notify Someone", - "shared_copyLink": "Copy Link", - "shared_removeShare": "Remove Share", - "shared_linkCopied": "Link copied to clipboard!", - "shared_linkCopyFailed": "Failed to copy link", - "shared_itemUpdated": "Share settings updated successfully", - "shared_itemRemoved": "Share removed successfully", - "shared_invalidEmail": "Please enter a valid email address", - "shared_notificationSent": "Notification sent successfully", - "shared_notificationFailed": "Failed to send notification" - }, - "files": { - "name": "Name", - "type": "Type", - "size": "Size", - "modified": "Modified", - "no_files": "No files in this folder", - "empty_hint": "Upload files or create folders to get started", - "loading": "Loading files…", - "view_grid": "Grid view", - "view_list": "List view", - "file_types": { - "document": "Document", - "image": "Image", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Text", - "folder": "Folder", - "spreadsheet": "Spreadsheet", - "presentation": "Presentation", - "archive": "Archive", - "installer": "Installer", - "code": "Code" - }, - "owner": "Owner" - }, - "dialogs": { - "rename_folder": "Rename folder", - "rename_file": "Rename file", - "new_name": "New name", - "new_folder_title": "New folder", - "folder_name": "Folder name", - "folder_placeholder": "My folder", - "rename_title": "Rename", - "move_file": "Move file", - "move_folder": "Move folder", - "select_destination": "Select destination folder:", - "select_this_folder": "Select this folder", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "root": "Root", - "delete_confirmation": "Are you sure you want to delete", - "and_contents": "and all its contents", - "no_undo": "This action cannot be undone", - "confirm_title": "Confirm action", - "confirm_delete": "Move to trash", - "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", - "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", - "confirm_permanent_delete": "Delete permanently", - "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", - "confirm_empty_trash": "Empty trash", - "confirm_delete_share": "Delete share link", - "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", - "share_file": "Share File", - "share_folder": "Share Folder", - "existing_shares": "Existing Shares", - "share_options": "Share Options", - "password": "Password", - "expiration": "Expiration", - "permissions": "Permissions", - "generated_link": "Generated Link", - "notify": "Send Notification", - "recipient": "Recipient", - "message": "Message", - "move_to_home": "Move to Home folder" - }, - "dropzone": { - "drag_files": "Drag files here or click to select", - "drop_files": "Drop files to upload" - }, - "permissions": { - "read": "Read", - "write": "Write", - "reshare": "Reshare" - }, - "errors": { - "file_not_found": "File not found", - "folder_not_found": "Folder not found", - "delete_error": "Error deleting", - "upload_error": "Error uploading file", - "rename_error": "Error renaming", - "move_error": "Error moving", - "empty_name": "Name cannot be empty", - "name_exists": "A file or folder with that name already exists", - "generic_error": "An error has occurred", - "group_name_invalid": "Group name must match the email-prefix format (letters, digits, dot, dash, underscore; 1–64 chars).", - "group_cycle": "This member would create a circular group reference.", - "group_depth_exceeded": "This nesting depth exceeds the maximum allowed (8).", - "group_virtual_immutable": "The 'Internal' group is system-managed and cannot be modified.", - "group_not_found": "Group not found.", - "group_name_taken": "A group with this name already exists." - }, - "breadcrumb": { - "home": "Home" - }, - "trash": { - "empty_trash": "Empty Trash", - "empty_state": "Trash is empty", - "original_location": "Original location", - "deleted_date": "Deletion date", - "remaining": "Remaining", - "actions": "Actions", - "restore": "Restore", - "delete_permanently": "Delete permanently", - "empty_confirm": "Are you sure you want to empty the trash? This will permanently delete all items.", - "groupby": { - "remaining_days": "Remaining days", - "trashed_time": "Trashed time" - } - }, - "daysRemaining": { - "expired": "Expired", - "today": "Today", - "tomorrow": "Tomorrow", - "inDays": "{{count}} days" - }, - "expiryChip": { - "never": "Never expires", - "expired": "Expired", - "today": "Expires today", - "tomorrow": "Expires tomorrow", - "inDays": "Expires in {{count}} days", - "onDate": "Expires {{date}}" - }, - "auth": { - "login_title": "Sign in", - "username": "Username", - "username_placeholder": "Enter your username", - "login_identifier": "Username or email", - "login_identifier_placeholder": "Enter your username or email", - "password": "Password", - "password_placeholder": "Enter your password", - "login_button": "Sign in", - "no_account": "Don't have an account?", - "register": "Sign up", - "admin_setup": "First time?", - "setup": "Setup administrator", - "register_title": "Create account", - "email": "Email", - "email_placeholder": "Enter your email", - "confirm_password": "Confirm password", - "confirm_password_placeholder": "Confirm your password", - "register_button": "Create account", - "have_account": "Already have an account?", - "login": "Sign in", - "setup_title": "Initial setup", - "setup_step1": "Admin", - "setup_step2": "System", - "setup_step3": "Complete", - "admin_username": "Admin username", - "admin_email": "Admin email", - "admin_password": "Admin password", - "create_admin": "Create administrator", - "back_to_login": "Already set up?", - "admin_success": "Administrator account created successfully! You can now sign in.", - "account_success": "Account created successfully! You can now sign in.", - "passwords_mismatch": "Passwords do not match", - "admin_create_error": "Error creating administrator account", - "or": "or", - "sso_login": "Sign in with SSO", - "sso_login_provider": "Sign in with {{provider}}", - "magicLinkHint": "No password? Enter your email and we'll send you a one-time sign-in link.", - "magicLinkEmailLabel": "Email address", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "Send sign-in link", - "magicLinkSent": "If an account exists for that email, a sign-in link has been sent. Check your inbox.", - "magicLinkUnavailable": "Sign-in by email is not available on this server.", - "magicLinkNetworkError": "Could not reach the server: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Storage", - "calculating": "Calculating...", - "used": "{{percentage}}% used ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "This file type cannot be previewed.", - "download_file": "Download file", - "zoom_in": "Zoom in", - "zoom_out": "Zoom out", - "zoom_reset": "Reset zoom" - }, - "language_selector": { - "title": "Welcome!", - "subtitle": "Select your language to continue", - "continue": "Continue", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "No favorites yet", - "empty_hint": "Star files or folders to add them to your favorites", - "add": "Add to favorites", - "remove": "Remove from favorites", - "added_title": "Added to favorites", - "added_msg": "added to favorites", - "removed_title": "Removed from favorites", - "removed_msg": "removed from favorites" - }, - "recent": { - "title": "Recent", - "clear": "Clear recent", - "accessed": "Accessed", - "empty_state": "No recent files", - "empty_hint": "Files you open will appear here", - "loadMore": "Load more" - }, - "notifications": { - "file_renamed": "File renamed", - "file_renamed_to": "File renamed to \"{{name}}\"", - "folder_renamed": "Folder renamed", - "folder_renamed_to": "Folder renamed to \"{{name}}\"", - "file_uploaded": "File uploaded", - "file_deleted": "File moved to trash", - "folder_deleted": "Folder moved to trash", - "item_deleted_permanently": "Item permanently deleted", - "trash_emptied": "Trash emptied successfully", - "title": "Notifications", - "empty": "No notifications", - "link_created": "Link created", - "share_success": "Shared link created successfully", - "upload_files_section_title": "Upload not available here", - "upload_files_section_body": "Go to the Files section to upload files" - }, - "batch": { - "one_selected": "1 item selected", - "n_selected": "{{count}} items selected", - "confirm_delete": "Are you sure you want to move {{count}} items to trash?", - "move_title": "Move {{count}} item(s)", - "add_favorites": "Add to favorites", - "move_copy": "Move or copy" - }, - "admin": { - "page_title": "Admin Panel", - "back_to_app": "Back to OxiCloud", - "loading": "Loading…", - "access_denied": "Access Denied", - "access_denied_desc": "Administrator privileges required to access this panel.", - "sign_in": "Sign in", - "tab_dashboard": "Dashboard", - "tab_users": "Users", - "tab_oidc": "SSO / OIDC", - "total_users": "Total Users", - "active_users": "Active Users", - "admins": "Admins", - "version": "Version", - "storage_overview": "Storage Overview", - "used": "Used", - "total_quota": "Total Quota", - "usage_pct": "Usage %", - "users_over_80": "Users >80% quota", - "users_over_quota": "Users over quota", - "system": "System", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quotas", - "enabled": "Enabled", - "disabled": "Disabled", - "active": "Active", - "off": "Off", - "allow_registration": "Allow public self-registration", - "registration_warning": "Public registration is disabled. Only admins can create new users.", - "user_management": "User Management", - "create_user": "Create User", - "col_user": "User", - "col_role": "Role", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Storage", - "col_last_login": "Last Login", - "col_actions": "Actions", - "loading_users": "Loading users…", - "failed_load_users": "Failed to load users", - "no_users_found": "No users found", - "showing_users": "Showing {{from}}-{{to}} of {{total}}", - "prev": "Prev", - "next": "Next", - "inactive": "Inactive", - "you_badge": "(you)", - "local": "Local", - "never": "Never", - "just_now": "Just now", - "minutes_ago": "{{n}}m ago", - "hours_ago": "{{n}}h ago", - "days_ago": "{{n}}d ago", - "edit_quota_title": "Edit quota", - "reset_password_title": "Reset password", - "toggle_role_title": "Toggle role", - "deactivate_title": "Deactivate", - "activate_title": "Activate", - "delete_title": "Delete", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "Enable SSO Authentication", - "provider_name": "Provider Name", - "issuer_url": "Issuer URL", - "issuer_url_hint": "OpenID Connect issuer URL of your identity provider", - "auto_discover": "Auto-discover", - "discovering": "Discovering…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Leave empty to keep current value", - "secret_configured": "A client secret is already configured", - "callback_url": "Callback URL", - "callback_url_hint": "(register in your IdP)", - "advanced_settings": "Advanced Settings", - "scopes": "Scopes", - "auto_provision": "Auto-provision users on first login", - "admin_groups": "Admin Groups", - "admin_groups_hint": "Comma-separated OIDC group names that map to admin role", - "disable_password": "Disable password login (OIDC only)", - "password_warning": "This will prevent ALL password-based logins!", - "test_btn": "Test", - "save_btn": "Save", - "saving": "Saving…", - "settings_saved": "Settings saved — OIDC is now {{status}}", - "quota_modal_title": "Update Storage Quota", - "quota_user_label": "User:", - "new_quota": "New Quota", - "quota_unlimited_hint": "Set to 0 for unlimited", - "cancel": "Cancel", - "create_user_title": "Create New User", - "username_label": "Username", - "username_placeholder": "johndoe", - "username_hint": "3–32 characters", - "password_label": "Password", - "password_placeholder": "Min 8 characters", - "email_label": "Email", - "email_optional": "(optional)", - "email_placeholder": "user@example.com (auto-generated if empty)", - "role_label": "Role", - "role_user": "User", - "role_admin": "Admin", - "quota_label": "Quota", - "creating": "Creating…", - "reset_pw_title": "Reset Password", - "new_password_label": "New Password", - "resetting": "Resetting…", - "reset_btn": "Reset", - "confirm_role_change": "Change role to {{role}}?", - "confirm_deactivate": "Are you sure you want to deactivate this user?", - "confirm_activate": "Are you sure you want to activate this user?", - "confirm_delete_user": "DELETE user \"{{name}}\"? This cannot be undone!", - "confirm_action": "Confirm Action", - "confirm_yes": "Confirm", - "confirm_no": "Cancel", - "error_username_short": "Username must be at least 3 characters", - "error_password_short": "Password must be at least 8 characters", - "error_generic": "Failed", - "error_network": "Network error: {{message}}", - "error_create_user": "Failed to create user", - "tab_storage": "Storage", - "storage_title": "Storage Backend", - "storage_current_backend": "Active Backend", - "storage_total_blobs": "Total Blobs", - "storage_total_size": "Total Size", - "storage_dedup_ratio": "Dedup Ratio", - "storage_backend": "Backend Type", - "storage_local": "Local Filesystem", - "storage_s3": "S3-Compatible", - "storage_provider_preset": "Provider Preset", - "storage_preset_custom": "Custom", - "storage_endpoint_url": "Endpoint URL", - "storage_endpoint_hint": "Leave empty for Amazon S3 default", - "storage_bucket": "Bucket", - "storage_region": "Region", - "storage_access_key": "Access Key ID", - "storage_secret_key": "Secret Access Key", - "storage_secret_configured": "A secret key is already configured", - "storage_key_placeholder": "Leave empty to keep current value", - "storage_path_style": "Force Path Style", - "storage_path_style_hint": "Required for MinIO and some S3-compatible providers", - "storage_test_connection": "Test Connection", - "storage_test_success": "Connection successful", - "storage_test_failure": "Connection failed", - "storage_save": "Save", - "storage_saved": "Storage settings saved successfully", - "storage_migration": "Backend Migration", - "storage_migration_coming_soon": "Backend migration will be available in a future update.", - "migration_status_label": "Status:", - "migration_start": "Start Migration", - "migration_pause": "Pause", - "migration_resume": "Resume", - "migration_verify": "Verify Integrity", - "migration_complete": "Finalize", - "migration_started": "Migration started", - "migration_paused_msg": "Migration paused", - "migration_resumed_msg": "Migration resumed", - "migration_completed_msg": "Migration finalized. Restart the server to use the new backend.", - "migration_verifying": "Verifying…", - "migration_verify_passed": "Verification passed", - "migration_verify_failed": "Verification failed", - "migration_failed_blobs": "failed blobs", - "testing": "Testing…", - "tab_plugins": "Plugins", - "plugins_title": "Plugins", - "plugins_disabled": "Plugins are disabled on this server. Set OXICLOUD_ENABLE_PLUGINS=true (and build with the \"plugins\" feature) to manage WASM plugins here.", - "plugins_install_title": "Install a plugin", - "plugins_install_intro": "Upload a plugin bundle (.zip) containing plugin.toml and its compiled WebAssembly module (.wasm). The manifest is validated and the module is probed before installation.", - "plugins_bundle_label": "Plugin bundle (.zip)", - "plugins_install": "Install plugin", - "plugins_installed_title": "Installed plugins", - "plugins_col_name": "Name", - "plugins_col_id": "ID", - "plugins_col_version": "Version", - "plugins_col_events": "Events", - "plugins_col_status": "Status", - "plugins_col_actions": "Actions", - "plugins_loading": "Loading plugins…", - "plugins_none": "No plugins installed.", - "plugins_enabled": "Enabled", - "plugins_disabled_badge": "Disabled", - "plugins_enable": "Enable", - "plugins_disable": "Disable", - "plugins_delete": "Delete", - "plugins_confirm_delete": "Delete plugin \"{{name}}\"? Its files will be removed from the server.", - "plugins_installing": "Installing…", - "plugins_installed": "Installed {{name}}.", - "plugins_install_missing_bundle": "Select a plugin bundle (.zip).", - "plugins_details": "Logs & details", - "plugins_back": "Back to plugins", - "plugins_retention_title": "Log retention", - "plugins_retention_intro": "Rotated log segments older than the retention window, or beyond the size cap, are pruned on a schedule.", - "plugins_retention_days": "Retention (days)", - "plugins_retention_max_mb": "Max log size (MB)", - "plugins_retention_save": "Save retention", - "plugins_retention_saved": "Retention saved.", - "plugins_retention_invalid": "Enter non-negative numbers.", - "plugins_logs_title": "Logs", - "plugins_logs_level_all": "All levels", - "plugins_logs_search": "Search messages…", - "plugins_logs_live": "Live", - "plugins_logs_clear": "Clear", - "plugins_logs_confirm_clear": "Clear all logs for this plugin?", - "plugins_logs_none": "No log entries.", - "plugins_logs_col_time": "Time", - "plugins_logs_col_level": "Level", - "plugins_logs_col_kind": "Kind", - "plugins_logs_col_invocation": "Invocation", - "plugins_logs_col_message": "Message", - "plugins_logs_showing": "Showing {{from}}–{{to}} of {{total}}", - "tab_smtp": "SMTP", - "smtp_title": "Outbound Email (SMTP)", - "smtp_intro": "SMTP is configured exclusively via environment variables (OXICLOUD_SMTP_*). The values below are read from the running server — to change them, edit the environment and restart OxiCloud.", - "smtp_enabled_label": "Status", - "smtp_enabled": "Enabled", - "smtp_disabled": "Disabled (host unset)", - "smtp_test_title": "Send a test email", - "smtp_test_intro": "Sends a hardcoded diagnostic message to the recipient below and reports the SMTP server's response so you can correlate it with your relay logs.", - "smtp_test_to": "Recipient address", - "smtp_send_test": "Send test email", - "smtp_sending": "Sending…", - "smtp_sent": "Test email sent.", - "smtp_send_failed": "Send failed.", - "smtp_server_code": "Server replied", - "smtp_test_missing_to": "Enter a recipient address.", - "smtp_not_configured": "SMTP is not configured on this server." - }, - "profile": { - "page_title": "Profile", - "back_to_app": "Back to OxiCloud", - "loading": "Loading…", - "not_authenticated": "Not Authenticated", - "not_authenticated_desc": "Please sign in to view your profile.", - "sign_in": "Sign in", - "role_admin": "Administrator", - "role_user": "User", - "account_details": "Account Details", - "username": "Username", - "email": "Email", - "role": "Role", - "last_login": "Last Login", - "storage": "Storage", - "used": "Used", - "quota": "Quota", - "usage": "Usage", - "unlimited": "Unlimited", - "app_passwords": "App Passwords", - "app_pw_desc": "Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.", - "app_pw_label_placeholder": "Label (e.g. Thunderbird, macOS)", - "generate": "Generate", - "generating": "Generating…", - "new_password_for": "New password for", - "copy_warning": "Copy this password now. You won't be able to see it again.", - "copy_to_clipboard": "Copy to clipboard", - "col_label": "Label", - "col_created": "Created", - "col_last_used": "Last Used", - "col_status": "Status", - "active": "Active", - "revoked": "Revoked", - "revoke_title": "Revoke", - "no_app_passwords": "No app passwords yet.", - "client_sessions": "Client sessions", - "client_sessions_desc": "Auto-generated when you connect a Nextcloud-compatible client.", - "col_client": "Client", - "never": "Never", - "just_now": "Just now", - "minutes_ago": "{{n}} min ago", - "hours_ago": "{{n}}h ago", - "days_ago": "{{n}} days ago", - "edit_profile": "Edit Profile", - "edit_oidc_managed": "To change your information (name, first name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.", - "username_claim_hint": "2–64 characters, letters/digits/dot/dash/underscore. Once chosen, the username can't be changed (DAV/NextCloud clients depend on it).", - "username_already_claimed": "Username is set and can't be changed (DAV/NextCloud clients depend on it).", - "given_name": "First name", - "family_name": "Last name", - "notify_on_share": "Email me when someone shares with me", - "notify_on_share_hint": "When unchecked, shares still appear in your account — you just won't get an email about them.", - "save_profile": "Save changes", - "profile_saved": "Profile updated", - "profile_no_changes": "No changes to save.", - "profile_save_failed": "Save failed", - "username_taken_error": "That username is already taken.", - "username_immutable_error": "Your username is already set and can't be changed here. Contact an administrator if you need a rename.", - "change_password": "Change Password", - "current_password": "Current Password", - "new_password": "New Password", - "min_8_chars": "At least 8 characters", - "confirm_password": "Confirm New Password", - "update_password": "Update Password", - "updating": "Updating…", - "password_updated": "Password updated successfully", - "passwords_no_match": "Passwords do not match", - "password_too_short": "Password must be at least 8 characters", - "password_change_failed": "Failed to change password", - "error_network": "Network error: {{message}}", - "error_label_required": "Please enter a label", - "error_create_pw": "Failed to create app password", - "confirm_revoke": "Revoke app password \"{{label}}\"? Clients using this password will stop working.", - "error_revoke": "Failed to revoke app password", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Uploading...", - "files": "files", - "complete": "{{count}} / {{total}} uploaded" - }, - "storage_quota_exceeded": "Storage quota exceeded", - "sharedwithme": { - "pageTitle": "Shared with me", - "pageDescription": "Files and folders others have shared with you", - "emptyStateTitle": "Nothing shared with you yet", - "emptyStateDesc": "Items shared with you by other users will appear here", - "loadMore": "Load more", - "sharedBy": "Shared by", - "colName": "Name", - "colType": "Type", - "colSharedBy": "Shared by", - "colDate": "Date shared", - "colPermissions": "Permissions" - }, - "groupby": { - "none": "None", - "byFiles": "By files", - "sharedWith": "Shared with", - "title": "Group by", - "type": "Type", - "type.folders": "Folders", - "owner": "Owner", - "shareDate": "Share date", - "favoriteDate": "Favorite date", - "accessedAt": "Accessed date", - "modifiedAt": "Modified date", - "createdAt": "Created date", - "size": "Size", - "justAdded": "New" - }, - "dateBucket": { - "today": "Today", - "last7days": "Last 7 days", - "last30days": "Last 30 days" - }, - "groups": { - "title": "Manage groups", - "create_button": "Create group", - "create_dialog_title": "New group", - "edit_dialog_title": "Rename group", - "name_label": "Name", - "name_placeholder": "engineering", - "description_label": "Description (optional)", - "members_section": "Members", - "members_loading": "Loading members…", - "members_empty": "No members", - "add_member_placeholder": "Add a user or group…", - "no_members": "No members yet.", - "remove_member": "Remove", - "delete_group": "Delete group", - "delete_confirm": "Delete the group \"{name}\"? Grants referencing this group will be revoked.", - "empty_state": "No groups yet.", - "load_more": "Load more", - "back_to_list": "Back", - "loading": "Loading…", - "virtual_badge": "System", - "member_count_zero": "no members", - "member_count_one": "1 member", - "member_count_other": "{count} members", - "delete_confirm_label": "Type the group name to confirm:", - "delete_confirm_mismatch": "Type the group name exactly to confirm.", - "virtual_internal_name": "Internal", - "virtual_internal_explanation": "Every internal user on this server" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", + "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen OxiCloud to see your new share:\n{{login_link}}\n\nYou may have additional new shares from {{inviter}} — sign in to see all your shared items.\n\n— OxiCloud\n\nYou're receiving this message because you have an OxiCloud account and your share-notification preference is on. You can turn it off in your profile (Email me when someone shares with me)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalist cloud storage system" + }, + "myshares": { + "resendInvitation": "Resend invitation email", + "notifyByEmail": "Notify by email", + "notifyGroupMembers": "Notify group members", + "notifyRateLimited": "Too many notifications for this recipient — try again later.", + "notifyFailed": "Could not send notification.", + "removeAccess": "Remove access", + "copyLink": "Copy link", + "deleteLink": "Delete link", + "editSharing": "Edit sharing", + "emptyStateDesc": "Items you share with others will appear here", + "emptyStateTitle": "You haven't shared anything yet", + "manageAccess": "Manage access", + "notifySent": "Notification sent.", + "passwordLinks": "Password-protected links", + "publicLinks": "Public links" + }, + "nav": { + "files": "Files", + "shared": "My shares", + "sharedwithme": "Shared with me", + "recent": "Recent", + "favorites": "Favorites", + "photos": "Photos", + "music": "Music", + "trash": "Trash", + "groups": "Groups", + "primary": "Primary", + "profile": "Profile", + "shared_with_me": "Shared with me", + "toggle": "Toggle navigation menu" + }, + "photos": { + "empty_state": "No photos yet", + "empty_hint": "Upload images or videos to see them here", + "items_selected": "selected", + "view_daily": "Day", + "view_monthly": "Month", + "view_yearly": "Year", + "confirm_delete": "Move {{n}} photos to trash?", + "confirm_delete_one": "Delete {{name}}?", + "delete": "Delete photos", + "empty": "No photos yet.", + "full_resolution": "Full resolution", + "group_by": "Group by", + "trash_partial": "{{ok}} of {{total}} moved to trash.", + "trashed": "{{n}} moved to trash." + }, + "music": { + "create_playlist": "Create Playlist", + "playlists": "Playlists", + "no_playlists": "No playlists yet", + "empty_hint": "Create your first playlist to start organizing your music", + "select_playlist": "Select a playlist", + "select_hint": "Choose a playlist from the sidebar or create a new one", + "add_tracks": "Add Tracks", + "add_to_playlist": "Add to Playlist", + "add": "Add", + "added": "Added!", + "added_to_playlist": "added to playlist", + "load_error": "Error loading playlists", + "add_error": "Could not add tracks to playlist", + "no_playlists_yet": "No playlists yet. Create one first!", + "selected_files": "Selected:", + "no_tracks": "No tracks in this playlist", + "unknown_artist": "Unknown Artist", + "unknown_title": "Unknown", + "confirm_delete": "Delete this playlist?", + "playlist_name": "Playlist name", + "create": "Create", + "delete": "Delete", + "share": "Share", + "edit": "Edit", + "play_all": "Play All", + "shuffle": "Shuffle", + "repeat": "Repeat", + "repeat_one": "Repeat One", + "queue": "Queue", + "queue_empty": "Queue is empty", + "not_playing": "Not playing", + "play": "Play", + "pause": "Pause", + "previous": "Previous", + "next": "Next", + "volume": "Volume", + "mute": "Mute", + "unmute": "Unmute", + "title": "Title", + "artist": "Artist", + "album": "Album", + "tracks": "tracks", + "share_with_user": "User ID or email", + "playback_error": "Playback failed", + "error": "Error", + "remove": "Remove", + "track_removed": "Track removed", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "remove_share": "Remove share", + "can_write": "Can edit", + "read_only": "Read only", + "public": "Public", + "private": "Private", + "toggle_public": "Visibility", + "make_public": "Make public", + "make_private": "Make private", + "set_cover": "Set cover", + "cover_updated": "Cover updated", + "search_audio": "Search audio files…", + "no_audio_files": "No audio files found", + "selected": "selected", + "loading": "Loading…", + "search_error": "Could not load audio files", + "adding": "Adding…", + "add_selected": "Add selected", + "create_playlist_hint": "Type a playlist name to create one.", + "created": "Created “{{name}}”.", + "delete_playlist": "Delete playlist", + "deleted": "Deleted “{{name}}”.", + "edit_description": "Edit description", + "empty_playlist": "This playlist has no tracks yet.", + "new_playlist": "New playlist", + "no_audio": "No audio files found.", + "now_private": "Playlist is now private.", + "now_public": "Playlist is now public.", + "pick_or_create": "Existing: {{list}}. Type a name to add or create.", + "prev": "Previous", + "rename_playlist": "Rename playlist", + "reordered": "Playlist reordered.", + "seek": "Seek", + "selected_count": "{{n}} selected", + "share_added": "Shared.", + "track_count": "{{n}} tracks", + "tracks_added": "Added {{n}} track(s)." + }, + "actions": { + "search": "Search files...", + "new_folder": "New folder", + "upload": "Upload", + "upload_files": "Upload files", + "upload_folder": "Upload folder", + "upload.uploading": "Uploading...", + "upload.complete": "{count} / {total} uploaded", + "upload.files": "files", + "rename": "Rename", + "move": "Move to...", + "move_to": "Move to", + "delete": "Delete", + "download": "Download", + "view": "View", + "cancel": "Cancel", + "confirm": "Confirm", + "share": "Share", + "favorite": "Add to favorites", + "unfavorite": "Remove from favorites", + "copy": "Copy", + "notify": "Notify", + "send": "Send", + "clear_recent": "Clear recent", + "logout": "Log out", + "create": "Create", + "search_btn": "Search", + "close": "Close", + "delete_permanently": "Delete permanently", + "empty_trash": "Empty trash", + "open_parent_folder": "Go to parent folder", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Appearance", + "about": "About OxiCloud", + "about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private.", + "admin_panel": "Admin Panel", + "profile": "My Profile", + "role_user": "User", + "theme": { + "light": "Light", + "dark": "Dark", + "auto": "Like OS" + }, + "manage_groups": "Manage groups", + "admin": "Admin", + "mit_license": "MIT License", + "title": "User menu" + }, + "share": { + "dialogTitle": "Share Link", + "linkLabel": "Share Link:", + "copyLink": "Copy", + "permissions": "Permissions:", + "permissionRead": "Read", + "permissionWrite": "Write", + "permissionReshare": "Reshare", + "password": "Password Protection:", + "generatePassword": "Generate", + "expiration": "Expiration Date:", + "update": "Update Share", + "remove": "Remove Share", + "notifyTitle": "Send Notification", + "notifyEmailLabel": "Email Address:", + "notifyMessageLabel": "Message (optional):", + "notifySend": "Send Notification", + "shareWithOthers": "Share with others", + "sharePublicly": "Share publicly", + "shareSettings": "Sharing settings", + "shareCopied": "Link copied to clipboard", + "shareCreated": "Share link created successfully", + "shareUpdated": "Share settings updated successfully", + "shareRemoved": "Share removed successfully", + "inviteByEmail": "Invite by email — invitation will be sent", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "addPassword": "Add password", + "add_people": "Add people, groups, or email…", + "bad_password": "Incorrect password. Please try again.", + "changePassword": "Change password", + "copied": "Link copied", + "copy": "Copy", + "copy_failed": "Could not copy link", + "create_link": "Create link", + "created": "Public link created", + "dialog_title": "Share “{{name}}”", + "download": "Download", + "download_zip": "Download ZIP", + "empty_folder": "This folder is empty.", + "error": "Something went wrong. Please try again.", + "expired": "This share link is no longer available.", + "expires_optional": "Expires (optional)", + "expiry": "Expiry", + "files": "Files", + "folders": "Folders", + "invalid": "This share link is invalid.", + "link": "Link", + "link_name": "Link name (optional)", + "no_people": "Not shared with anyone yet.", + "none": "No public links yet.", + "notify": { + "coalesced": "{{n}} already notified recently.", + "rateLimited": "{{n}} hit the rate limit — try later.", + "sent": "{{n}} notified by email.", + "skipped": "{{n}} skipped (no email / opted out)." + }, + "notifyByEmail": "Notify by email", + "passwordPrompt": "Set a password:", + "passwordPrompt_clear": "New password (blank to remove):", + "password_cleared": "Password removed", + "password_optional": "Password (optional)", + "password_set": "Password updated", + "password_title": "Password required", + "public_link": "Public link", + "revoke": "Remove", + "set_expiry": "Set expiry", + "title": "Shared", + "unlock": "Unlock", + "role_label": "Role" + }, + "share_dialogTitle": "Share Link", + "share_linkLabel": "Share Link:", + "share_copyLink": "Copy", + "share_permissions": "Permissions:", + "share_permissionRead": "Read", + "share_permissionWrite": "Write", + "share_permissionReshare": "Reshare", + "share_password": "Password Protection:", + "share_generatePassword": "Generate", + "share_expiration": "Expiration Date:", + "share_update": "Update Share", + "share_remove": "Remove Share", + "share_notifyTitle": "Send Notification", + "share_notifyEmailLabel": "Email Address:", + "share_notifyMessageLabel": "Message (optional):", + "share_notifySend": "Send Notification", + "shared": { + "backToFiles": "Back to Files", + "pageTitle": "Shared Resources", + "pageDescription": "Manage your shared files and folders", + "filterType": "Type:", + "filterAll": "All", + "filterFiles": "Files", + "filterFolders": "Folders", + "sortBy": "Sort by:", + "sortByName": "Name", + "sortByDate": "Date shared", + "sortByExpiration": "Expiration", + "search": "Search", + "colName": "Name", + "colType": "Type", + "colDateShared": "Date Shared", + "colExpiration": "Expiration", + "colPermissions": "Permissions", + "colPassword": "Password", + "colActions": "Actions", + "emptyStateTitle": "No shared resources yet", + "emptyStateDesc": "When you share files or folders, they will appear here", + "goToFiles": "Go to Files", + "typeFile": "File", + "typeFolder": "Folder", + "noExpiration": "No expiration", + "hasPassword": "Yes", + "noPassword": "No", + "editShare": "Edit Share", + "notifyShare": "Notify Someone", + "copyLink": "Copy Link", + "removeShare": "Remove Share", + "linkCopied": "Link copied to clipboard!", + "linkCopyFailed": "Failed to copy link", + "itemUpdated": "Share settings updated successfully", + "itemRemoved": "Share removed successfully", + "invalidEmail": "Please enter a valid email address", + "notificationSent": "Notification sent successfully", + "notificationFailed": "Failed to send notification", + "shared_backToFiles": "Back to Files", + "shared_pageTitle": "Shared Resources", + "shared_pageDescription": "Manage your shared files and folders", + "shared_filterType": "Type:", + "shared_filterAll": "All", + "shared_filterFiles": "Files", + "shared_filterFolders": "Folders", + "shared_sortBy": "Sort by:", + "shared_sortByName": "Name", + "shared_sortByDate": "Date shared", + "shared_sortByExpiration": "Expiration", + "shared_search": "Search", + "shared_colName": "Name", + "shared_colType": "Type", + "shared_colDateShared": "Date Shared", + "shared_colExpiration": "Expiration", + "shared_colPermissions": "Permissions", + "shared_colPassword": "Password", + "shared_colActions": "Actions", + "shared_emptyStateTitle": "No shared resources yet", + "shared_emptyStateDesc": "When you share files or folders, they will appear here", + "shared_goToFiles": "Go to Files", + "shared_typeFile": "File", + "shared_typeFolder": "Folder", + "shared_noExpiration": "No expiration", + "shared_hasPassword": "Yes", + "shared_noPassword": "No", + "shared_editShare": "Edit Share", + "shared_notifyShare": "Notify Someone", + "shared_copyLink": "Copy Link", + "shared_removeShare": "Remove Share", + "shared_linkCopied": "Link copied to clipboard!", + "shared_linkCopyFailed": "Failed to copy link", + "shared_itemUpdated": "Share settings updated successfully", + "shared_itemRemoved": "Share removed successfully", + "shared_invalidEmail": "Please enter a valid email address", + "shared_notificationSent": "Notification sent successfully", + "shared_notificationFailed": "Failed to send notification" + }, + "files": { + "name": "Name", + "type": "Type", + "size": "Size", + "modified": "Modified", + "no_files": "No files in this folder", + "empty_hint": "Upload files or create folders to get started", + "loading": "Loading files…", + "view_grid": "Grid view", + "view_list": "List view", + "file_types": { + "document": "Document", + "image": "Image", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Text", + "folder": "Folder", + "spreadsheet": "Spreadsheet", + "presentation": "Presentation", + "archive": "Archive", + "installer": "Installer", + "code": "Code" + }, + "owner": "Owner", + "add_favorites": "Add to favorites", + "added_favorites": "Added to favorites", + "already_favorites": "All selected items are already favorites", + "batch_delete": "Delete selected", + "breadcrumb": "Breadcrumb", + "cancel_selection": "Cancel selection", + "col_modified": "Date", + "col_name": "Name", + "col_owner": "Owner", + "col_path": "Location", + "col_size": "Size", + "col_type": "Type", + "confirm_batch_delete": "Move {{n}} items to trash?", + "confirm_delete": "Move \"{{name}}\" to trash?", + "confirm_delete_n": "Delete {{count}} item(s)?", + "copied": "Copied", + "copy": "Copy", + "copy_here": "Copy here", + "copy_n": "Copy {{n}} items", + "copy_title": "Copy “{{name}}”", + "download_zip": "Download as ZIP", + "edit": "Edit", + "edit_new_tab": "Edit in new tab", + "editor": "Document editor", + "empty_title": "This folder is empty", + "favorite": "Add favorite", + "favorited": "Favorite", + "file": "File", + "folder": "Folder", + "grid": "Grid", + "list": "List", + "more_actions": "More actions", + "move": "Move", + "move_here": "Move here", + "move_n": "Move {{n}} items", + "move_title": "Move “{{name}}”", + "moved": "Moved", + "new_folder": "New folder", + "new_folder_prompt": "New folder name", + "no_home": "No home folder available.", + "no_preview": "No preview available for this file type.", + "no_subfolders": "No subfolders here.", + "open": "Open", + "open_parent": "Open parent folder", + "owner_me": "Me", + "preview_failed": "Could not load preview.", + "select_all": "Select all", + "selected_count": "{{count}} selected", + "selection": "Selection", + "share": "Share", + "shared": "Shared", + "unfavorite": "Remove favorite", + "uploaded": "Upload complete", + "uploaded_saved": "Upload complete — {{mb}} MB deduplicated", + "uploading": "Uploading…", + "uploading_file": "Uploading {{name}}…", + "uploading_n": "Uploading {{done}}/{{total}} files…", + "view": "View" + }, + "dialogs": { + "rename_folder": "Rename folder", + "rename_file": "Rename file", + "new_name": "New name", + "new_folder_title": "New folder", + "folder_name": "Folder name", + "folder_placeholder": "My folder", + "rename_title": "Rename", + "move_file": "Move file", + "move_folder": "Move folder", + "select_destination": "Select destination folder:", + "select_this_folder": "Select this folder", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "root": "Root", + "delete_confirmation": "Are you sure you want to delete", + "and_contents": "and all its contents", + "no_undo": "This action cannot be undone", + "confirm_title": "Confirm action", + "confirm_delete": "Move to trash", + "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", + "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", + "confirm_permanent_delete": "Delete permanently", + "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", + "confirm_empty_trash": "Empty trash", + "confirm_delete_share": "Delete share link", + "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", + "share_file": "Share File", + "share_folder": "Share Folder", + "existing_shares": "Existing Shares", + "share_options": "Share Options", + "password": "Password", + "expiration": "Expiration", + "permissions": "Permissions", + "generated_link": "Generated Link", + "notify": "Send Notification", + "recipient": "Recipient", + "message": "Message", + "move_to_home": "Move to Home folder" + }, + "dropzone": { + "drag_files": "Drag files here or click to select", + "drop_files": "Drop files to upload" + }, + "permissions": { + "read": "Read", + "write": "Write", + "reshare": "Reshare" + }, + "errors": { + "file_not_found": "File not found", + "folder_not_found": "Folder not found", + "delete_error": "Error deleting", + "upload_error": "Error uploading file", + "rename_error": "Error renaming", + "move_error": "Error moving", + "empty_name": "Name cannot be empty", + "name_exists": "A file or folder with that name already exists", + "generic_error": "An error has occurred", + "group_name_invalid": "Group name must match the email-prefix format (letters, digits, dot, dash, underscore; 1–64 chars).", + "group_cycle": "This member would create a circular group reference.", + "group_depth_exceeded": "This nesting depth exceeds the maximum allowed (8).", + "group_virtual_immutable": "The 'Internal' group is system-managed and cannot be modified.", + "group_not_found": "Group not found.", + "group_name_taken": "A group with this name already exists.", + "forbidden": "Could not load files" + }, + "breadcrumb": { + "home": "Home" + }, + "trash": { + "empty_trash": "Empty Trash", + "empty_state": "Trash is empty", + "original_location": "Original location", + "deleted_date": "Deletion date", + "remaining": "Remaining", + "actions": "Actions", + "restore": "Restore", + "delete_permanently": "Delete permanently", + "empty_confirm": "Are you sure you want to empty the trash? This will permanently delete all items.", + "groupby": { + "remaining_days": "Remaining days", + "trashed_time": "Trashed time" + }, + "confirm_delete": "Permanently delete this item? This cannot be undone.", + "confirm_empty": "Empty the trash? This cannot be undone.", + "delete": "Delete permanently", + "empty_action": "Empty trash", + "restored": "Restored" + }, + "daysRemaining": { + "expired": "Expired", + "today": "Today", + "tomorrow": "Tomorrow", + "inDays": "{{count}} days" + }, + "expiryChip": { + "never": "Never expires", + "expired": "Expired", + "today": "Expires today", + "tomorrow": "Expires tomorrow", + "inDays": "Expires in {{count}} days", + "onDate": "Expires {{date}}" + }, + "auth": { + "login_title": "Sign in", + "username": "Username", + "username_placeholder": "Enter your username", + "login_identifier": "Username or email", + "login_identifier_placeholder": "Enter your username or email", + "password": "Password", + "password_placeholder": "Enter your password", + "login_button": "Sign in", + "no_account": "Don't have an account?", + "register": "Sign up", + "admin_setup": "First time?", + "setup": "Setup administrator", + "register_title": "Create account", + "email": "Email", + "email_placeholder": "Enter your email", + "confirm_password": "Confirm password", + "confirm_password_placeholder": "Confirm your password", + "register_button": "Create account", + "have_account": "Already have an account?", + "login": "Sign in", + "setup_title": "Initial setup", + "setup_step1": "Admin", + "setup_step2": "System", + "setup_step3": "Complete", + "admin_username": "Admin username", + "admin_email": "Admin email", + "admin_password": "Admin password", + "create_admin": "Create administrator", + "back_to_login": "Already set up?", + "admin_success": "Administrator account created successfully! You can now sign in.", + "account_success": "Account created successfully! You can now sign in.", + "passwords_mismatch": "Passwords do not match", + "admin_create_error": "Error creating administrator account", + "or": "or", + "sso_login": "Sign in with SSO", + "sso_login_provider": "Sign in with {{provider}}", + "magicLinkHint": "No password? Enter your email and we'll send you a one-time sign-in link.", + "magicLinkEmailLabel": "Email address", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "Send sign-in link", + "magicLinkSent": "If an account exists for that email, a sign-in link has been sent. Check your inbox.", + "magicLinkUnavailable": "Sign-in by email is not available on this server.", + "magicLinkNetworkError": "Could not reach the server: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "cookie_rejected": "Login succeeded but the browser rejected the session cookie. If you are on HTTP, set OXICLOUD_COOKIE_SECURE=false or use HTTPS.", + "login_error": "Error logging in", + "magic_email_label": "Email address", + "magic_error": "Something went wrong. Try again.", + "magic_hint": "No password? Enter your email and we'll send you a one-time sign-in link.", + "magic_prompt": "No password? Sign in with an email link", + "magic_send": "Send link", + "magic_sent": "If an account exists, a sign-in link has been sent. Check your inbox.", + "magic_unavailable": "Sign-in by email is not available on this server.", + "passwords_match": "Passwords match", + "register_error": "Registration failed", + "session_expired": "Your session expired. Please sign in again.", + "sign_in": "Sign in", + "signing_in": "Signing in…", + "toggle_password": "Show password" + }, + "storage": { + "title": "Storage", + "calculating": "Calculating...", + "used": "{{percentage}}% used ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "This file type cannot be previewed.", + "download_file": "Download file", + "zoom_in": "Zoom in", + "zoom_out": "Zoom out", + "zoom_reset": "Reset zoom", + "zoom": "Zoom" + }, + "language_selector": { + "title": "Welcome!", + "subtitle": "Select your language to continue", + "continue": "Continue", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "No favorites yet", + "empty_hint": "Star files or folders to add them to your favorites", + "add": "Add to favorites", + "remove": "Remove from favorites", + "added_title": "Added to favorites", + "added_msg": "added to favorites", + "removed_title": "Removed from favorites", + "removed_msg": "removed from favorites" + }, + "recent": { + "title": "Recent", + "clear": "Clear recent", + "accessed": "Accessed", + "empty_state": "No recent files", + "empty_hint": "Files you open will appear here", + "loadMore": "Load more", + "confirm_clear": "Clear your recent items?" + }, + "notifications": { + "file_renamed": "File renamed", + "file_renamed_to": "File renamed to \"{{name}}\"", + "folder_renamed": "Folder renamed", + "folder_renamed_to": "Folder renamed to \"{{name}}\"", + "file_uploaded": "File uploaded", + "file_deleted": "File moved to trash", + "folder_deleted": "Folder moved to trash", + "item_deleted_permanently": "Item permanently deleted", + "trash_emptied": "Trash emptied successfully", + "title": "Notifications", + "empty": "No notifications", + "link_created": "Link created", + "share_success": "Shared link created successfully", + "upload_files_section_title": "Upload not available here", + "upload_files_section_body": "Go to the Files section to upload files", + "clear": "Clear all" + }, + "batch": { + "one_selected": "1 item selected", + "n_selected": "{{count}} items selected", + "confirm_delete": "Are you sure you want to move {{count}} items to trash?", + "move_title": "Move {{count}} item(s)", + "add_favorites": "Add to favorites", + "move_copy": "Move or copy" + }, + "admin": { + "page_title": "Admin Panel", + "back_to_app": "Back to OxiCloud", + "loading": "Loading…", + "access_denied": "Access Denied", + "access_denied_desc": "Administrator privileges required to access this panel.", + "sign_in": "Sign in", + "tab_dashboard": "Dashboard", + "tab_users": "Users", + "tab_oidc": "SSO / OIDC", + "total_users": "Total Users", + "active_users": "Active Users", + "admins": "Admins", + "version": "Version", + "storage_overview": "Storage Overview", + "used": "Used", + "total_quota": "Total Quota", + "usage_pct": "Usage %", + "users_over_80": "Users >80% quota", + "users_over_quota": "Users over quota", + "system": "System", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quotas", + "enabled": "Enabled", + "disabled": "Disabled", + "active": "Active", + "off": "Off", + "allow_registration": "Allow public self-registration", + "registration_warning": "Public registration is disabled. Only admins can create new users.", + "user_management": "User Management", + "create_user": "Create User", + "col_user": "User", + "col_role": "Role", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Storage", + "col_last_login": "Last Login", + "col_actions": "Actions", + "loading_users": "Loading users…", + "failed_load_users": "Failed to load users", + "no_users_found": "No users found", + "showing_users": "Showing {{from}}-{{to}} of {{total}}", + "prev": "Prev", + "next": "Next", + "inactive": "Inactive", + "you_badge": "(you)", + "local": "Local", + "never": "Never", + "just_now": "Just now", + "minutes_ago": "{{n}}m ago", + "hours_ago": "{{n}}h ago", + "days_ago": "{{n}}d ago", + "edit_quota_title": "Edit quota", + "reset_password_title": "Reset password", + "toggle_role_title": "Toggle role", + "deactivate_title": "Deactivate", + "activate_title": "Activate", + "delete_title": "Delete", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "Enable SSO Authentication", + "provider_name": "Provider Name", + "issuer_url": "Issuer URL", + "issuer_url_hint": "OpenID Connect issuer URL of your identity provider", + "auto_discover": "Auto-discover", + "discovering": "Discovering…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Leave empty to keep current value", + "secret_configured": "A client secret is already configured", + "callback_url": "Callback URL", + "callback_url_hint": "(register in your IdP)", + "advanced_settings": "Advanced Settings", + "scopes": "Scopes", + "auto_provision": "Auto-provision users on first login", + "admin_groups": "Admin Groups", + "admin_groups_hint": "Comma-separated OIDC group names that map to admin role", + "disable_password": "Disable password login (OIDC only)", + "password_warning": "This will prevent ALL password-based logins!", + "test_btn": "Test", + "save_btn": "Save", + "saving": "Saving…", + "settings_saved": "Settings saved — OIDC is now {{status}}", + "quota_modal_title": "Update Storage Quota", + "quota_user_label": "User:", + "new_quota": "New Quota", + "quota_unlimited_hint": "Set to 0 for unlimited", + "cancel": "Cancel", + "create_user_title": "Create New User", + "username_label": "Username", + "username_placeholder": "johndoe", + "username_hint": "3–32 characters", + "password_label": "Password", + "password_placeholder": "Min 8 characters", + "email_label": "Email", + "email_optional": "(optional)", + "email_placeholder": "user@example.com (auto-generated if empty)", + "role_label": "Role", + "role_user": "User", + "role_admin": "Admin", + "quota_label": "Quota", + "creating": "Creating…", + "reset_pw_title": "Reset Password", + "new_password_label": "New Password", + "resetting": "Resetting…", + "reset_btn": "Reset", + "confirm_role_change": "Change role to {{role}}?", + "confirm_deactivate": "Are you sure you want to deactivate this user?", + "confirm_activate": "Are you sure you want to activate this user?", + "confirm_delete_user": "DELETE user \"{{name}}\"? This cannot be undone!", + "confirm_action": "Confirm Action", + "confirm_yes": "Confirm", + "confirm_no": "Cancel", + "error_username_short": "Username must be at least 3 characters", + "error_password_short": "Password must be at least 8 characters", + "error_generic": "Failed", + "error_network": "Network error: {{message}}", + "error_create_user": "Failed to create user", + "tab_storage": "Storage", + "storage_title": "Storage Backend", + "storage_current_backend": "Active Backend", + "storage_total_blobs": "Total Blobs", + "storage_total_size": "Total Size", + "storage_dedup_ratio": "Dedup Ratio", + "storage_backend": "Backend Type", + "storage_local": "Local Filesystem", + "storage_s3": "S3-Compatible", + "storage_provider_preset": "Provider Preset", + "storage_preset_custom": "Custom", + "storage_endpoint_url": "Endpoint URL", + "storage_endpoint_hint": "Leave empty for Amazon S3 default", + "storage_bucket": "Bucket", + "storage_region": "Region", + "storage_access_key": "Access Key ID", + "storage_secret_key": "Secret Access Key", + "storage_secret_configured": "A secret key is already configured", + "storage_key_placeholder": "Leave empty to keep current value", + "storage_path_style": "Force Path Style", + "storage_path_style_hint": "Required for MinIO and some S3-compatible providers", + "storage_test_connection": "Test Connection", + "storage_test_success": "Connection successful", + "storage_test_failure": "Connection failed", + "storage_save": "Save", + "storage_saved": "Storage settings saved successfully", + "storage_migration": "Backend Migration", + "storage_migration_coming_soon": "Backend migration will be available in a future update.", + "migration_status_label": "Status:", + "migration_start": "Start Migration", + "migration_pause": "Pause", + "migration_resume": "Resume", + "migration_verify": "Verify Integrity", + "migration_complete": "Finalize", + "migration_started": "Migration started", + "migration_paused_msg": "Migration paused", + "migration_resumed_msg": "Migration resumed", + "migration_completed_msg": "Migration finalized. Restart the server to use the new backend.", + "migration_verifying": "Verifying…", + "migration_verify_passed": "Verification passed", + "migration_verify_failed": "Verification failed", + "migration_failed_blobs": "failed blobs", + "testing": "Testing…", + "tab_plugins": "Plugins", + "plugins_title": "Plugins", + "plugins_disabled": "Plugins are disabled on this server. Set OXICLOUD_ENABLE_PLUGINS=true (and build with the \"plugins\" feature) to manage WASM plugins here.", + "plugins_install_title": "Install a plugin", + "plugins_install_intro": "Upload a plugin bundle (.zip) containing plugin.toml and its compiled WebAssembly module (.wasm). The manifest is validated and the module is probed before installation.", + "plugins_bundle_label": "Plugin bundle (.zip)", + "plugins_install": "Install plugin", + "plugins_installed_title": "Installed plugins", + "plugins_col_name": "Name", + "plugins_col_id": "ID", + "plugins_col_version": "Version", + "plugins_col_events": "Events", + "plugins_col_status": "Status", + "plugins_col_actions": "Actions", + "plugins_loading": "Loading plugins…", + "plugins_none": "No plugins installed.", + "plugins_enabled": "Enabled", + "plugins_disabled_badge": "Disabled", + "plugins_enable": "Enable", + "plugins_disable": "Disable", + "plugins_delete": "Delete", + "plugins_confirm_delete": "Delete plugin \"{{name}}\"? Its files will be removed from the server.", + "plugins_installing": "Installing…", + "plugins_installed": "Installed {{name}}.", + "plugins_install_missing_bundle": "Select a plugin bundle (.zip).", + "plugins_details": "Logs & details", + "plugins_back": "Back to plugins", + "plugins_retention_title": "Log retention", + "plugins_retention_intro": "Rotated log segments older than the retention window, or beyond the size cap, are pruned on a schedule.", + "plugins_retention_days": "Retention (days)", + "plugins_retention_max_mb": "Max log size (MB)", + "plugins_retention_save": "Save retention", + "plugins_retention_saved": "Retention saved.", + "plugins_retention_invalid": "Enter non-negative numbers.", + "plugins_logs_title": "Logs", + "plugins_logs_level_all": "All levels", + "plugins_logs_search": "Search messages…", + "plugins_logs_live": "Live", + "plugins_logs_clear": "Clear", + "plugins_logs_confirm_clear": "Clear all logs for this plugin?", + "plugins_logs_none": "No log entries.", + "plugins_logs_col_time": "Time", + "plugins_logs_col_level": "Level", + "plugins_logs_col_kind": "Kind", + "plugins_logs_col_invocation": "Invocation", + "plugins_logs_col_message": "Message", + "plugins_logs_showing": "Showing {{from}}–{{to}} of {{total}}", + "tab_smtp": "SMTP", + "smtp_title": "Outbound Email (SMTP)", + "smtp_intro": "SMTP is configured exclusively via environment variables (OXICLOUD_SMTP_*). The values below are read from the running server — to change them, edit the environment and restart OxiCloud.", + "smtp_enabled_label": "Status", + "smtp_enabled": "Enabled", + "smtp_disabled": "Disabled (host unset)", + "smtp_test_title": "Send a test email", + "smtp_test_intro": "Sends a hardcoded diagnostic message to the recipient below and reports the SMTP server's response so you can correlate it with your relay logs.", + "smtp_test_to": "Recipient address", + "smtp_send_test": "Send test email", + "smtp_sending": "Sending…", + "smtp_sent": "Test email sent.", + "smtp_send_failed": "Send failed.", + "smtp_server_code": "Server replied", + "smtp_test_missing_to": "Enter a recipient address.", + "smtp_not_configured": "SMTP is not configured on this server.", + "admin_users": "Admins", + "auth": "Authentication", + "available": "available", + "confirm_delete_plugin": "Delete plugin {{name}}?", + "confirm_role": "Change role to {{role}}?", + "dashboard": "Dashboard", + "disable": "Disable", + "email": "Email", + "email_auto": "Auto-generated if left blank", + "enable": "Enable", + "env_locked": "Set by an environment variable", + "last_login": "Last login", + "logs_all": "All levels", + "logs_empty": "No log entries.", + "logs_invocation": "Invocation", + "logs_kind": "Kind", + "logs_level": "Level", + "logs_live": "Live", + "logs_message": "Message", + "logs_search": "Search…", + "logs_showing": "Showing {{from}}–{{to}} of {{total}}", + "logs_time": "Time", + "mig_complete": "Finalize", + "mig_eta": "~{{min}} min remaining", + "mig_failed": "{{n}} failed blobs", + "mig_pause": "Pause", + "mig_resume": "Resume", + "mig_start": "Start", + "mig_verify": "Verify integrity", + "mig_verify_failed": "Verification failed", + "mig_verify_mismatch": "{{n}} size mismatches", + "mig_verify_missing": "{{n}} missing", + "mig_verify_passed": "Verification passed", + "mig_verify_summary": "{{checked}} blobs checked, {{total}} total in database", + "mig_verifying": "Verifying…", + "migration": "Storage migration", + "new_password": "New password", + "no_plugins": "No plugins installed.", + "oidc": "OIDC / SSO", + "oidc_admin_groups": "Admin groups", + "oidc_auth_endpoint": "Auth endpoint", + "oidc_auto_provision": "Auto-provision users on first login", + "oidc_callback": "Callback URL", + "oidc_client_id": "Client ID", + "oidc_client_secret": "Client secret", + "oidc_disable_pw": "Disable password login (OIDC only)", + "oidc_discover": "Test / discover", + "oidc_enabled": "Enable OIDC login", + "oidc_issuer": "Issuer URL", + "oidc_provider_name": "Provider name", + "oidc_scopes": "Scopes", + "oidc_secret_set": "A client secret is already configured.", + "over_80": "{{n}} users over 80% quota", + "over_quota": "{{n}} users over quota", + "password": "Password", + "password_reset": "Password reset", + "plugin": "Plugin", + "plugin_logs": "Plugin logs", + "plugins": "Plugins", + "plugins_clear_logs": "Clear logs", + "plugins_install_hint": "Upload a plugin bundle (.zip).", + "plugins_retention": "Log retention", + "plugins_retention_max": "Max size (MB)", + "plugins_upload": "Upload .zip", + "quota": "Storage usage", + "quota_for": "Quota for", + "quotas": "Quotas", + "registration": "Registration", + "registration_disabled_warning": "Public registration is disabled. Only admins can create new accounts.", + "reset_pw_for": "New password for", + "role": "Role", + "settings_saved_ok": "Settings saved.", + "smtp": "Email (SMTP)", + "smtp_fail": "Send failed.", + "smtp_from": "From", + "smtp_host": "Host", + "smtp_port": "Port", + "smtp_send": "Send", + "smtp_status": "SMTP status", + "smtp_test": "Send test email", + "smtp_to": "recipient@example.com", + "smtp_user_state": "Auth", + "status": "Status", + "storage": "Storage", + "storage_blobs": "Blobs", + "storage_current": "Current backend", + "storage_dedup": "Dedup ratio", + "storage_endpoint": "Endpoint URL", + "storage_preset": "Preset", + "storage_size": "Stored", + "storage_tab": "Storage", + "storage_test": "Test connection", + "time_day_ago": "{{n}} d ago", + "time_hour_ago": "{{n}} h ago", + "time_just_now": "just now", + "time_min_ago": "{{n}} min ago", + "title": "Admin", + "unchanged": "Leave blank to keep current", + "user": "User", + "username": "Username", + "users": "Users" + }, + "profile": { + "page_title": "Profile", + "back_to_app": "Back to OxiCloud", + "loading": "Loading…", + "not_authenticated": "Not Authenticated", + "not_authenticated_desc": "Please sign in to view your profile.", + "sign_in": "Sign in", + "role_admin": "Administrator", + "role_user": "User", + "account_details": "Account Details", + "username": "Username", + "email": "Email", + "role": "Role", + "last_login": "Last Login", + "storage": "Storage", + "used": "Used", + "quota": "Quota", + "usage": "Usage", + "unlimited": "Unlimited", + "app_passwords": "App Passwords", + "app_pw_desc": "Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.", + "app_pw_label_placeholder": "Label (e.g. Thunderbird, macOS)", + "generate": "Generate", + "generating": "Generating…", + "new_password_for": "New password for", + "copy_warning": "Copy this password now. You won't be able to see it again.", + "copy_to_clipboard": "Copy to clipboard", + "col_label": "Label", + "col_created": "Created", + "col_last_used": "Last Used", + "col_status": "Status", + "active": "Active", + "revoked": "Revoked", + "revoke_title": "Revoke", + "no_app_passwords": "No app passwords yet.", + "client_sessions": "Client sessions", + "client_sessions_desc": "Auto-generated when you connect a Nextcloud-compatible client.", + "col_client": "Client", + "never": "Never", + "just_now": "Just now", + "minutes_ago": "{{n}} min ago", + "hours_ago": "{{n}}h ago", + "days_ago": "{{n}} days ago", + "edit_profile": "Edit Profile", + "edit_oidc_managed": "To change your information (name, first name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.", + "username_claim_hint": "2–64 characters, letters/digits/dot/dash/underscore. Once chosen, the username can't be changed (DAV/NextCloud clients depend on it).", + "username_already_claimed": "Username is set and can't be changed (DAV/NextCloud clients depend on it).", + "given_name": "First name", + "family_name": "Last name", + "notify_on_share": "Email me when someone shares with me", + "notify_on_share_hint": "When unchecked, shares still appear in your account — you just won't get an email about them.", + "save_profile": "Save changes", + "profile_saved": "Profile updated", + "profile_no_changes": "No changes to save.", + "profile_save_failed": "Save failed", + "username_taken_error": "That username is already taken.", + "username_immutable_error": "Your username is already set and can't be changed here. Contact an administrator if you need a rename.", + "change_password": "Change Password", + "current_password": "Current Password", + "new_password": "New Password", + "min_8_chars": "At least 8 characters", + "confirm_password": "Confirm New Password", + "update_password": "Update Password", + "updating": "Updating…", + "password_updated": "Password updated successfully", + "passwords_no_match": "Passwords do not match", + "password_too_short": "Password must be at least 8 characters", + "password_change_failed": "Failed to change password", + "error_network": "Network error: {{message}}", + "error_label_required": "Please enter a label", + "error_create_pw": "Failed to create app password", + "confirm_revoke": "Revoke app password \"{{label}}\"? Clients using this password will stop working.", + "error_revoke": "Failed to revoke app password", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "app_pw_revoke": "Revoke app password", + "avatar": "Avatar", + "copied": "Copied", + "copy_failed": "Could not copy", + "language": "Language", + "language_auto": "Automatic", + "password_mismatch": "Passwords do not match", + "saved": "Profile saved" + }, + "upload": { + "uploading": "Uploading...", + "files": "files", + "complete": "{{count}} / {{total}} uploaded", + "files_counter": "{{completed}} / {{total}} files" + }, + "storage_quota_exceeded": "Storage quota exceeded", + "sharedwithme": { + "pageTitle": "Shared with me", + "pageDescription": "Files and folders others have shared with you", + "emptyStateTitle": "Nothing shared with you yet", + "emptyStateDesc": "Items shared with you by other users will appear here", + "loadMore": "Load more", + "sharedBy": "Shared by", + "colName": "Name", + "colType": "Type", + "colSharedBy": "Shared by", + "colDate": "Date shared", + "colPermissions": "Permissions" + }, + "groupby": { + "none": "None", + "byFiles": "By files", + "sharedWith": "Shared with", + "title": "Group by", + "type": "Type", + "type.folders": "Folders", + "owner": "Owner", + "shareDate": "Share date", + "favoriteDate": "Favorite date", + "accessedAt": "Accessed date", + "modifiedAt": "Modified date", + "createdAt": "Created date", + "size": "Size", + "justAdded": "New", + "folders": "Folders" + }, + "dateBucket": { + "today": "Today", + "last7days": "Last 7 days", + "last30days": "Last 30 days", + "unknown": "Unknown" + }, + "groups": { + "title": "Manage groups", + "create_button": "Create group", + "create_dialog_title": "New group", + "edit_dialog_title": "Rename group", + "name_label": "Name", + "name_placeholder": "engineering", + "description_label": "Description (optional)", + "members_section": "Members", + "members_loading": "Loading members…", + "members_empty": "No members", + "add_member_placeholder": "Add a user or group…", + "no_members": "No members yet.", + "remove_member": "Remove", + "delete_group": "Delete group", + "delete_confirm": "Delete the group \"{name}\"? Grants referencing this group will be revoked.", + "empty_state": "No groups yet.", + "load_more": "Load more", + "back_to_list": "Back", + "loading": "Loading…", + "virtual_badge": "System", + "member_count_zero": "no members", + "member_count_one": "1 member", + "member_count_other": "{count} members", + "delete_confirm_label": "Type the group name to confirm:", + "delete_confirm_mismatch": "Type the group name exactly to confirm.", + "virtual_internal_name": "Internal", + "virtual_internal_explanation": "Every internal user on this server", + "add_member_search": "Search users or groups to add…", + "create": "Create group", + "empty": "No groups yet.", + "members": "Members", + "nested": "Group" + }, + "sort": { + "asc": "ascending", + "desc": "descending", + "direction": "Sort direction" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "about": { + "description": "OxiCloud — a fast, self-hosted file storage and sync server." + }, + "category": { + "archives": "Archives", + "audio": "Audio", + "code": "Code", + "documents": "Documents", + "images": "Images", + "installers": "Installers", + "markdown": "Markdown", + "presentations": "Presentations", + "spreadsheets": "Spreadsheets", + "text": "Text", + "videos": "Videos" + }, + "cmdk": { + "no_results": "No matching commands", + "placeholder": "Type a command or search…", + "title": "Command palette", + "toggle_theme": "Toggle theme" + }, + "common": { + "add": "Add", + "cancel": "Cancel", + "clear": "Clear", + "close": "Close", + "confirm": "Confirm", + "copied": "Copied to clipboard", + "copy": "Copy", + "copy_failed": "Copy failed", + "create": "Create", + "delete": "Delete", + "download": "Download", + "empty": "Nothing here yet.", + "error": "unknown error", + "favorite": "Favorite", + "load_more": "Load more", + "loading": "Loading…", + "next": "Next", + "no": "No", + "ok": "OK", + "optional": "optional", + "previous": "Previous", + "remove": "Remove", + "rename": "Rename", + "retry": "Try Again", + "save": "Save", + "search": "Search", + "select": "Select", + "select_all": "Select all", + "yes": "Yes", + "dismiss": "Dismiss" + }, + "device": { + "approve": "Approve", + "approved": "Device approved. You can return to your device.", + "client": "Application", + "continue": "Continue", + "denied": "Device access denied.", + "deny": "Deny", + "enter_code": "Enter the code shown on your device", + "lookup_failed": "Failed to verify code. Please try again.", + "not_found": "Code not found or expired. Please check and try again.", + "scopes": "Access", + "title": "Device verification", + "unauthorized": "You must be logged in to authorize a device. Please log in first.", + "unknown": "Unknown" + }, + "errors_loadFailed": "Failed to load items", + "expiryBucket": { + "expired": "Expired", + "later": "Later", + "month": "In less than 30 days", + "noExpiry": "No expiration", + "today": "Today", + "tomorrow": "Tomorrow", + "week": "In less than 7 days" + }, + "nextcloud": { + "close_window": "Close Window", + "error_expired_body": "Your session has expired. Please try again.", + "error_expired_title": "Session Expired", + "error_generic_body": "An unexpected error occurred. Please try again.", + "error_invalid_body": "Invalid username or password. Please check your credentials and try again.", + "error_invalid_title": "Login Failed", + "error_notfound_body": "The requested page was not found.", + "error_notfound_title": "Not Found", + "error_title": "Error", + "grant": "Grant access", + "grant_subtitle": "A Nextcloud client is requesting access to your account.", + "grant_title": "Grant access", + "invalid_token": "Invalid session token.", + "sign_in_with": "Sign in with {{provider}}", + "success_body": "You can now return to your application — it is connected.", + "success_title": "Access granted" + }, + "search": { + "clear_filters": "Clear filters", + "date": { + "all": "Any time", + "day": "Past 24 hours", + "month": "Past month", + "week": "Past week", + "year": "Past year" + }, + "date_label": "Date", + "everywhere": "Everywhere", + "no_results": "No results found for this search", + "prompt": "Type a query in the search bar above.", + "results_for": "Results for “{{q}}”", + "scope": "Scope", + "searching_for": "Searching for “{{q}}”…", + "see_all": "See all results", + "size": { + "all": "Any size", + "large": "> 100 MB", + "medium": "1–100 MB", + "small": "< 1 MB" + }, + "size_label": "Size", + "sort": { + "largest": "Largest", + "name_asc": "Name A-Z", + "name_desc": "Name Z-A", + "newest": "Newest", + "oldest": "Oldest", + "relevance": "Relevance", + "smallest": "Smallest" + }, + "sort_by": "Sort by", + "this_folder": "This folder", + "title": "Search", + "type": { + "all": "All types", + "archive": "Archives", + "audio": "Audio", + "document": "Documents", + "image": "Images", + "video": "Videos" + }, + "type_label": "Type" + }, + "settings": { + "language": "Language" + }, + "shared_with_me": { + "empty": "Nothing has been shared with you yet.", + "from": "Shared by {{who}}" + }, + "sizeBucket": { + "empty": "Empty (0 B)", + "folders": "Folders", + "huge": "> 5 GB", + "large": "1 – 5 GB", + "medium": "100 MB – 1 GB", + "small": "1 – 100 MB", + "tiny": "< 1 MB" + }, + "sortdir": { + "title": "Sort direction" + }, + "view": { + "grid": "Grid view", + "label": "View options", + "list": "List view" + } } diff --git a/frontend/static/locales/es.json b/frontend/static/locales/es.json index 3a1027ea..6f69a5cc 100644 --- a/frontend/static/locales/es.json +++ b/frontend/static/locales/es.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Este enlace de inicio de sesión ya no es válido", - "expired_body": "Es posible que el enlace haya expirado o ya se haya utilizado. Podemos enviarte uno nuevo — llegará a tu bandeja de entrada en unos segundos.", - "resend_to": "Enviar un nuevo enlace a {{email}}", - "generic_unavailable": "Este enlace de inicio de sesión ya no es válido. Es posible que ya se haya usado o que haya expirado. Solicita uno nuevo desde la página de inicio de sesión.", - "service_unavailable": "El inicio de sesión por enlace mágico no está habilitado en este servidor.", - "internal_error": "Algo salió mal al iniciar sesión. Por favor, inténtalo de nuevo.", - "resend_failure": "Algo salió mal al enviar el enlace. Por favor, inténtalo de nuevo.", - "cross_browser_title": "¿Continuar el inicio de sesión en este dispositivo?", - "cross_browser_body": "Has abierto este enlace de inicio de sesión en un navegador o dispositivo diferente del que lo solicitó.", - "cross_browser_warning": "Si solicitaste este enlace, es seguro continuar. Si no, cierra esta página — hacer clic en Continuar iniciaría sesión a otra persona en tu cuenta.", - "cross_browser_continue": "Continuar e iniciar sesión", - "resend_confirmation_title": "Revisa tu bandeja de entrada", - "resend_confirmation_body": "Si el enlace de inicio de sesión pertenecía a una cuenta activa, se acaba de enviar uno nuevo. Por favor, revisa tu bandeja de entrada.", - "return_link": "Volver a OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", - "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nÁbrelo haciendo clic en el enlace de abajo:\n{{link}}\n\nEl enlace es de un solo uso y expira en {{ttl_hours}} horas.\nSi no esperabas esta invitación, puedes ignorar este mensaje.\n\n— OxiCloud" - }, - "login": { - "subject": "Inicia sesión en OxiCloud", - "body": "Hola,\n\nUsa el enlace de abajo para iniciar sesión en OxiCloud. El enlace es de un solo uso y expira en {{ttl_minutes}} minutos. Ábrelo en el mismo dispositivo donde lo solicitaste.\n\n{{link}}\n\nSi no solicitaste este enlace de inicio de sesión, puedes ignorar este mensaje — no se necesita ninguna acción adicional.\n\n— OxiCloud" - }, - "kind_file": "archivo", - "kind_folder": "carpeta", - "english_fallback_divider": "--- Versión en inglés a continuación ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Este enlace de inicio de sesión ya no es válido", + "expired_body": "Es posible que el enlace haya expirado o ya se haya utilizado. Podemos enviarte uno nuevo — llegará a tu bandeja de entrada en unos segundos.", + "resend_to": "Enviar un nuevo enlace a {{email}}", + "generic_unavailable": "Este enlace de inicio de sesión ya no es válido. Es posible que ya se haya usado o que haya expirado. Solicita uno nuevo desde la página de inicio de sesión.", + "service_unavailable": "El inicio de sesión por enlace mágico no está habilitado en este servidor.", + "internal_error": "Algo salió mal al iniciar sesión. Por favor, inténtalo de nuevo.", + "resend_failure": "Algo salió mal al enviar el enlace. Por favor, inténtalo de nuevo.", + "cross_browser_title": "¿Continuar el inicio de sesión en este dispositivo?", + "cross_browser_body": "Has abierto este enlace de inicio de sesión en un navegador o dispositivo diferente del que lo solicitó.", + "cross_browser_warning": "Si solicitaste este enlace, es seguro continuar. Si no, cierra esta página — hacer clic en Continuar iniciaría sesión a otra persona en tu cuenta.", + "cross_browser_continue": "Continuar e iniciar sesión", + "resend_confirmation_title": "Revisa tu bandeja de entrada", + "resend_confirmation_body": "Si el enlace de inicio de sesión pertenecía a una cuenta activa, se acaba de enviar uno nuevo. Por favor, revisa tu bandeja de entrada.", + "return_link": "Volver a OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", + "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nÁbrelo haciendo clic en el enlace de abajo:\n{{link}}\n\nEl enlace es de un solo uso y expira en {{ttl_hours}} horas.\nSi no esperabas esta invitación, puedes ignorar este mensaje.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", - "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nAbre OxiCloud para ver tu nuevo recurso compartido:\n{{login_link}}\n\nPuede que tengas más recursos compartidos nuevos de {{inviter}} — inicia sesión para ver todos tus elementos compartidos.\n\n— OxiCloud\n\nRecibes este mensaje porque tienes una cuenta de OxiCloud y la preferencia de notificación de recursos compartidos está activada. Puedes desactivarla en tu perfil (Enviarme un correo cuando alguien comparta conmigo)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Sistema de almacenamiento en la nube minimalista" - }, - "nav": { - "files": "Archivos", - "shared": "Compartidos", - "recent": "Recientes", - "favorites": "Favoritos", - "photos": "Fotos", - "music": "Música", - "trash": "Papelera", - "sharedwithme": "Compartidos conmigo" - }, - "photos": { - "empty_state": "Aún no hay fotos", - "empty_hint": "Sube imágenes o videos para verlos aquí", - "items_selected": "seleccionados", - "view_daily": "Día", - "view_monthly": "Mes", - "view_yearly": "Año" - }, - "music": { - "create_playlist": "Crear Lista", - "playlists": "Listas", - "no_playlists": "Sin listas aún", - "empty_hint": "Crea tu primera lista para empezar a organizar tu música", - "select_playlist": "Selecciona una lista", - "select_hint": "Elige una lista de la barra lateral o crea una nueva", - "add_tracks": "Añadir Pistas", - "no_tracks": "No hay pistas en esta lista", - "unknown_artist": "Artista Desconocido", - "unknown_title": "Desconocido", - "confirm_delete": "¿Eliminar esta lista?", - "playlist_name": "Nombre de la lista", - "create": "Crear", - "delete": "Eliminar", - "share": "Compartir", - "edit": "Editar", - "play_all": "Reproducir Todo", - "shuffle": "Aleatorio", - "repeat": "Repetir", - "repeat_one": "Repetir Una", - "queue": "Cola", - "queue_empty": "Cola vacía", - "not_playing": "No reproduciendo", - "play": "Reproducir", - "pause": "Pausar", - "previous": "Anterior", - "next": "Siguiente", - "volume": "Volumen", - "mute": "Silenciar", - "unmute": "Activar sonido", - "title": "Título", - "artist": "Artista", - "album": "Álbum", - "tracks": "pistas", - "add": "Añadir", - "added": "¡Añadido!", - "added_to_playlist": "añadido a la lista", - "add_to_playlist": "Añadir a playlist", - "load_error": "Error al cargar listas", - "add_error": "No se pudieron añadir las pistas", - "no_playlists_yet": "No hay listas aún. ¡Crea una primero!", - "selected_files": "Seleccionados:", - "share_with_user": "ID de usuario o email", - "playback_error": "Error de reproducción", - "error": "Error", - "remove": "Eliminar", - "track_removed": "Pista eliminada", - "manage_shares": "Gestionar compartidos", - "no_shares": "Sin compartidos aún", - "remove_share": "Eliminar compartido", - "can_write": "Puede editar", - "read_only": "Solo lectura", - "public": "Pública", - "private": "Privada", - "toggle_public": "Visibilidad", - "make_public": "Hacer pública", - "make_private": "Hacer privada", - "set_cover": "Establecer portada", - "cover_updated": "Portada actualizada", - "search_audio": "Buscar archivos de audio…", - "no_audio_files": "No se encontraron archivos de audio", - "selected": "seleccionados", - "loading": "Cargando…", - "search_error": "No se pudieron cargar los archivos de audio", - "adding": "Añadiendo…" - }, - "share": { - "dialogTitle": "Compartir Enlace", - "linkLabel": "Enlace compartido:", - "copyLink": "Copiar", - "permissions": "Permisos:", - "permissionRead": "Lectura", - "permissionWrite": "Escritura", - "permissionReshare": "Recompartir", - "password": "Protección con contraseña:", - "generatePassword": "Generar", - "expiration": "Fecha de caducidad:", - "update": "Actualizar compartido", - "remove": "Eliminar compartido", - "notifyTitle": "Enviar notificación", - "notifyEmailLabel": "Dirección de correo:", - "notifyMessageLabel": "Mensaje (opcional):", - "notifySend": "Enviar notificación", - "shareWithOthers": "Compartir con otros", - "sharePublicly": "Compartir públicamente", - "shareSettings": "Configuración de compartido", - "shareCopied": "Enlace copiado al portapapeles", - "shareCreated": "Enlace compartido creado correctamente", - "shareUpdated": "Configuración de compartido actualizada", - "shareRemoved": "Compartido eliminado correctamente", - "inviteByEmail": "Invitar por correo — se enviará una invitación", - "directoryUnavailable": "Directorio de usuarios no disponible", - "linkNamePlaceholder": "Nombre del enlace (opcional)", - "newLink": "Nuevo enlace", - "noExpiry": "Sin caducidad", - "pending": "Pendiente", - "people": "Personas", - "publicLinks": "Enlaces públicos", - "role": { - "canEdit": "Puede editar", - "canManage": "Puede gestionar", - "canView": "Puede ver" + "login": { + "subject": "Inicia sesión en OxiCloud", + "body": "Hola,\n\nUsa el enlace de abajo para iniciar sesión en OxiCloud. El enlace es de un solo uso y expira en {{ttl_minutes}} minutos. Ábrelo en el mismo dispositivo donde lo solicitaste.\n\n{{link}}\n\nSi no solicitaste este enlace de inicio de sesión, puedes ignorar este mensaje — no se necesita ninguna acción adicional.\n\n— OxiCloud" }, - "searchPlaceholder": "Buscar personas…", - "shareOf": "Compartir:", - "sharedLink": "Enlace compartido" + "kind_file": "archivo", + "kind_folder": "carpeta", + "english_fallback_divider": "--- Versión en inglés a continuación ---" + } }, - "share_dialogTitle": "Compartir Enlace", - "share_linkLabel": "Enlace compartido:", - "share_copyLink": "Copiar", - "share_permissions": "Permisos:", - "share_permissionRead": "Lectura", - "share_permissionWrite": "Escritura", - "share_permissionReshare": "Recompartir", - "share_password": "Protección con contraseña:", - "share_generatePassword": "Generar", - "share_expiration": "Fecha de caducidad:", - "share_update": "Actualizar compartido", - "share_remove": "Eliminar compartido", - "share_notifyTitle": "Enviar notificación", - "share_notifyEmailLabel": "Dirección de correo:", - "share_notifyMessageLabel": "Mensaje (opcional):", - "share_notifySend": "Enviar notificación", - "shared": { - "backToFiles": "Volver a Archivos", - "pageTitle": "Recursos Compartidos", - "pageDescription": "Administra tus archivos y carpetas compartidos", - "filterType": "Tipo:", - "filterAll": "Todos", - "filterFiles": "Archivos", - "filterFolders": "Carpetas", - "sortBy": "Ordenar por:", - "sortByName": "Nombre", - "sortByDate": "Fecha compartido", - "sortByExpiration": "Caducidad", - "search": "Buscar", - "colName": "Nombre", - "colType": "Tipo", - "colDateShared": "Fecha compartido", - "colExpiration": "Caducidad", - "colPermissions": "Permisos", - "colPassword": "Contraseña", - "colActions": "Acciones", - "emptyStateTitle": "Aún no hay recursos compartidos", - "emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", - "goToFiles": "Ir a Archivos", - "typeFile": "Archivo", - "typeFolder": "Carpeta", - "noExpiration": "Sin caducidad", - "hasPassword": "Sí", - "noPassword": "No", - "editShare": "Editar compartido", - "notifyShare": "Notificar a alguien", - "copyLink": "Copiar enlace", - "removeShare": "Eliminar compartido", - "linkCopied": "¡Enlace copiado al portapapeles!", - "linkCopyFailed": "Error al copiar el enlace", - "itemUpdated": "Configuración de compartido actualizada", - "itemRemoved": "Compartido eliminado correctamente", - "invalidEmail": "Por favor, introduce una dirección de correo válida", - "notificationSent": "Notificación enviada correctamente", - "notificationFailed": "Error al enviar la notificación", - "shared_backToFiles": "Volver a Archivos", - "shared_pageTitle": "Recursos Compartidos", - "shared_pageDescription": "Administra tus archivos y carpetas compartidos", - "shared_filterType": "Tipo:", - "shared_filterAll": "Todos", - "shared_filterFiles": "Archivos", - "shared_filterFolders": "Carpetas", - "shared_sortBy": "Ordenar por:", - "shared_sortByName": "Nombre", - "shared_sortByDate": "Fecha compartido", - "shared_sortByExpiration": "Caducidad", - "shared_search": "Buscar", - "shared_colName": "Nombre", - "shared_colType": "Tipo", - "shared_colDateShared": "Fecha compartido", - "shared_colExpiration": "Caducidad", - "shared_colPermissions": "Permisos", - "shared_colPassword": "Contraseña", - "shared_colActions": "Acciones", - "shared_emptyStateTitle": "Aún no hay recursos compartidos", - "shared_emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", - "shared_goToFiles": "Ir a Archivos", - "shared_typeFile": "Archivo", - "shared_typeFolder": "Carpeta", - "shared_noExpiration": "Sin caducidad", - "shared_hasPassword": "Sí", - "shared_noPassword": "No", - "shared_editShare": "Editar compartido", - "shared_notifyShare": "Notificar a alguien", - "shared_copyLink": "Copiar enlace", - "shared_removeShare": "Eliminar compartido", - "shared_linkCopied": "¡Enlace copiado al portapapeles!", - "shared_linkCopyFailed": "Error al copiar el enlace", - "shared_itemUpdated": "Configuración de compartido actualizada", - "shared_itemRemoved": "Compartido eliminado correctamente", - "shared_invalidEmail": "Por favor, introduce una dirección de correo válida", - "shared_notificationSent": "Notificación enviada correctamente", - "shared_notificationFailed": "Error al enviar la notificación" - }, - "actions": { - "search": "Buscar archivos...", - "new_folder": "Nueva carpeta", - "upload": "Subir", - "upload_files": "Subir archivos", - "upload_folder": "Subir carpeta", - "upload.uploading": "Subiendo...", - "upload.complete": "{count} / {total} subidos", - "upload.files": "archivos", - "rename": "Renombrar", - "move": "Mover a...", - "move_to": "Mover a", - "delete": "Eliminar", - "download": "Descargar", - "view": "Ver", - "cancel": "Cancelar", - "confirm": "Confirmar", - "share": "Compartir", - "favorite": "Añadir a favoritos", - "unfavorite": "Quitar de favoritos", - "copy": "Copiar", - "notify": "Notificar", - "send": "Enviar", - "clear_recent": "Limpiar recientes", - "logout": "Cerrar sesión", - "create": "Crear", - "search_btn": "Buscar", - "close": "Cerrar", - "delete_permanently": "Eliminar permanentemente", - "empty_trash": "Vaciar papelera", - "open_parent_folder": "Ir a la carpeta padre", - "add": "Añadir", - "apply": "Aplicar", - "clear": "Limpiar", - "remove": "Quitar" - }, - "user_menu": { - "appearance": "Apariencia", - "about": "Acerca de OxiCloud", - "about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada.", - "admin_panel": "Panel de administración", - "profile": "Mi perfil", - "role_user": "Usuario", - "theme": { - "light": "Claro", - "dark": "Oscuro", - "auto": "Como el sistema" - }, - "manage_groups": "Gestionar grupos" - }, - "files": { - "name": "Nombre", - "type": "Tipo", - "size": "Tamaño", - "modified": "Modificado", - "no_files": "No hay archivos en esta carpeta", - "empty_hint": "Sube archivos o crea carpetas para comenzar", - "loading": "Cargando archivos…", - "view_grid": "Vista de cuadrícula", - "view_list": "Vista de lista", - "file_types": { - "document": "Documento", - "image": "Imagen", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Texto", - "folder": "Carpeta", - "spreadsheet": "Hoja de cálculo", - "presentation": "Presentación", - "archive": "Archivo comprimido", - "installer": "Instalador", - "code": "Código" - }, - "owner": "Propietario" - }, - "dialogs": { - "rename_folder": "Renombrar carpeta", - "rename_file": "Renombrar archivo", - "new_name": "Nuevo nombre", - "new_folder_title": "Nueva carpeta", - "folder_name": "Nombre de la carpeta", - "folder_placeholder": "Mi carpeta", - "rename_title": "Renombrar", - "move_file": "Mover archivo", - "move_folder": "Mover carpeta", - "select_destination": "Selecciona la carpeta destino:", - "select_this_folder": "Seleccionar esta carpeta", - "go_to_parent": ".. (carpeta superior)", - "no_subfolders": "Sin subcarpetas", - "root": "Raíz", - "delete_confirmation": "¿Estás seguro de que quieres eliminar", - "and_contents": "y todo su contenido", - "no_undo": "Esta acción no se puede deshacer", - "confirm_title": "Confirmar acción", - "confirm_delete": "Mover a papelera", - "confirm_delete_file": "¿Estás seguro de que quieres mover a la papelera el archivo \"{{name}}\"?", - "confirm_delete_folder": "¿Estás seguro de que quieres mover a la papelera la carpeta \"{{name}}\" y todo su contenido?", - "confirm_permanent_delete": "Eliminar permanentemente", - "confirm_permanent_delete_msg": "¿Estás seguro de que quieres eliminar permanentemente este elemento? Esta acción no se puede deshacer.", - "confirm_empty_trash": "Vaciar papelera", - "confirm_delete_share": "Eliminar enlace compartido", - "confirm_delete_share_msg": "¿Estás seguro de que quieres eliminar este enlace compartido?", - "share_file": "Compartir Archivo", - "share_folder": "Compartir Carpeta", - "existing_shares": "Compartidos Existentes", - "share_options": "Opciones de Compartición", - "password": "Contraseña", - "expiration": "Caducidad", - "permissions": "Permisos", - "generated_link": "Enlace Generado", - "notify": "Enviar Notificación", - "recipient": "Destinatario", - "message": "Mensaje", - "move_to_home": "Mover a la carpeta de inicio" - }, - "dropzone": { - "drag_files": "Arrastra archivos aquí o haz clic para seleccionar", - "drop_files": "Suelta los archivos para subirlos" - }, - "permissions": { - "read": "Lectura", - "write": "Escritura", - "reshare": "Recompartir" - }, - "errors": { - "file_not_found": "Archivo no encontrado", - "folder_not_found": "Carpeta no encontrada", - "delete_error": "Error al eliminar", - "upload_error": "Error al subir el archivo", - "rename_error": "Error al renombrar", - "move_error": "Error al mover", - "empty_name": "El nombre no puede estar vacío", - "name_exists": "Ya existe un archivo o carpeta con ese nombre", - "generic_error": "Ha ocurrido un error", - "group_name_invalid": "El nombre del grupo debe seguir el formato de prefijo de correo (letras, dígitos, punto, guión, guion bajo; 1–64 caracteres).", - "group_cycle": "Este miembro creaería una referencia circular entre grupos.", - "group_depth_exceeded": "Esta profundidad de anidamiento excede el máximo permitido (8).", - "group_virtual_immutable": "El grupo «Internal» es gestionado por el sistema y no se puede modificar.", - "group_not_found": "Grupo no encontrado.", - "group_name_taken": "Ya existe un grupo con este nombre." - }, - "breadcrumb": { - "home": "Inicio" - }, - "trash": { - "empty_trash": "Vaciar papelera", - "empty_state": "La papelera está vacía", - "original_location": "Ubicación original", - "deleted_date": "Fecha de eliminación", - "remaining": "Restante", - "actions": "Acciones", - "restore": "Restaurar", - "delete_permanently": "Eliminar permanentemente", - "empty_confirm": "¿Estás seguro de que quieres vaciar la papelera? Esta acción eliminará permanentemente todos los elementos.", - "groupby": { - "remaining_days": "Días restantes", - "trashed_time": "Fecha de eliminación" - } - }, - "daysRemaining": { - "expired": "Caducado", - "today": "Hoy", - "tomorrow": "Mañana", - "inDays": "{{count}} días" - }, - "expiryChip": { - "never": "Nunca caduca", - "expired": "Caducado", - "today": "Caduca hoy", - "tomorrow": "Caduca mañana", - "inDays": "Caduca en {{count}} días", - "onDate": "Caduca el {{date}}" - }, - "auth": { - "login_title": "Iniciar sesión", - "username": "Usuario", - "username_placeholder": "Ingresa tu nombre de usuario", - "login_identifier": "Usuario o correo electrónico", - "login_identifier_placeholder": "Ingresa tu usuario o correo electrónico", - "password": "Contraseña", - "password_placeholder": "Ingresa tu contraseña", - "login_button": "Iniciar sesión", - "no_account": "¿No tienes cuenta?", - "register": "Regístrate", - "admin_setup": "¿Primera vez?", - "setup": "Configurar administrador", - "register_title": "Crear cuenta", - "email": "Email", - "email_placeholder": "Ingresa tu email", - "confirm_password": "Confirmar contraseña", - "confirm_password_placeholder": "Confirma tu contraseña", - "register_button": "Crear cuenta", - "have_account": "¿Ya tienes cuenta?", - "login": "Iniciar sesión", - "setup_title": "Configuración inicial", - "setup_step1": "Admin", - "setup_step2": "Sistema", - "setup_step3": "Completado", - "admin_username": "Usuario administrador", - "admin_email": "Email administrador", - "admin_password": "Contraseña administrador", - "create_admin": "Crear administrador", - "back_to_login": "¿Ya está configurado?", - "admin_success": "¡Cuenta de administrador creada con éxito! Ahora puedes iniciar sesión.", - "account_success": "¡Cuenta creada con éxito! Ahora puedes iniciar sesión.", - "passwords_mismatch": "Las contraseñas no coinciden", - "admin_create_error": "Error al crear cuenta de administrador", - "or": "o", - "sso_login": "Iniciar sesión con SSO", - "sso_login_provider": "Iniciar sesión con {{provider}}", - "magicLinkHint": "¿Sin contraseña? Introduce tu correo electrónico y te enviaremos un enlace de inicio de sesión único.", - "magicLinkEmailLabel": "Correo electrónico", - "magicLinkEmailPlaceholder": "tu@ejemplo.com", - "magicLinkSubmit": "Enviar enlace de inicio de sesión", - "magicLinkSent": "Si existe una cuenta para ese correo, se ha enviado un enlace de inicio de sesión. Revisa tu bandeja de entrada.", - "magicLinkUnavailable": "El inicio de sesión por correo electrónico no está disponible en este servidor.", - "magicLinkNetworkError": "No se pudo conectar con el servidor: {{message}}", - "magicLinkToggle": "¿Sin contraseña? Recíbelo por correo", - "passwordsMatch": "Las contraseñas coinciden", - "capsLock": "Bloq Mayús activado" - }, - "storage": { - "title": "Almacenamiento", - "calculating": "Calculando...", - "used": "{{percentage}}% usado ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Este tipo de archivo no se puede previsualizar.", - "download_file": "Descargar archivo", - "zoom_in": "Acercar", - "zoom_out": "Alejar", - "zoom_reset": "Restablecer zoom" - }, - "language_selector": { - "title": "¡Bienvenido!", - "subtitle": "Selecciona tu idioma para continuar", - "continue": "Continuar", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Aún no hay favoritos", - "empty_hint": "Marca archivos o carpetas con estrella para añadirlos a favoritos", - "add": "Añadir a favoritos", - "remove": "Quitar de favoritos", - "added_title": "Añadido a favoritos", - "added_msg": "añadido a favoritos", - "removed_title": "Quitado de favoritos", - "removed_msg": "quitado de favoritos" - }, - "recent": { - "title": "Recientes", - "clear": "Limpiar recientes", - "accessed": "Accedido", - "empty_state": "No hay archivos recientes", - "empty_hint": "Los archivos que abras aparecerán aquí", - "loadMore": "Cargar más" - }, - "notifications": { - "file_renamed": "Archivo renombrado", - "file_renamed_to": "Archivo renombrado a \"{{name}}\"", - "folder_renamed": "Carpeta renombrada", - "folder_renamed_to": "Carpeta renombrada a \"{{name}}\"", - "file_uploaded": "Archivo subido", - "file_deleted": "Archivo movido a papelera", - "folder_deleted": "Carpeta movida a papelera", - "item_deleted_permanently": "Elemento eliminado permanentemente", - "trash_emptied": "Papelera vaciada correctamente", - "title": "Notificaciones", - "empty": "Sin notificaciones", - "link_created": "Enlace creado", - "share_success": "Enlace compartido creado correctamente", - "upload_files_section_title": "Subida no disponible aquí", - "upload_files_section_body": "Ve a la sección Archivos para subir archivos" - }, - "batch": { - "one_selected": "1 elemento seleccionado", - "n_selected": "{{count}} elementos seleccionados", - "confirm_delete": "¿Estás seguro de que quieres mover {{count}} elementos a la papelera?", - "move_title": "Mover {{count}} elemento(s)", - "add_favorites": "Añadir a favoritos", - "move_copy": "Mover o copiar" - }, - "admin": { - "page_title": "Panel de Administración", - "back_to_app": "Volver a OxiCloud", - "loading": "Cargando…", - "access_denied": "Acceso Denegado", - "access_denied_desc": "Se requieren privilegios de administrador para acceder a este panel.", - "sign_in": "Iniciar sesión", - "tab_dashboard": "Panel", - "tab_users": "Usuarios", - "tab_oidc": "SSO / OIDC", - "total_users": "Usuarios Totales", - "active_users": "Usuarios Activos", - "admins": "Administradores", - "version": "Versión", - "storage_overview": "Resumen de Almacenamiento", - "used": "Usado", - "total_quota": "Cuota Total", - "usage_pct": "Uso %", - "users_over_80": "Usuarios >80% cuota", - "users_over_quota": "Usuarios sobre cuota", - "system": "Sistema", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Cuotas", - "enabled": "Habilitado", - "disabled": "Deshabilitado", - "active": "Activo", - "off": "Inactivo", - "allow_registration": "Permitir registro público", - "registration_warning": "El registro público está deshabilitado. Solo los administradores pueden crear nuevos usuarios.", - "user_management": "Gestión de Usuarios", - "create_user": "Crear Usuario", - "col_user": "Usuario", - "col_role": "Rol", - "col_auth": "Auth", - "col_status": "Estado", - "col_storage": "Almacenamiento", - "col_last_login": "Último Acceso", - "col_actions": "Acciones", - "loading_users": "Cargando usuarios…", - "failed_load_users": "Error al cargar usuarios", - "no_users_found": "No se encontraron usuarios", - "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", - "prev": "Anterior", - "next": "Siguiente", - "inactive": "Inactivo", - "you_badge": "(tú)", - "local": "Local", - "never": "Nunca", - "just_now": "Ahora mismo", - "minutes_ago": "hace {{n}}m", - "hours_ago": "hace {{n}}h", - "days_ago": "hace {{n}}d", - "edit_quota_title": "Editar cuota", - "reset_password_title": "Restablecer contraseña", - "toggle_role_title": "Cambiar rol", - "deactivate_title": "Desactivar", - "activate_title": "Activar", - "delete_title": "Eliminar", - "sso_title": "Inicio de Sesión Único (OIDC / SSO)", - "enable_sso": "Habilitar autenticación SSO", - "provider_name": "Nombre del Proveedor", - "issuer_url": "URL del Emisor", - "issuer_url_hint": "URL del emisor OpenID Connect de tu proveedor de identidad", - "auto_discover": "Auto-descubrir", - "discovering": "Descubriendo…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Dejar vacío para mantener el valor actual", - "secret_configured": "Ya hay un client secret configurado", - "callback_url": "URL de Callback", - "callback_url_hint": "(registrar en tu IdP)", - "advanced_settings": "Configuración Avanzada", - "scopes": "Scopes", - "auto_provision": "Auto-provisionar usuarios en el primer inicio de sesión", - "admin_groups": "Grupos de Admin", - "admin_groups_hint": "Nombres de grupos OIDC separados por comas que mapean al rol de admin", - "disable_password": "Desactivar inicio de sesión con contraseña (solo OIDC)", - "password_warning": "¡Esto impedirá TODOS los inicios de sesión con contraseña!", - "test_btn": "Probar", - "save_btn": "Guardar", - "saving": "Guardando…", - "settings_saved": "Configuración guardada — OIDC ahora está {{status}}", - "quota_modal_title": "Actualizar Cuota de Almacenamiento", - "quota_user_label": "Usuario:", - "new_quota": "Nueva Cuota", - "quota_unlimited_hint": "Establecer 0 para ilimitado", - "cancel": "Cancelar", - "create_user_title": "Crear Nuevo Usuario", - "username_label": "Nombre de usuario", - "username_placeholder": "juanperez", - "username_hint": "3–32 caracteres", - "password_label": "Contraseña", - "password_placeholder": "Mín 8 caracteres", - "email_label": "Correo", - "email_optional": "(opcional)", - "email_placeholder": "usuario@ejemplo.com (auto-generado si vacío)", - "role_label": "Rol", - "role_user": "Usuario", - "role_admin": "Admin", - "quota_label": "Cuota", - "creating": "Creando…", - "reset_pw_title": "Restablecer Contraseña", - "new_password_label": "Nueva Contraseña", - "resetting": "Restableciendo…", - "reset_btn": "Restablecer", - "confirm_role_change": "¿Cambiar rol a {{role}}?", - "confirm_deactivate": "¿Estás seguro de que quieres desactivar este usuario?", - "confirm_activate": "¿Estás seguro de que quieres activar este usuario?", - "confirm_delete_user": "¿ELIMINAR usuario \"{{name}}\"? ¡Esto no se puede deshacer!", - "confirm_action": "Confirmar Acción", - "confirm_yes": "Confirmar", - "confirm_no": "Cancelar", - "error_username_short": "El nombre de usuario debe tener al menos 3 caracteres", - "error_password_short": "La contraseña debe tener al menos 8 caracteres", - "error_generic": "Error", - "error_network": "Error de red: {{message}}", - "error_create_user": "Error al crear usuario", - "tab_storage": "Almacenamiento", - "storage_title": "Backend de Almacenamiento", - "storage_current_backend": "Backend Activo", - "storage_total_blobs": "Total de Blobs", - "storage_total_size": "Tamaño Total", - "storage_dedup_ratio": "Ratio de Dedup", - "storage_backend": "Tipo de Backend", - "storage_local": "Sistema de Archivos Local", - "storage_s3": "Compatible con S3", - "storage_provider_preset": "Proveedor Preconfigurado", - "storage_preset_custom": "Personalizado", - "storage_endpoint_url": "URL del Endpoint", - "storage_endpoint_hint": "Dejar vacío para usar Amazon S3 por defecto", - "storage_bucket": "Bucket", - "storage_region": "Región", - "storage_access_key": "Access Key ID", - "storage_secret_key": "Secret Access Key", - "storage_secret_configured": "Ya hay una clave secreta configurada", - "storage_key_placeholder": "Dejar vacío para mantener el valor actual", - "storage_path_style": "Forzar Path Style", - "storage_path_style_hint": "Requerido para MinIO y algunos proveedores compatibles con S3", - "storage_test_connection": "Probar Conexión", - "storage_test_success": "Conexión exitosa", - "storage_test_failure": "Conexión fallida", - "storage_save": "Guardar", - "storage_saved": "Configuración de almacenamiento guardada correctamente", - "storage_migration": "Migración de Backend", - "storage_migration_coming_soon": "La migración de backend estará disponible en una futura actualización.", - "migration_status_label": "Estado:", - "migration_start": "Iniciar Migración", - "migration_pause": "Pausar", - "migration_resume": "Reanudar", - "migration_verify": "Verificar Integridad", - "migration_complete": "Finalizar", - "migration_started": "Migración iniciada", - "migration_paused_msg": "Migración pausada", - "migration_resumed_msg": "Migración reanudada", - "migration_completed_msg": "Migración finalizada. Reinicia el servidor para usar el nuevo backend.", - "migration_verifying": "Verificando…", - "migration_verify_passed": "Verificación exitosa", - "migration_verify_failed": "Verificación fallida", - "migration_failed_blobs": "blobs fallidos", - "testing": "Probando…", - "smtp_disabled": "Desactivado (host no configurado)", - "smtp_enabled": "Activado", - "smtp_enabled_label": "Estado", - "smtp_intro": "SMTP se configura exclusivamente a través de variables de entorno (OXICLOUD_SMTP_*). Los valores siguientes se leen del servidor en ejecución — para modificarlos, edita el entorno y reinicia OxiCloud.", - "smtp_not_configured": "SMTP no está configurado en este servidor.", - "smtp_send_failed": "Fallo al enviar.", - "smtp_send_test": "Enviar correo de prueba", - "smtp_sending": "Enviando…", - "smtp_sent": "Correo de prueba enviado.", - "smtp_server_code": "Respuesta del servidor", - "smtp_test_intro": "Envía un mensaje de diagnóstico predefinido al destinatario indicado abajo e informa de la respuesta del servidor SMTP para que puedas cruzarla con los registros de tu relay.", - "smtp_test_missing_to": "Introduce una dirección de destinatario.", - "smtp_test_title": "Enviar correo de prueba", - "smtp_test_to": "Dirección del destinatario", - "smtp_title": "Correo saliente (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Perfil", - "back_to_app": "Volver a OxiCloud", - "loading": "Cargando…", - "not_authenticated": "No Autenticado", - "not_authenticated_desc": "Inicia sesión para ver tu perfil.", - "sign_in": "Iniciar sesión", - "role_admin": "Administrador", - "role_user": "Usuario", - "account_details": "Detalles de la Cuenta", - "username": "Nombre de usuario", - "email": "Correo electrónico", - "role": "Rol", - "last_login": "Último acceso", - "storage": "Almacenamiento", - "used": "Usado", - "quota": "Cuota", - "usage": "Uso", - "unlimited": "Ilimitado", - "app_passwords": "Contraseñas de Aplicación", - "app_pw_desc": "Genera contraseñas para clientes WebDAV, CalDAV y CardDAV. Cada contraseña se muestra solo una vez.", - "app_pw_label_placeholder": "Etiqueta (ej. Thunderbird, macOS)", - "generate": "Generar", - "generating": "Generando…", - "new_password_for": "Nueva contraseña para", - "copy_warning": "Copia esta contraseña ahora. No podrás verla de nuevo.", - "copy_to_clipboard": "Copiar al portapapeles", - "col_label": "Etiqueta", - "col_created": "Creado", - "col_last_used": "Último uso", - "col_status": "Estado", - "active": "Activa", - "revoked": "Revocada", - "revoke_title": "Revocar", - "no_app_passwords": "Aún no hay contraseñas de aplicación.", - "client_sessions": "Sesiones de cliente", - "client_sessions_desc": "Generadas automáticamente al conectar un cliente compatible con Nextcloud.", - "col_client": "Cliente", - "never": "Nunca", - "just_now": "Ahora mismo", - "minutes_ago": "hace {{n}} min", - "hours_ago": "hace {{n}}h", - "days_ago": "hace {{n}} días", - "edit_profile": "Editar perfil", - "edit_oidc_managed": "Para cambiar tu información (nombre, apellidos, foto de perfil, …), actualízala en tu proveedor de identidad. Los cambios se aplicarán en tu próximo inicio de sesión.", - "username_claim_hint": "Entre 2 y 64 caracteres, letras / dígitos / punto / guion / subrayado. Una vez elegido, el nombre de usuario no se puede cambiar (los clientes DAV/NextCloud dependen de él).", - "username_already_claimed": "Nombre de usuario fijado y no modificable (los clientes DAV/NextCloud dependen de él).", - "given_name": "Nombre", - "family_name": "Apellidos", - "notify_on_share": "Enviarme un correo cuando alguien comparta conmigo", - "notify_on_share_hint": "Cuando esté desmarcado, los recursos compartidos seguirán apareciendo en tu cuenta — simplemente no recibirás un correo sobre ellos.", - "save_profile": "Guardar cambios", - "profile_saved": "Perfil actualizado", - "profile_no_changes": "Sin cambios que guardar.", - "profile_save_failed": "Error al guardar", - "username_taken_error": "Ese nombre de usuario ya está en uso.", - "username_immutable_error": "Tu nombre de usuario ya está fijado y no se puede cambiar aquí. Contacta con un administrador si necesitas renombrarlo.", - "change_password": "Cambiar Contraseña", - "current_password": "Contraseña Actual", - "new_password": "Nueva Contraseña", - "min_8_chars": "Al menos 8 caracteres", - "confirm_password": "Confirmar Nueva Contraseña", - "update_password": "Actualizar Contraseña", - "updating": "Actualizando…", - "password_updated": "Contraseña actualizada correctamente", - "passwords_no_match": "Las contraseñas no coinciden", - "password_too_short": "La contraseña debe tener al menos 8 caracteres", - "password_change_failed": "Error al cambiar la contraseña", - "error_network": "Error de red: {{message}}", - "error_label_required": "Introduce una etiqueta", - "error_create_pw": "Error al crear contraseña de aplicación", - "confirm_revoke": "¿Revocar contraseña \"{{label}}\"? Los clientes que la usen dejarán de funcionar.", - "error_revoke": "Error al revocar contraseña", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Subiendo...", - "files": "archivos", - "complete": "{{count}} / {{total}} subidos" - }, - "storage_quota_exceeded": "Cuota de almacenamiento superada", - "sharedwithme": { - "pageTitle": "Compartido conmigo", - "pageDescription": "Archivos y carpetas que otros usuarios han compartido contigo", - "emptyStateTitle": "Aún no hay nada compartido contigo", - "emptyStateDesc": "Los elementos que otros usuarios compartan contigo aparecerán aquí", - "loadMore": "Cargar más", - "sharedBy": "Compartido por", - "colName": "Nombre", - "colType": "Tipo", - "colSharedBy": "Compartido por", - "colDate": "Fecha de compartición", - "colPermissions": "Permisos" - }, - "groupby": { - "none": "Ninguno", - "title": "Agrupar por", - "owner": "Propietario", - "shareDate": "Fecha de compartición", - "type": "Tipo", - "type.folders": "Carpetas", - "accessedAt": "Fecha de acceso", - "modifiedAt": "Fecha de modificación", - "createdAt": "Fecha de creación", - "size": "Tamaño", - "favoriteDate": "Fecha de favorito", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nuevo" - }, - "dateBucket": { - "today": "Hoy", - "last7days": "Últimos 7 días", - "last30days": "Últimos 30 días" - }, - "groups": { - "title": "Gestionar grupos", - "create_button": "Crear grupo", - "create_dialog_title": "Nuevo grupo", - "edit_dialog_title": "Renombrar grupo", - "name_label": "Nombre", - "name_placeholder": "ingenieria", - "description_label": "Descripción (opcional)", - "members_section": "Miembros", - "add_member_placeholder": "Añadir un usuario o grupo…", - "no_members": "Aún no hay miembros.", - "remove_member": "Eliminar", - "delete_group": "Eliminar grupo", - "delete_confirm": "¿Eliminar el grupo «{name}»? Se revocarán las concesiones que hagan referencia a este grupo.", - "empty_state": "Aún no hay grupos.", - "load_more": "Cargar más", - "back_to_list": "Volver", - "loading": "Cargando…", - "virtual_badge": "Sistema", - "member_count_zero": "Sin miembros", - "member_count_one": "1 miembro", - "member_count_other": "{count} miembros", - "delete_confirm_label": "Escribe el nombre del grupo para confirmar:", - "delete_confirm_mismatch": "Escribe el nombre del grupo exactamente para confirmar.", - "virtual_internal_name": "Interno", - "members_loading": "Cargando miembros…", - "members_empty": "Sin miembros", - "virtual_internal_explanation": "Todos los usuarios internos de este servidor" - }, - "myshares": { - "copyLink": "Copiar enlace", - "deleteLink": "Eliminar enlace", - "notifyByEmail": "Notificar por correo", - "notifyFailed": "No se pudo enviar la notificación.", - "notifyGroupMembers": "Notificar a los miembros del grupo", - "notifyRateLimited": "Demasiadas notificaciones para este destinatario — inténtalo más tarde.", - "removeAccess": "Quitar acceso", - "resendInvitation": "Reenviar correo de invitación" - }, - "sort": { - "asc": "ascendente", - "desc": "descendente" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error al realizar la búsqueda", - "cleanupCompleted": "Limpieza completada", - "cleanupCompletedBody": "Se ha borrado el historial de archivos recientes", - "batchCopy": "Copia en lote", - "batchCopyBody": "{{success}} copiados, {{errors}} fallidos", - "itemsCopied": "Elementos copiados", - "itemsCopiedBody": "{{count}} elementos copiados correctamente", - "batchMove": "Movimiento en lote", - "batchMoveBody": "{{success}} movidos, {{errors}} fallidos", - "itemsMoved": "Elementos movidos", - "itemsMovedBody": "{{count}} elementos movidos correctamente", - "batchDelete": "Borrado en lote", - "batchDeleteBody": "{{success}} movidos a la papelera, {{errors}} fallidos", - "movedToTrash": "Movido a la papelera", - "movedToTrashBody": "{{count}} elementos movidos a la papelera", - "trashItemsError": "No se pudieron mover los elementos a la papelera", - "preparingDownload": "Preparando descarga", - "preparingDownloadBody": "Preparando la descarga…", - "downloadItemsError": "No se pudieron descargar los elementos seleccionados", - "favoritesAddError": "No se pudieron añadir los elementos a favoritos", - "invalidEmail": "Introduce una dirección de correo válida", - "notificationSendError": "No se pudo enviar la notificación", - "folderCreated": "Carpeta creada", - "folderCreatedBody": "«{{name}}» creada correctamente", - "fileMoved": "Archivo movido", - "fileMovedBody": "Archivo movido correctamente", - "fileMoveError": "Error al mover el archivo: {{error}}", - "fileMoveErrorGeneric": "Error al mover el archivo", - "folderMoved": "Carpeta movida", - "folderMovedBody": "Carpeta movida correctamente", - "folderMoveError": "Error al mover la carpeta: {{error}}", - "folderMoveErrorGeneric": "Error al mover la carpeta", - "fileCopied": "Archivo copiado", - "fileCopiedBody": "Archivo copiado correctamente", - "fileCopyError": "Error al copiar el archivo: {{error}}", - "fileCopyErrorGeneric": "Error al copiar el archivo", - "folderRenamed": "Carpeta renombrada", - "folderRenamedBody": "Carpeta renombrada a «{{name}}»", - "fileTrashed": "Archivo movido a la papelera", - "fileTrashedBody": "«{{name}}» movido a la papelera", - "fileDeleted": "Archivo eliminado", - "fileDeletedBody": "«{{name}}» eliminado correctamente", - "fileDeleteError": "Error al eliminar el archivo", - "folderTrashed": "Carpeta movida a la papelera", - "folderTrashedBody": "«{{name}}» movida a la papelera", - "folderDeleted": "Carpeta eliminada", - "folderDeletedBody": "«{{name}}» eliminada correctamente", - "folderDeleteError": "Error al eliminar la carpeta", - "itemRestored": "Elemento restaurado", - "itemRestoredBody": "Elemento restaurado correctamente", - "itemRestoreError": "Error al restaurar el elemento", - "itemDeleted": "Elemento eliminado", - "itemDeletedBody": "Elemento eliminado permanentemente", - "itemDeleteError": "Error al eliminar el elemento", - "trashEmptied": "Papelera vaciada", - "trashEmptiedBody": "La papelera se ha vaciado correctamente", - "trashEmptyError": "Error al vaciar la papelera", - "cacheCleared": "Caché limpiada", - "cacheClearedBody": "Caché de búsqueda limpiada correctamente", - "cacheClearError": "Error al limpiar la caché de búsqueda", - "wopiOpenError": "No se pudo abrir el editor de documentos.", - "linkCopied": "Enlace copiado", - "linkCopiedBody": "Enlace copiado al portapapeles", - "linkCopyError": "No se pudo copiar el enlace", - "notificationSent": "Notificación enviada", - "notificationSentBody": "Notificación enviada a {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", + "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nAbre OxiCloud para ver tu nuevo recurso compartido:\n{{login_link}}\n\nPuede que tengas más recursos compartidos nuevos de {{inviter}} — inicia sesión para ver todos tus elementos compartidos.\n\n— OxiCloud\n\nRecibes este mensaje porque tienes una cuenta de OxiCloud y la preferencia de notificación de recursos compartidos está activada. Puedes desactivarla en tu perfil (Enviarme un correo cuando alguien comparta conmigo)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Sistema de almacenamiento en la nube minimalista" + }, + "nav": { + "files": "Archivos", + "shared": "Compartidos", + "recent": "Recientes", + "favorites": "Favoritos", + "photos": "Fotos", + "music": "Música", + "trash": "Papelera", + "sharedwithme": "Compartidos conmigo", + "profile": "Perfil", + "shared_with_me": "Compartidos conmigo" + }, + "photos": { + "empty_state": "Aún no hay fotos", + "empty_hint": "Sube imágenes o videos para verlos aquí", + "items_selected": "seleccionados", + "view_daily": "Día", + "view_monthly": "Mes", + "view_yearly": "Año", + "group_by": "Agrupar por" + }, + "music": { + "create_playlist": "Crear Lista", + "playlists": "Listas", + "no_playlists": "Sin listas aún", + "empty_hint": "Crea tu primera lista para empezar a organizar tu música", + "select_playlist": "Selecciona una lista", + "select_hint": "Elige una lista de la barra lateral o crea una nueva", + "add_tracks": "Añadir Pistas", + "no_tracks": "No hay pistas en esta lista", + "unknown_artist": "Artista Desconocido", + "unknown_title": "Desconocido", + "confirm_delete": "¿Eliminar esta lista?", + "playlist_name": "Nombre de la lista", + "create": "Crear", + "delete": "Eliminar", + "share": "Compartir", + "edit": "Editar", + "play_all": "Reproducir Todo", + "shuffle": "Aleatorio", + "repeat": "Repetir", + "repeat_one": "Repetir Una", + "queue": "Cola", + "queue_empty": "Cola vacía", + "not_playing": "No reproduciendo", + "play": "Reproducir", + "pause": "Pausar", + "previous": "Anterior", + "next": "Siguiente", + "volume": "Volumen", + "mute": "Silenciar", + "unmute": "Activar sonido", + "title": "Título", + "artist": "Artista", + "album": "Álbum", + "tracks": "pistas", + "add": "Añadir", + "added": "¡Añadido!", + "added_to_playlist": "añadido a la lista", + "add_to_playlist": "Añadir a playlist", + "load_error": "Error al cargar listas", + "add_error": "No se pudieron añadir las pistas", + "no_playlists_yet": "No hay listas aún. ¡Crea una primero!", + "selected_files": "Seleccionados:", + "share_with_user": "ID de usuario o email", + "playback_error": "Error de reproducción", + "error": "Error", + "remove": "Eliminar", + "track_removed": "Pista eliminada", + "manage_shares": "Gestionar compartidos", + "no_shares": "Sin compartidos aún", + "remove_share": "Eliminar compartido", + "can_write": "Puede editar", + "read_only": "Solo lectura", + "public": "Pública", + "private": "Privada", + "toggle_public": "Visibilidad", + "make_public": "Hacer pública", + "make_private": "Hacer privada", + "set_cover": "Establecer portada", + "cover_updated": "Portada actualizada", + "search_audio": "Buscar archivos de audio…", + "no_audio_files": "No se encontraron archivos de audio", + "selected": "seleccionados", + "loading": "Cargando…", + "search_error": "No se pudieron cargar los archivos de audio", + "adding": "Añadiendo…", + "prev": "Anterior" + }, + "share": { + "dialogTitle": "Compartir Enlace", + "linkLabel": "Enlace compartido:", + "copyLink": "Copiar", + "permissions": "Permisos:", + "permissionRead": "Lectura", + "permissionWrite": "Escritura", + "permissionReshare": "Recompartir", + "password": "Protección con contraseña:", + "generatePassword": "Generar", + "expiration": "Fecha de caducidad:", + "update": "Actualizar compartido", + "remove": "Eliminar compartido", + "notifyTitle": "Enviar notificación", + "notifyEmailLabel": "Dirección de correo:", + "notifyMessageLabel": "Mensaje (opcional):", + "notifySend": "Enviar notificación", + "shareWithOthers": "Compartir con otros", + "sharePublicly": "Compartir públicamente", + "shareSettings": "Configuración de compartido", + "shareCopied": "Enlace copiado al portapapeles", + "shareCreated": "Enlace compartido creado correctamente", + "shareUpdated": "Configuración de compartido actualizada", + "shareRemoved": "Compartido eliminado correctamente", + "inviteByEmail": "Invitar por correo — se enviará una invitación", + "directoryUnavailable": "Directorio de usuarios no disponible", + "linkNamePlaceholder": "Nombre del enlace (opcional)", + "newLink": "Nuevo enlace", + "noExpiry": "Sin caducidad", + "pending": "Pendiente", + "people": "Personas", + "publicLinks": "Enlaces públicos", + "role": { + "canEdit": "Puede editar", + "canManage": "Puede gestionar", + "canView": "Puede ver" + }, + "searchPlaceholder": "Buscar personas…", + "shareOf": "Compartir:", + "sharedLink": "Enlace compartido", + "copied": "Enlace copiado", + "copy": "Copiar", + "copy_failed": "No se pudo copiar el enlace", + "download": "Descargar", + "files": "Archivos", + "folders": "Carpetas", + "link_name": "Nombre del enlace (opcional)", + "notifyByEmail": "Notificar por correo", + "revoke": "Eliminar", + "role_label": "Rol" + }, + "share_dialogTitle": "Compartir Enlace", + "share_linkLabel": "Enlace compartido:", + "share_copyLink": "Copiar", + "share_permissions": "Permisos:", + "share_permissionRead": "Lectura", + "share_permissionWrite": "Escritura", + "share_permissionReshare": "Recompartir", + "share_password": "Protección con contraseña:", + "share_generatePassword": "Generar", + "share_expiration": "Fecha de caducidad:", + "share_update": "Actualizar compartido", + "share_remove": "Eliminar compartido", + "share_notifyTitle": "Enviar notificación", + "share_notifyEmailLabel": "Dirección de correo:", + "share_notifyMessageLabel": "Mensaje (opcional):", + "share_notifySend": "Enviar notificación", + "shared": { + "backToFiles": "Volver a Archivos", + "pageTitle": "Recursos Compartidos", + "pageDescription": "Administra tus archivos y carpetas compartidos", + "filterType": "Tipo:", + "filterAll": "Todos", + "filterFiles": "Archivos", + "filterFolders": "Carpetas", + "sortBy": "Ordenar por:", + "sortByName": "Nombre", + "sortByDate": "Fecha compartido", + "sortByExpiration": "Caducidad", + "search": "Buscar", + "colName": "Nombre", + "colType": "Tipo", + "colDateShared": "Fecha compartido", + "colExpiration": "Caducidad", + "colPermissions": "Permisos", + "colPassword": "Contraseña", + "colActions": "Acciones", + "emptyStateTitle": "Aún no hay recursos compartidos", + "emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", + "goToFiles": "Ir a Archivos", + "typeFile": "Archivo", + "typeFolder": "Carpeta", + "noExpiration": "Sin caducidad", + "hasPassword": "Sí", + "noPassword": "No", + "editShare": "Editar compartido", + "notifyShare": "Notificar a alguien", + "copyLink": "Copiar enlace", + "removeShare": "Eliminar compartido", + "linkCopied": "¡Enlace copiado al portapapeles!", + "linkCopyFailed": "Error al copiar el enlace", + "itemUpdated": "Configuración de compartido actualizada", + "itemRemoved": "Compartido eliminado correctamente", + "invalidEmail": "Por favor, introduce una dirección de correo válida", + "notificationSent": "Notificación enviada correctamente", + "notificationFailed": "Error al enviar la notificación", + "shared_backToFiles": "Volver a Archivos", + "shared_pageTitle": "Recursos Compartidos", + "shared_pageDescription": "Administra tus archivos y carpetas compartidos", + "shared_filterType": "Tipo:", + "shared_filterAll": "Todos", + "shared_filterFiles": "Archivos", + "shared_filterFolders": "Carpetas", + "shared_sortBy": "Ordenar por:", + "shared_sortByName": "Nombre", + "shared_sortByDate": "Fecha compartido", + "shared_sortByExpiration": "Caducidad", + "shared_search": "Buscar", + "shared_colName": "Nombre", + "shared_colType": "Tipo", + "shared_colDateShared": "Fecha compartido", + "shared_colExpiration": "Caducidad", + "shared_colPermissions": "Permisos", + "shared_colPassword": "Contraseña", + "shared_colActions": "Acciones", + "shared_emptyStateTitle": "Aún no hay recursos compartidos", + "shared_emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", + "shared_goToFiles": "Ir a Archivos", + "shared_typeFile": "Archivo", + "shared_typeFolder": "Carpeta", + "shared_noExpiration": "Sin caducidad", + "shared_hasPassword": "Sí", + "shared_noPassword": "No", + "shared_editShare": "Editar compartido", + "shared_notifyShare": "Notificar a alguien", + "shared_copyLink": "Copiar enlace", + "shared_removeShare": "Eliminar compartido", + "shared_linkCopied": "¡Enlace copiado al portapapeles!", + "shared_linkCopyFailed": "Error al copiar el enlace", + "shared_itemUpdated": "Configuración de compartido actualizada", + "shared_itemRemoved": "Compartido eliminado correctamente", + "shared_invalidEmail": "Por favor, introduce una dirección de correo válida", + "shared_notificationSent": "Notificación enviada correctamente", + "shared_notificationFailed": "Error al enviar la notificación" + }, + "actions": { + "search": "Buscar archivos...", + "new_folder": "Nueva carpeta", + "upload": "Subir", + "upload_files": "Subir archivos", + "upload_folder": "Subir carpeta", + "upload.uploading": "Subiendo...", + "upload.complete": "{count} / {total} subidos", + "upload.files": "archivos", + "rename": "Renombrar", + "move": "Mover a...", + "move_to": "Mover a", + "delete": "Eliminar", + "download": "Descargar", + "view": "Ver", + "cancel": "Cancelar", + "confirm": "Confirmar", + "share": "Compartir", + "favorite": "Añadir a favoritos", + "unfavorite": "Quitar de favoritos", + "copy": "Copiar", + "notify": "Notificar", + "send": "Enviar", + "clear_recent": "Limpiar recientes", + "logout": "Cerrar sesión", + "create": "Crear", + "search_btn": "Buscar", + "close": "Cerrar", + "delete_permanently": "Eliminar permanentemente", + "empty_trash": "Vaciar papelera", + "open_parent_folder": "Ir a la carpeta padre", + "add": "Añadir", + "apply": "Aplicar", + "clear": "Limpiar", + "remove": "Quitar" + }, + "user_menu": { + "appearance": "Apariencia", + "about": "Acerca de OxiCloud", + "about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada.", + "admin_panel": "Panel de administración", + "profile": "Mi perfil", + "role_user": "Usuario", + "theme": { + "light": "Claro", + "dark": "Oscuro", + "auto": "Como el sistema" + }, + "manage_groups": "Gestionar grupos", + "admin": "Admin" + }, + "files": { + "name": "Nombre", + "type": "Tipo", + "size": "Tamaño", + "modified": "Modificado", + "no_files": "No hay archivos en esta carpeta", + "empty_hint": "Sube archivos o crea carpetas para comenzar", + "loading": "Cargando archivos…", + "view_grid": "Vista de cuadrícula", + "view_list": "Vista de lista", + "file_types": { + "document": "Documento", + "image": "Imagen", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Texto", + "folder": "Carpeta", + "spreadsheet": "Hoja de cálculo", + "presentation": "Presentación", + "archive": "Archivo comprimido", + "installer": "Instalador", + "code": "Código" + }, + "owner": "Propietario", + "add_favorites": "Añadir a favoritos", + "added_favorites": "Añadido a favoritos", + "col_name": "Nombre", + "col_owner": "Propietario", + "col_size": "Tamaño", + "col_type": "Tipo", + "copy": "Copiar", + "edit": "Editar", + "file": "Archivo", + "folder": "Carpeta", + "new_folder": "Nueva carpeta", + "share": "Compartir", + "view": "Ver" + }, + "dialogs": { + "rename_folder": "Renombrar carpeta", + "rename_file": "Renombrar archivo", + "new_name": "Nuevo nombre", + "new_folder_title": "Nueva carpeta", + "folder_name": "Nombre de la carpeta", + "folder_placeholder": "Mi carpeta", + "rename_title": "Renombrar", + "move_file": "Mover archivo", + "move_folder": "Mover carpeta", + "select_destination": "Selecciona la carpeta destino:", + "select_this_folder": "Seleccionar esta carpeta", + "go_to_parent": ".. (carpeta superior)", + "no_subfolders": "Sin subcarpetas", + "root": "Raíz", + "delete_confirmation": "¿Estás seguro de que quieres eliminar", + "and_contents": "y todo su contenido", + "no_undo": "Esta acción no se puede deshacer", + "confirm_title": "Confirmar acción", + "confirm_delete": "Mover a papelera", + "confirm_delete_file": "¿Estás seguro de que quieres mover a la papelera el archivo \"{{name}}\"?", + "confirm_delete_folder": "¿Estás seguro de que quieres mover a la papelera la carpeta \"{{name}}\" y todo su contenido?", + "confirm_permanent_delete": "Eliminar permanentemente", + "confirm_permanent_delete_msg": "¿Estás seguro de que quieres eliminar permanentemente este elemento? Esta acción no se puede deshacer.", + "confirm_empty_trash": "Vaciar papelera", + "confirm_delete_share": "Eliminar enlace compartido", + "confirm_delete_share_msg": "¿Estás seguro de que quieres eliminar este enlace compartido?", + "share_file": "Compartir Archivo", + "share_folder": "Compartir Carpeta", + "existing_shares": "Compartidos Existentes", + "share_options": "Opciones de Compartición", + "password": "Contraseña", + "expiration": "Caducidad", + "permissions": "Permisos", + "generated_link": "Enlace Generado", + "notify": "Enviar Notificación", + "recipient": "Destinatario", + "message": "Mensaje", + "move_to_home": "Mover a la carpeta de inicio" + }, + "dropzone": { + "drag_files": "Arrastra archivos aquí o haz clic para seleccionar", + "drop_files": "Suelta los archivos para subirlos" + }, + "permissions": { + "read": "Lectura", + "write": "Escritura", + "reshare": "Recompartir" + }, + "errors": { + "file_not_found": "Archivo no encontrado", + "folder_not_found": "Carpeta no encontrada", + "delete_error": "Error al eliminar", + "upload_error": "Error al subir el archivo", + "rename_error": "Error al renombrar", + "move_error": "Error al mover", + "empty_name": "El nombre no puede estar vacío", + "name_exists": "Ya existe un archivo o carpeta con ese nombre", + "generic_error": "Ha ocurrido un error", + "group_name_invalid": "El nombre del grupo debe seguir el formato de prefijo de correo (letras, dígitos, punto, guión, guion bajo; 1–64 caracteres).", + "group_cycle": "Este miembro creaería una referencia circular entre grupos.", + "group_depth_exceeded": "Esta profundidad de anidamiento excede el máximo permitido (8).", + "group_virtual_immutable": "El grupo «Internal» es gestionado por el sistema y no se puede modificar.", + "group_not_found": "Grupo no encontrado.", + "group_name_taken": "Ya existe un grupo con este nombre." + }, + "breadcrumb": { + "home": "Inicio" + }, + "trash": { + "empty_trash": "Vaciar papelera", + "empty_state": "La papelera está vacía", + "original_location": "Ubicación original", + "deleted_date": "Fecha de eliminación", + "remaining": "Restante", + "actions": "Acciones", + "restore": "Restaurar", + "delete_permanently": "Eliminar permanentemente", + "empty_confirm": "¿Estás seguro de que quieres vaciar la papelera? Esta acción eliminará permanentemente todos los elementos.", + "groupby": { + "remaining_days": "Días restantes", + "trashed_time": "Fecha de eliminación" + }, + "delete": "Eliminar permanentemente", + "empty_action": "Vaciar papelera" + }, + "daysRemaining": { + "expired": "Caducado", + "today": "Hoy", + "tomorrow": "Mañana", + "inDays": "{{count}} días" + }, + "expiryChip": { + "never": "Nunca caduca", + "expired": "Caducado", + "today": "Caduca hoy", + "tomorrow": "Caduca mañana", + "inDays": "Caduca en {{count}} días", + "onDate": "Caduca el {{date}}" + }, + "auth": { + "login_title": "Iniciar sesión", + "username": "Usuario", + "username_placeholder": "Ingresa tu nombre de usuario", + "login_identifier": "Usuario o correo electrónico", + "login_identifier_placeholder": "Ingresa tu usuario o correo electrónico", + "password": "Contraseña", + "password_placeholder": "Ingresa tu contraseña", + "login_button": "Iniciar sesión", + "no_account": "¿No tienes cuenta?", + "register": "Regístrate", + "admin_setup": "¿Primera vez?", + "setup": "Configurar administrador", + "register_title": "Crear cuenta", + "email": "Email", + "email_placeholder": "Ingresa tu email", + "confirm_password": "Confirmar contraseña", + "confirm_password_placeholder": "Confirma tu contraseña", + "register_button": "Crear cuenta", + "have_account": "¿Ya tienes cuenta?", + "login": "Iniciar sesión", + "setup_title": "Configuración inicial", + "setup_step1": "Admin", + "setup_step2": "Sistema", + "setup_step3": "Completado", + "admin_username": "Usuario administrador", + "admin_email": "Email administrador", + "admin_password": "Contraseña administrador", + "create_admin": "Crear administrador", + "back_to_login": "¿Ya está configurado?", + "admin_success": "¡Cuenta de administrador creada con éxito! Ahora puedes iniciar sesión.", + "account_success": "¡Cuenta creada con éxito! Ahora puedes iniciar sesión.", + "passwords_mismatch": "Las contraseñas no coinciden", + "admin_create_error": "Error al crear cuenta de administrador", + "or": "o", + "sso_login": "Iniciar sesión con SSO", + "sso_login_provider": "Iniciar sesión con {{provider}}", + "magicLinkHint": "¿Sin contraseña? Introduce tu correo electrónico y te enviaremos un enlace de inicio de sesión único.", + "magicLinkEmailLabel": "Correo electrónico", + "magicLinkEmailPlaceholder": "tu@ejemplo.com", + "magicLinkSubmit": "Enviar enlace de inicio de sesión", + "magicLinkSent": "Si existe una cuenta para ese correo, se ha enviado un enlace de inicio de sesión. Revisa tu bandeja de entrada.", + "magicLinkUnavailable": "El inicio de sesión por correo electrónico no está disponible en este servidor.", + "magicLinkNetworkError": "No se pudo conectar con el servidor: {{message}}", + "magicLinkToggle": "¿Sin contraseña? Recíbelo por correo", + "passwordsMatch": "Las contraseñas coinciden", + "capsLock": "Bloq Mayús activado", + "caps_lock": "Bloq Mayús activado", + "magic_email_label": "Correo electrónico", + "magic_hint": "¿Sin contraseña? Introduce tu correo electrónico y te enviaremos un enlace de inicio de sesión único.", + "magic_unavailable": "El inicio de sesión por correo electrónico no está disponible en este servidor.", + "passwords_match": "Las contraseñas coinciden", + "sign_in": "Iniciar sesión" + }, + "storage": { + "title": "Almacenamiento", + "calculating": "Calculando...", + "used": "{{percentage}}% usado ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Este tipo de archivo no se puede previsualizar.", + "download_file": "Descargar archivo", + "zoom_in": "Acercar", + "zoom_out": "Alejar", + "zoom_reset": "Restablecer zoom" + }, + "language_selector": { + "title": "¡Bienvenido!", + "subtitle": "Selecciona tu idioma para continuar", + "continue": "Continuar", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Aún no hay favoritos", + "empty_hint": "Marca archivos o carpetas con estrella para añadirlos a favoritos", + "add": "Añadir a favoritos", + "remove": "Quitar de favoritos", + "added_title": "Añadido a favoritos", + "added_msg": "añadido a favoritos", + "removed_title": "Quitado de favoritos", + "removed_msg": "quitado de favoritos" + }, + "recent": { + "title": "Recientes", + "clear": "Limpiar recientes", + "accessed": "Accedido", + "empty_state": "No hay archivos recientes", + "empty_hint": "Los archivos que abras aparecerán aquí", + "loadMore": "Cargar más" + }, + "notifications": { + "file_renamed": "Archivo renombrado", + "file_renamed_to": "Archivo renombrado a \"{{name}}\"", + "folder_renamed": "Carpeta renombrada", + "folder_renamed_to": "Carpeta renombrada a \"{{name}}\"", + "file_uploaded": "Archivo subido", + "file_deleted": "Archivo movido a papelera", + "folder_deleted": "Carpeta movida a papelera", + "item_deleted_permanently": "Elemento eliminado permanentemente", + "trash_emptied": "Papelera vaciada correctamente", + "title": "Notificaciones", + "empty": "Sin notificaciones", + "link_created": "Enlace creado", + "share_success": "Enlace compartido creado correctamente", + "upload_files_section_title": "Subida no disponible aquí", + "upload_files_section_body": "Ve a la sección Archivos para subir archivos" + }, + "batch": { + "one_selected": "1 elemento seleccionado", + "n_selected": "{{count}} elementos seleccionados", + "confirm_delete": "¿Estás seguro de que quieres mover {{count}} elementos a la papelera?", + "move_title": "Mover {{count}} elemento(s)", + "add_favorites": "Añadir a favoritos", + "move_copy": "Mover o copiar" + }, + "admin": { + "page_title": "Panel de Administración", + "back_to_app": "Volver a OxiCloud", + "loading": "Cargando…", + "access_denied": "Acceso Denegado", + "access_denied_desc": "Se requieren privilegios de administrador para acceder a este panel.", + "sign_in": "Iniciar sesión", + "tab_dashboard": "Panel", + "tab_users": "Usuarios", + "tab_oidc": "SSO / OIDC", + "total_users": "Usuarios Totales", + "active_users": "Usuarios Activos", + "admins": "Administradores", + "version": "Versión", + "storage_overview": "Resumen de Almacenamiento", + "used": "Usado", + "total_quota": "Cuota Total", + "usage_pct": "Uso %", + "users_over_80": "Usuarios >80% cuota", + "users_over_quota": "Usuarios sobre cuota", + "system": "Sistema", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Cuotas", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "active": "Activo", + "off": "Inactivo", + "allow_registration": "Permitir registro público", + "registration_warning": "El registro público está deshabilitado. Solo los administradores pueden crear nuevos usuarios.", + "user_management": "Gestión de Usuarios", + "create_user": "Crear Usuario", + "col_user": "Usuario", + "col_role": "Rol", + "col_auth": "Auth", + "col_status": "Estado", + "col_storage": "Almacenamiento", + "col_last_login": "Último Acceso", + "col_actions": "Acciones", + "loading_users": "Cargando usuarios…", + "failed_load_users": "Error al cargar usuarios", + "no_users_found": "No se encontraron usuarios", + "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", + "prev": "Anterior", + "next": "Siguiente", + "inactive": "Inactivo", + "you_badge": "(tú)", + "local": "Local", + "never": "Nunca", + "just_now": "Ahora mismo", + "minutes_ago": "hace {{n}}m", + "hours_ago": "hace {{n}}h", + "days_ago": "hace {{n}}d", + "edit_quota_title": "Editar cuota", + "reset_password_title": "Restablecer contraseña", + "toggle_role_title": "Cambiar rol", + "deactivate_title": "Desactivar", + "activate_title": "Activar", + "delete_title": "Eliminar", + "sso_title": "Inicio de Sesión Único (OIDC / SSO)", + "enable_sso": "Habilitar autenticación SSO", + "provider_name": "Nombre del Proveedor", + "issuer_url": "URL del Emisor", + "issuer_url_hint": "URL del emisor OpenID Connect de tu proveedor de identidad", + "auto_discover": "Auto-descubrir", + "discovering": "Descubriendo…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Dejar vacío para mantener el valor actual", + "secret_configured": "Ya hay un client secret configurado", + "callback_url": "URL de Callback", + "callback_url_hint": "(registrar en tu IdP)", + "advanced_settings": "Configuración Avanzada", + "scopes": "Scopes", + "auto_provision": "Auto-provisionar usuarios en el primer inicio de sesión", + "admin_groups": "Grupos de Admin", + "admin_groups_hint": "Nombres de grupos OIDC separados por comas que mapean al rol de admin", + "disable_password": "Desactivar inicio de sesión con contraseña (solo OIDC)", + "password_warning": "¡Esto impedirá TODOS los inicios de sesión con contraseña!", + "test_btn": "Probar", + "save_btn": "Guardar", + "saving": "Guardando…", + "settings_saved": "Configuración guardada — OIDC ahora está {{status}}", + "quota_modal_title": "Actualizar Cuota de Almacenamiento", + "quota_user_label": "Usuario:", + "new_quota": "Nueva Cuota", + "quota_unlimited_hint": "Establecer 0 para ilimitado", + "cancel": "Cancelar", + "create_user_title": "Crear Nuevo Usuario", + "username_label": "Nombre de usuario", + "username_placeholder": "juanperez", + "username_hint": "3–32 caracteres", + "password_label": "Contraseña", + "password_placeholder": "Mín 8 caracteres", + "email_label": "Correo", + "email_optional": "(opcional)", + "email_placeholder": "usuario@ejemplo.com (auto-generado si vacío)", + "role_label": "Rol", + "role_user": "Usuario", + "role_admin": "Admin", + "quota_label": "Cuota", + "creating": "Creando…", + "reset_pw_title": "Restablecer Contraseña", + "new_password_label": "Nueva Contraseña", + "resetting": "Restableciendo…", + "reset_btn": "Restablecer", + "confirm_role_change": "¿Cambiar rol a {{role}}?", + "confirm_deactivate": "¿Estás seguro de que quieres desactivar este usuario?", + "confirm_activate": "¿Estás seguro de que quieres activar este usuario?", + "confirm_delete_user": "¿ELIMINAR usuario \"{{name}}\"? ¡Esto no se puede deshacer!", + "confirm_action": "Confirmar Acción", + "confirm_yes": "Confirmar", + "confirm_no": "Cancelar", + "error_username_short": "El nombre de usuario debe tener al menos 3 caracteres", + "error_password_short": "La contraseña debe tener al menos 8 caracteres", + "error_generic": "Error", + "error_network": "Error de red: {{message}}", + "error_create_user": "Error al crear usuario", + "tab_storage": "Almacenamiento", + "storage_title": "Backend de Almacenamiento", + "storage_current_backend": "Backend Activo", + "storage_total_blobs": "Total de Blobs", + "storage_total_size": "Tamaño Total", + "storage_dedup_ratio": "Ratio de Dedup", + "storage_backend": "Tipo de Backend", + "storage_local": "Sistema de Archivos Local", + "storage_s3": "Compatible con S3", + "storage_provider_preset": "Proveedor Preconfigurado", + "storage_preset_custom": "Personalizado", + "storage_endpoint_url": "URL del Endpoint", + "storage_endpoint_hint": "Dejar vacío para usar Amazon S3 por defecto", + "storage_bucket": "Bucket", + "storage_region": "Región", + "storage_access_key": "Access Key ID", + "storage_secret_key": "Secret Access Key", + "storage_secret_configured": "Ya hay una clave secreta configurada", + "storage_key_placeholder": "Dejar vacío para mantener el valor actual", + "storage_path_style": "Forzar Path Style", + "storage_path_style_hint": "Requerido para MinIO y algunos proveedores compatibles con S3", + "storage_test_connection": "Probar Conexión", + "storage_test_success": "Conexión exitosa", + "storage_test_failure": "Conexión fallida", + "storage_save": "Guardar", + "storage_saved": "Configuración de almacenamiento guardada correctamente", + "storage_migration": "Migración de Backend", + "storage_migration_coming_soon": "La migración de backend estará disponible en una futura actualización.", + "migration_status_label": "Estado:", + "migration_start": "Iniciar Migración", + "migration_pause": "Pausar", + "migration_resume": "Reanudar", + "migration_verify": "Verificar Integridad", + "migration_complete": "Finalizar", + "migration_started": "Migración iniciada", + "migration_paused_msg": "Migración pausada", + "migration_resumed_msg": "Migración reanudada", + "migration_completed_msg": "Migración finalizada. Reinicia el servidor para usar el nuevo backend.", + "migration_verifying": "Verificando…", + "migration_verify_passed": "Verificación exitosa", + "migration_verify_failed": "Verificación fallida", + "migration_failed_blobs": "blobs fallidos", + "testing": "Probando…", + "smtp_disabled": "Desactivado (host no configurado)", + "smtp_enabled": "Activado", + "smtp_enabled_label": "Estado", + "smtp_intro": "SMTP se configura exclusivamente a través de variables de entorno (OXICLOUD_SMTP_*). Los valores siguientes se leen del servidor en ejecución — para modificarlos, edita el entorno y reinicia OxiCloud.", + "smtp_not_configured": "SMTP no está configurado en este servidor.", + "smtp_send_failed": "Fallo al enviar.", + "smtp_send_test": "Enviar correo de prueba", + "smtp_sending": "Enviando…", + "smtp_sent": "Correo de prueba enviado.", + "smtp_server_code": "Respuesta del servidor", + "smtp_test_intro": "Envía un mensaje de diagnóstico predefinido al destinatario indicado abajo e informa de la respuesta del servidor SMTP para que puedas cruzarla con los registros de tu relay.", + "smtp_test_missing_to": "Introduce una dirección de destinatario.", + "smtp_test_title": "Enviar correo de prueba", + "smtp_test_to": "Dirección del destinatario", + "smtp_title": "Correo saliente (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Administradores", + "confirm_role": "¿Cambiar rol a {{role}}?", + "dashboard": "Panel", + "email": "Correo", + "mig_complete": "Finalizar", + "mig_pause": "Pausar", + "mig_resume": "Reanudar", + "mig_verify_failed": "Verificación fallida", + "mig_verify_passed": "Verificación exitosa", + "mig_verifying": "Verificando…", + "oidc_auto_provision": "Auto-provisionar usuarios en el primer inicio de sesión", + "oidc_callback": "URL de Callback", + "oidc_client_id": "Client ID", + "oidc_disable_pw": "Desactivar inicio de sesión con contraseña (solo OIDC)", + "oidc_issuer": "URL del Emisor", + "oidc_scopes": "Scopes", + "password": "Contraseña", + "quotas": "Cuotas", + "reset_pw_for": "Nueva contraseña para", + "role": "Rol", + "smtp_fail": "Fallo al enviar.", + "smtp_send": "Enviar", + "smtp_test": "Enviar correo de prueba", + "smtp_user_state": "Auth", + "status": "Estado", + "storage": "Almacenamiento", + "storage_endpoint": "URL del Endpoint", + "storage_tab": "Almacenamiento", + "time_min_ago": "hace {{n}} min", + "title": "Admin", + "user": "Usuario", + "username": "Nombre de usuario", + "users": "Usuarios" + }, + "profile": { + "page_title": "Perfil", + "back_to_app": "Volver a OxiCloud", + "loading": "Cargando…", + "not_authenticated": "No Autenticado", + "not_authenticated_desc": "Inicia sesión para ver tu perfil.", + "sign_in": "Iniciar sesión", + "role_admin": "Administrador", + "role_user": "Usuario", + "account_details": "Detalles de la Cuenta", + "username": "Nombre de usuario", + "email": "Correo electrónico", + "role": "Rol", + "last_login": "Último acceso", + "storage": "Almacenamiento", + "used": "Usado", + "quota": "Cuota", + "usage": "Uso", + "unlimited": "Ilimitado", + "app_passwords": "Contraseñas de Aplicación", + "app_pw_desc": "Genera contraseñas para clientes WebDAV, CalDAV y CardDAV. Cada contraseña se muestra solo una vez.", + "app_pw_label_placeholder": "Etiqueta (ej. Thunderbird, macOS)", + "generate": "Generar", + "generating": "Generando…", + "new_password_for": "Nueva contraseña para", + "copy_warning": "Copia esta contraseña ahora. No podrás verla de nuevo.", + "copy_to_clipboard": "Copiar al portapapeles", + "col_label": "Etiqueta", + "col_created": "Creado", + "col_last_used": "Último uso", + "col_status": "Estado", + "active": "Activa", + "revoked": "Revocada", + "revoke_title": "Revocar", + "no_app_passwords": "Aún no hay contraseñas de aplicación.", + "client_sessions": "Sesiones de cliente", + "client_sessions_desc": "Generadas automáticamente al conectar un cliente compatible con Nextcloud.", + "col_client": "Cliente", + "never": "Nunca", + "just_now": "Ahora mismo", + "minutes_ago": "hace {{n}} min", + "hours_ago": "hace {{n}}h", + "days_ago": "hace {{n}} días", + "edit_profile": "Editar perfil", + "edit_oidc_managed": "Para cambiar tu información (nombre, apellidos, foto de perfil, …), actualízala en tu proveedor de identidad. Los cambios se aplicarán en tu próximo inicio de sesión.", + "username_claim_hint": "Entre 2 y 64 caracteres, letras / dígitos / punto / guion / subrayado. Una vez elegido, el nombre de usuario no se puede cambiar (los clientes DAV/NextCloud dependen de él).", + "username_already_claimed": "Nombre de usuario fijado y no modificable (los clientes DAV/NextCloud dependen de él).", + "given_name": "Nombre", + "family_name": "Apellidos", + "notify_on_share": "Enviarme un correo cuando alguien comparta conmigo", + "notify_on_share_hint": "Cuando esté desmarcado, los recursos compartidos seguirán apareciendo en tu cuenta — simplemente no recibirás un correo sobre ellos.", + "save_profile": "Guardar cambios", + "profile_saved": "Perfil actualizado", + "profile_no_changes": "Sin cambios que guardar.", + "profile_save_failed": "Error al guardar", + "username_taken_error": "Ese nombre de usuario ya está en uso.", + "username_immutable_error": "Tu nombre de usuario ya está fijado y no se puede cambiar aquí. Contacta con un administrador si necesitas renombrarlo.", + "change_password": "Cambiar Contraseña", + "current_password": "Contraseña Actual", + "new_password": "Nueva Contraseña", + "min_8_chars": "Al menos 8 caracteres", + "confirm_password": "Confirmar Nueva Contraseña", + "update_password": "Actualizar Contraseña", + "updating": "Actualizando…", + "password_updated": "Contraseña actualizada correctamente", + "passwords_no_match": "Las contraseñas no coinciden", + "password_too_short": "La contraseña debe tener al menos 8 caracteres", + "password_change_failed": "Error al cambiar la contraseña", + "error_network": "Error de red: {{message}}", + "error_label_required": "Introduce una etiqueta", + "error_create_pw": "Error al crear contraseña de aplicación", + "confirm_revoke": "¿Revocar contraseña \"{{label}}\"? Los clientes que la usen dejarán de funcionar.", + "error_revoke": "Error al revocar contraseña", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Las contraseñas no coinciden" + }, + "upload": { + "uploading": "Subiendo...", + "files": "archivos", + "complete": "{{count}} / {{total}} subidos" + }, + "storage_quota_exceeded": "Cuota de almacenamiento superada", + "sharedwithme": { + "pageTitle": "Compartido conmigo", + "pageDescription": "Archivos y carpetas que otros usuarios han compartido contigo", + "emptyStateTitle": "Aún no hay nada compartido contigo", + "emptyStateDesc": "Los elementos que otros usuarios compartan contigo aparecerán aquí", + "loadMore": "Cargar más", + "sharedBy": "Compartido por", + "colName": "Nombre", + "colType": "Tipo", + "colSharedBy": "Compartido por", + "colDate": "Fecha de compartición", + "colPermissions": "Permisos" + }, + "groupby": { + "none": "Ninguno", + "title": "Agrupar por", + "owner": "Propietario", + "shareDate": "Fecha de compartición", + "type": "Tipo", + "type.folders": "Carpetas", + "accessedAt": "Fecha de acceso", + "modifiedAt": "Fecha de modificación", + "createdAt": "Fecha de creación", + "size": "Tamaño", + "favoriteDate": "Fecha de favorito", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nuevo", + "folders": "Carpetas" + }, + "dateBucket": { + "today": "Hoy", + "last7days": "Últimos 7 días", + "last30days": "Últimos 30 días", + "unknown": "Desconocido" + }, + "groups": { + "title": "Gestionar grupos", + "create_button": "Crear grupo", + "create_dialog_title": "Nuevo grupo", + "edit_dialog_title": "Renombrar grupo", + "name_label": "Nombre", + "name_placeholder": "ingenieria", + "description_label": "Descripción (opcional)", + "members_section": "Miembros", + "add_member_placeholder": "Añadir un usuario o grupo…", + "no_members": "Aún no hay miembros.", + "remove_member": "Eliminar", + "delete_group": "Eliminar grupo", + "delete_confirm": "¿Eliminar el grupo «{name}»? Se revocarán las concesiones que hagan referencia a este grupo.", + "empty_state": "Aún no hay grupos.", + "load_more": "Cargar más", + "back_to_list": "Volver", + "loading": "Cargando…", + "virtual_badge": "Sistema", + "member_count_zero": "Sin miembros", + "member_count_one": "1 miembro", + "member_count_other": "{count} miembros", + "delete_confirm_label": "Escribe el nombre del grupo para confirmar:", + "delete_confirm_mismatch": "Escribe el nombre del grupo exactamente para confirmar.", + "virtual_internal_name": "Interno", + "members_loading": "Cargando miembros…", + "members_empty": "Sin miembros", + "virtual_internal_explanation": "Todos los usuarios internos de este servidor", + "create": "Crear grupo", + "empty": "Aún no hay grupos.", + "members": "Miembros" + }, + "myshares": { + "copyLink": "Copiar enlace", + "deleteLink": "Eliminar enlace", + "notifyByEmail": "Notificar por correo", + "notifyFailed": "No se pudo enviar la notificación.", + "notifyGroupMembers": "Notificar a los miembros del grupo", + "notifyRateLimited": "Demasiadas notificaciones para este destinatario — inténtalo más tarde.", + "removeAccess": "Quitar acceso", + "resendInvitation": "Reenviar correo de invitación", + "publicLinks": "Enlaces públicos" + }, + "sort": { + "asc": "ascendente", + "desc": "descendente" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error al realizar la búsqueda", + "cleanupCompleted": "Limpieza completada", + "cleanupCompletedBody": "Se ha borrado el historial de archivos recientes", + "batchCopy": "Copia en lote", + "batchCopyBody": "{{success}} copiados, {{errors}} fallidos", + "itemsCopied": "Elementos copiados", + "itemsCopiedBody": "{{count}} elementos copiados correctamente", + "batchMove": "Movimiento en lote", + "batchMoveBody": "{{success}} movidos, {{errors}} fallidos", + "itemsMoved": "Elementos movidos", + "itemsMovedBody": "{{count}} elementos movidos correctamente", + "batchDelete": "Borrado en lote", + "batchDeleteBody": "{{success}} movidos a la papelera, {{errors}} fallidos", + "movedToTrash": "Movido a la papelera", + "movedToTrashBody": "{{count}} elementos movidos a la papelera", + "trashItemsError": "No se pudieron mover los elementos a la papelera", + "preparingDownload": "Preparando descarga", + "preparingDownloadBody": "Preparando la descarga…", + "downloadItemsError": "No se pudieron descargar los elementos seleccionados", + "favoritesAddError": "No se pudieron añadir los elementos a favoritos", + "invalidEmail": "Introduce una dirección de correo válida", + "notificationSendError": "No se pudo enviar la notificación", + "folderCreated": "Carpeta creada", + "folderCreatedBody": "«{{name}}» creada correctamente", + "fileMoved": "Archivo movido", + "fileMovedBody": "Archivo movido correctamente", + "fileMoveError": "Error al mover el archivo: {{error}}", + "fileMoveErrorGeneric": "Error al mover el archivo", + "folderMoved": "Carpeta movida", + "folderMovedBody": "Carpeta movida correctamente", + "folderMoveError": "Error al mover la carpeta: {{error}}", + "folderMoveErrorGeneric": "Error al mover la carpeta", + "fileCopied": "Archivo copiado", + "fileCopiedBody": "Archivo copiado correctamente", + "fileCopyError": "Error al copiar el archivo: {{error}}", + "fileCopyErrorGeneric": "Error al copiar el archivo", + "folderRenamed": "Carpeta renombrada", + "folderRenamedBody": "Carpeta renombrada a «{{name}}»", + "fileTrashed": "Archivo movido a la papelera", + "fileTrashedBody": "«{{name}}» movido a la papelera", + "fileDeleted": "Archivo eliminado", + "fileDeletedBody": "«{{name}}» eliminado correctamente", + "fileDeleteError": "Error al eliminar el archivo", + "folderTrashed": "Carpeta movida a la papelera", + "folderTrashedBody": "«{{name}}» movida a la papelera", + "folderDeleted": "Carpeta eliminada", + "folderDeletedBody": "«{{name}}» eliminada correctamente", + "folderDeleteError": "Error al eliminar la carpeta", + "itemRestored": "Elemento restaurado", + "itemRestoredBody": "Elemento restaurado correctamente", + "itemRestoreError": "Error al restaurar el elemento", + "itemDeleted": "Elemento eliminado", + "itemDeletedBody": "Elemento eliminado permanentemente", + "itemDeleteError": "Error al eliminar el elemento", + "trashEmptied": "Papelera vaciada", + "trashEmptiedBody": "La papelera se ha vaciado correctamente", + "trashEmptyError": "Error al vaciar la papelera", + "cacheCleared": "Caché limpiada", + "cacheClearedBody": "Caché de búsqueda limpiada correctamente", + "cacheClearError": "Error al limpiar la caché de búsqueda", + "wopiOpenError": "No se pudo abrir el editor de documentos.", + "linkCopied": "Enlace copiado", + "linkCopiedBody": "Enlace copiado al portapapeles", + "linkCopyError": "No se pudo copiar el enlace", + "notificationSent": "Notificación enviada", + "notificationSentBody": "Notificación enviada a {{email}}" + }, + "category": { + "audio": "Audio", + "code": "Código", + "text": "Texto" + }, + "common": { + "add": "Añadir", + "cancel": "Cancelar", + "clear": "Limpiar", + "close": "Cerrar", + "confirm": "Confirmar", + "copy": "Copiar", + "create": "Crear", + "delete": "Eliminar", + "download": "Descargar", + "load_more": "Cargar más", + "loading": "Cargando…", + "next": "Siguiente", + "no": "No", + "previous": "Anterior", + "remove": "Eliminar", + "rename": "Renombrar", + "save": "Guardar", + "search": "Buscar", + "yes": "Sí" + }, + "device": { + "continue": "Continuar", + "unknown": "Desconocido" + }, + "expiryBucket": { + "expired": "Caducado", + "noExpiry": "Sin caducidad", + "today": "Hoy", + "tomorrow": "Mañana" + }, + "nextcloud": { + "error_title": "Error", + "sign_in_with": "Iniciar sesión con {{provider}}" + }, + "search": { + "size_label": "Tamaño", + "title": "Buscar", + "type": { + "audio": "Audio" + }, + "type_label": "Tipo" + }, + "sizeBucket": { + "folders": "Carpetas" + }, + "view": { + "grid": "Vista de cuadrícula", + "list": "Vista de lista" + } } diff --git a/frontend/static/locales/fa.json b/frontend/static/locales/fa.json index b99bd940..69228880 100644 --- a/frontend/static/locales/fa.json +++ b/frontend/static/locales/fa.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "این پیوند ورود دیگر معتبر نیست", - "expired_body": "ممکن است پیوند منقضی شده یا قبلاً استفاده شده باشد. می‌توانیم پیوند جدیدی برایتان ارسال کنیم — ظرف چند ثانیه به صندوق ورودی شما می‌رسد.", - "resend_to": "ارسال پیوند جدید به {{email}}", - "generic_unavailable": "این پیوند ورود دیگر معتبر نیست. ممکن است قبلاً استفاده شده باشد یا منقضی شده باشد. پیوند جدیدی را از صفحهٔ ورود درخواست کنید.", - "service_unavailable": "ورود از طریق پیوند جادویی روی این سرور فعال نیست.", - "internal_error": "هنگام ورود خطایی رخ داد. لطفاً دوباره تلاش کنید.", - "resend_failure": "هنگام ارسال پیوند خطایی رخ داد. لطفاً دوباره تلاش کنید.", - "cross_browser_title": "آیا می‌خواهید ورود در این دستگاه ادامه یابد؟", - "cross_browser_body": "این پیوند ورود را در مرورگر یا دستگاهی متفاوت از جایی که درخواست کرده‌اید باز کرده‌اید.", - "cross_browser_warning": "اگر این پیوند را خودتان درخواست کرده‌اید، ادامه دادن ایمن است. در غیر این صورت این صفحه را ببندید — کلیک روی ادامه باعث ورود شخص دیگری به حساب شما خواهد شد.", - "cross_browser_continue": "ادامه و ورود", - "resend_confirmation_title": "صندوق ورودی خود را بررسی کنید", - "resend_confirmation_body": "اگر پیوند ورود متعلق به یک حساب فعال بوده، پیوند جدیدی هم اکنون ارسال شد. لطفاً صندوق ورودی خود را بررسی کنید.", - "return_link": "بازگشت به OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", - "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبا کلیک روی پیوند زیر آن را باز کنید:\n{{link}}\n\nپیوند یک‌بار مصرف است و در {{ttl_hours}} ساعت منقضی می‌شود.\nاگر منتظر این دعوت نبودید، می‌توانید این پیام را نادیده بگیرید.\n\n— OxiCloud" - }, - "login": { - "subject": "ورود به OxiCloud", - "body": "سلام،\n\nبرای ورود به OxiCloud از پیوند زیر استفاده کنید. پیوند یک‌بار مصرف است و در {{ttl_minutes}} دقیقه منقضی می‌شود. آن را در همان دستگاهی که درخواست کرده‌اید باز کنید.\n\n{{link}}\n\nاگر این پیوند ورود را درخواست نکرده‌اید، می‌توانید این پیام را نادیده بگیرید — اقدام دیگری لازم نیست.\n\n— OxiCloud" - }, - "kind_file": "فایل", - "kind_folder": "پوشه", - "english_fallback_divider": "--- نسخهٔ انگلیسی در پایین ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "این پیوند ورود دیگر معتبر نیست", + "expired_body": "ممکن است پیوند منقضی شده یا قبلاً استفاده شده باشد. می‌توانیم پیوند جدیدی برایتان ارسال کنیم — ظرف چند ثانیه به صندوق ورودی شما می‌رسد.", + "resend_to": "ارسال پیوند جدید به {{email}}", + "generic_unavailable": "این پیوند ورود دیگر معتبر نیست. ممکن است قبلاً استفاده شده باشد یا منقضی شده باشد. پیوند جدیدی را از صفحهٔ ورود درخواست کنید.", + "service_unavailable": "ورود از طریق پیوند جادویی روی این سرور فعال نیست.", + "internal_error": "هنگام ورود خطایی رخ داد. لطفاً دوباره تلاش کنید.", + "resend_failure": "هنگام ارسال پیوند خطایی رخ داد. لطفاً دوباره تلاش کنید.", + "cross_browser_title": "آیا می‌خواهید ورود در این دستگاه ادامه یابد؟", + "cross_browser_body": "این پیوند ورود را در مرورگر یا دستگاهی متفاوت از جایی که درخواست کرده‌اید باز کرده‌اید.", + "cross_browser_warning": "اگر این پیوند را خودتان درخواست کرده‌اید، ادامه دادن ایمن است. در غیر این صورت این صفحه را ببندید — کلیک روی ادامه باعث ورود شخص دیگری به حساب شما خواهد شد.", + "cross_browser_continue": "ادامه و ورود", + "resend_confirmation_title": "صندوق ورودی خود را بررسی کنید", + "resend_confirmation_body": "اگر پیوند ورود متعلق به یک حساب فعال بوده، پیوند جدیدی هم اکنون ارسال شد. لطفاً صندوق ورودی خود را بررسی کنید.", + "return_link": "بازگشت به OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", + "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبا کلیک روی پیوند زیر آن را باز کنید:\n{{link}}\n\nپیوند یک‌بار مصرف است و در {{ttl_hours}} ساعت منقضی می‌شود.\nاگر منتظر این دعوت نبودید، می‌توانید این پیام را نادیده بگیرید.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", - "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبرای دیدن اشتراک‌گذاری جدید خود، OxiCloud را باز کنید:\n{{login_link}}\n\nممکن است اشتراک‌گذاری‌های جدید دیگری از {{inviter}} داشته باشید — وارد شوید تا همه موارد به اشتراک گذاشته‌شده با خود را ببینید.\n\n— OxiCloud\n\nشما این پیام را دریافت می‌کنید زیرا حساب OxiCloud دارید و گزینه اعلان اشتراک‌گذاری شما روشن است. می‌توانید آن را در پروفایل خود خاموش کنید (وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "سیستم ذخیره‌سازی ابری ساده‌گرا" - }, - "nav": { - "files": "پرونده‌ها", - "shared": "هم‌رسانی‌های من", - "recent": "اخیر", - "favorites": "موردعلاقه‌ها", - "photos": "عکس‌ها", - "music": "موسیقی", - "trash": "سطل زباله", - "sharedwithme": "به اشتراک‌گذاشته شده با من" - }, - "photos": { - "empty_state": "هنوز عکسی نیست", - "empty_hint": "تصاویر یا ویدیوها را آپلود کنید تا اینجا نمایش داده شوند", - "items_selected": "انتخاب شده", - "view_daily": "روز", - "view_monthly": "ماه", - "view_yearly": "سال" - }, - "music": { - "create_playlist": "ایجاد فهرست پخش", - "playlists": "فهرست‌های پخش", - "no_playlists": "هنوز فهرست پخشی نیست", - "select_playlist": "یک فهرست پخش انتخاب کنید", - "select_hint": "از نوار کناری یک فهرست پخش انتخاب کنید یا یکی جدید بسازید", - "add_tracks": "افزودن آهنگ‌ها", - "no_tracks": "هیچ آهنگی در این فهرست پخش نیست", - "unknown_artist": "هنرمند ناشناس", - "unknown_title": "ناشناس", - "confirm_delete": "این فهرست پخش حذف شود؟", - "playlist_name": "نام فهرست پخش", - "create": "ایجاد", - "delete": "حذف", - "share": "هم‌رسانی", - "edit": "ویرایش", - "play_all": "پخش همه", - "shuffle": "تصادفی", - "repeat": "تکرار", - "repeat_one": "تکرار یک", - "queue": "صف", - "queue_empty": "صف خالی است", - "not_playing": "در حال پخش نیست", - "play": "پخش", - "pause": "توقف", - "previous": "قبلی", - "next": "بعدی", - "volume": "صدا", - "mute": "بی‌صدا", - "unmute": "صدا فعال", - "title": "عنوان", - "artist": "هنرمند", - "album": "آلبوم", - "tracks": "آهنگ", - "add": "افزودن", - "added": "افزوده شد!", - "added_to_playlist": "به فهرست پخش افزوده شد", - "add_to_playlist": "افزودن به فهرست پخش", - "load_error": "خطا در بارگیری فهرست پخش", - "add_error": "امکان افزودن آهنگ‌ها به فهرست پخش نیست", - "no_playlists_yet": "فهرست پخشی وجود ندارد. اول یکی بسازید!", - "selected_files": "انتخاب شده:", - "error": "خطا", - "search_audio": "جستجوی فایل‌های صوتی…", - "no_audio_files": "فایل صوتی یافت نشد", - "selected": "انتخاب شده", - "loading": "در حال بارگذاری…", - "search_error": "بارگذاری فایل‌های صوتی ممکن نشد", - "adding": "در حال افزودن…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "جست‌و‌جوی پرونده‌ها..", - "new_folder": "پوشهٔ جدید", - "upload": "بارگذاری", - "upload_files": "بارگذاری پرونده‌ها", - "upload_folder": "بارگذاری پوشه", - "upload.uploading": "...در حال بارگذاری", - "upload.complete": "{count} / {total} بارگذاری شد", - "upload.files": "فایل‌ها", - "rename": "تغییر نام", - "move": "انتقال به...", - "move_to": "انتقال به", - "delete": "حذف", - "download": "بارگیری", - "view": "مشاهده", - "cancel": "لغو", - "confirm": "تأیید", - "share": "هم‌رسانی", - "favorite": "افزودن به موردعلاقه‌ها", - "unfavorite": "حذف از موردعلاقه‌ها", - "copy": "رونوشت", - "notify": "آگاه‌سازی", - "send": "ارسال", - "clear_recent": "پاک‌کردن موارد اخیر", - "logout": "خروج", - "create": "ایجاد", - "search_btn": "جست‌و‌جو", - "close": "بستن", - "delete_permanently": "Delete permanently", - "empty_trash": "Empty trash", - "open_parent_folder": "رفتن به پوشه والد", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "ظاهر", - "about": "درباره OxiCloud", - "about_description": "پلتفرم ذخیره‌سازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی.", - "admin_panel": "پنل مدیریت", - "profile": "نمایه من", - "role_user": "کاربر", - "theme": { - "light": "روشن", - "dark": "تاریک", - "auto": "مانند سیستم" + "login": { + "subject": "ورود به OxiCloud", + "body": "سلام،\n\nبرای ورود به OxiCloud از پیوند زیر استفاده کنید. پیوند یک‌بار مصرف است و در {{ttl_minutes}} دقیقه منقضی می‌شود. آن را در همان دستگاهی که درخواست کرده‌اید باز کنید.\n\n{{link}}\n\nاگر این پیوند ورود را درخواست نکرده‌اید، می‌توانید این پیام را نادیده بگیرید — اقدام دیگری لازم نیست.\n\n— OxiCloud" }, - "manage_groups": "مدیریت گروه‌ها" + "kind_file": "فایل", + "kind_folder": "پوشه", + "english_fallback_divider": "--- نسخهٔ انگلیسی در پایین ---" + } }, - "share": { - "dialogTitle": "پیوند هم‌رسانی", - "linkLabel": "پیوند هم‌سانی:", - "copyLink": "رونوشت", - "permissions": "دسترسی‌ها:", - "permissionRead": "خواندن", - "permissionWrite": "نوشتن", - "permissionReshare": "هم‌رسانی دوباره", - "password": "محافظت با گذرواژه:", - "generatePassword": "تولید", - "expiration": "تاریخ انقضا:", - "update": "به‌روزرسانی هم‌رسانی", - "remove": "پاک‌کردن هم‌رسانی", - "notifyTitle": "ارسال آگاه‌سازی", - "notifyEmailLabel": "نشانی رایانامه:", - "notifyMessageLabel": "پیام (اختیاری):", - "notifySend": "ارسال آگاه‌سازی", - "shareWithOthers": "هم‌رسانی با دیگران", - "sharePublicly": "هم‌رسانی عمومی", - "shareSettings": "تنظیمات هم‌رسانی", - "shareCopied": "پیوند به بُریده‌دان رونوشت شد", - "shareCreated": "پیوند هم‌رسانی با موفقیت ایجاد شد", - "shareUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", - "shareRemoved": "هم‌رسانی با موفقیت پاک شد", - "inviteByEmail": "دعوت از طریق ایمیل — دعوت ارسال خواهد شد", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "پیوند هم‌رسانی", - "share_linkLabel": "پیوند هم‌رسانی:", - "share_copyLink": "رونوشت", - "share_permissions": "دسترسی‌ها:", - "share_permissionRead": "خواندن", - "share_permissionWrite": "نوشتن", - "share_permissionReshare": "هم‌رسانی دوباره", - "share_password": "محافظت با گذرواژه:", - "share_generatePassword": "تولید", - "share_expiration": "تاریخ انقضا:", - "share_update": "به‌روزرسانی هم‌رسانی", - "share_remove": "پاک‌کردن هم‌رسانی", - "share_notifyTitle": "ارسال آگاه‌سازی", - "share_notifyEmailLabel": "نشانی رایانامه:", - "share_notifyMessageLabel": "پیام (اختیاری):", - "share_notifySend": "ارسال آگاه‌سازی", - "shared": { - "backToFiles": "بازگشت به پرونده‌ها", - "pageTitle": "منابع هم‌رسانی شده", - "pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", - "filterType": "نوع:", - "filterAll": "همه", - "filterFiles": "پرونده‌ها", - "filterFolders": "پوشه‌ها", - "sortBy": "مرتب‌سازی بر اساس:", - "sortByName": "نام", - "sortByDate": "تاریخ هم‌رسانی", - "sortByExpiration": "تاریخ انقضا", - "search": "جست‌و‌جو", - "colName": "نام", - "colType": "نوع", - "colDateShared": "تاریخ هم‌رسانی", - "colExpiration": "تاریخ انقضا", - "colPermissions": "دسترسی‌ها", - "colPassword": "گذرواژه", - "colActions": "عملیات", - "emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", - "emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", - "goToFiles": "رفتن به پرونده‌ها", - "typeFile": "پرونده", - "typeFolder": "پوشه", - "noExpiration": "بدون انقضا", - "hasPassword": "بله", - "noPassword": "خیر", - "editShare": "ویرایش هم‌رسانی", - "notifyShare": "آگاه‌سازی کسی", - "copyLink": "رونوشت پیوند", - "removeShare": "حذف هم‌رسانی", - "linkCopied": "پیوند به بُریده‌دان رونوشت شد", - "linkCopyFailed": "رونوشت پیوند ناموفق بود", - "itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", - "itemRemoved": "هم‌رسانی با موفقیت پاک شد", - "invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", - "notificationSent": "آگاه‌سازی با موفقیت ارسال شد", - "notificationFailed": "ارسال آگاه‌سازی ناموفق بود", - "shared_backToFiles": "بازگشت به پرونده‌ها", - "shared_pageTitle": "منابع هم‌رسانی شده", - "shared_pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", - "shared_filterType": "نوع:", - "shared_filterAll": "همه", - "shared_filterFiles": "پرونده‌ها", - "shared_filterFolders": "پوشه‌ها", - "shared_sortBy": "مرتب‌سازی بر اساس:", - "shared_sortByName": "نام", - "shared_sortByDate": "تاریخ هم‌رسانی", - "shared_sortByExpiration": "تاریخ انقضا", - "shared_search": "جست‌و‌جو", - "shared_colName": "نام", - "shared_colType": "نوع", - "shared_colDateShared": "تاریخ هم‌رسانی", - "shared_colExpiration": "تاریخ انقضا", - "shared_colPermissions": "دسترسی‌ها", - "shared_colPassword": "گذرواژه", - "shared_colActions": "عملیات", - "shared_emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", - "shared_emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", - "shared_goToFiles": "رفتن به پرونده‌ها", - "shared_typeFile": "پرونده", - "shared_typeFolder": "پوشه", - "shared_noExpiration": "بدون انقضا", - "shared_hasPassword": "بله", - "shared_noPassword": "خیر", - "shared_editShare": "ویرایش هم‌رسانی", - "shared_notifyShare": "آگاه‌سازی کسی", - "shared_copyLink": "رونوشت پیوند", - "shared_removeShare": "حذف هم‌رسانی", - "shared_linkCopied": "پیوند به بُریده‌دان رونوشت شد", - "shared_linkCopyFailed": "رونوشت پیوند ناموفق بود", - "shared_itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", - "shared_itemRemoved": "هم‌رسانی با موفقیت پاک شد", - "shared_invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", - "shared_notificationSent": "آگاه‌سازی با موفقیت ارسال شد", - "shared_notificationFailed": "ارسال آگاه‌سازی ناموفق بود" - }, - "files": { - "name": "نام", - "type": "نوع", - "size": "اندازه", - "modified": "تاریخ تغییر", - "no_files": "هنوز هیچ پرونده‌ای در این پوشه وجود ندارد", - "empty_hint": "برای شروع، فایل‌ها را آپلود کنید یا پوشه بسازید", - "loading": "در حال بارگذاری فایل‌ها…", - "view_grid": "نمای شبکه‌ای", - "view_list": "نمای فهرستی", - "file_types": { - "document": "سند", - "image": "تصویر", - "video": "ویدیو", - "audio": "صوتی", - "pdf": "PDF", - "text": "متن", - "folder": "پوشه", - "spreadsheet": "صفحه گسترده", - "presentation": "ارائه", - "archive": "بایگانی", - "installer": "نصب‌کننده", - "code": "کد" - }, - "owner": "مالک" - }, - "dialogs": { - "rename_folder": "تغییر نام پوشه", - "new_name": "نام جدید", - "new_folder_title": "پوشه جدید", - "folder_name": "نام پوشه", - "folder_placeholder": "پوشه من", - "rename_title": "تغییر نام", - "move_file": "انتقال پرونده", - "select_destination": "انتخاب پوشهٔ مقصد", - "root": "ریشه", - "delete_confirmation": "آیا مطمئن هستید که می‌خواهید حذف کنید", - "and_contents": "و همهٔ محتویات آن", - "no_undo": "این عملیات قابل بازگردانی نیست", - "share_file": "هم‌رسانی پرونده", - "share_folder": "هم‌رسانی پوشه", - "existing_shares": "هم‌رسانی موجود", - "share_options": "گزینه‌های هم‌رسانی", - "password": "گذرواژه", - "expiration": "تاریخ انقضا", - "permissions": "دسترسی‌ها", - "generated_link": "پیوند تولید شده", - "notify": "ارسال آگاه‌سازی", - "recipient": "گیرنده", - "message": "پیام", - "confirm_delete": "Move to trash", - "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", - "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", - "confirm_delete_share": "Delete share link", - "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", - "confirm_empty_trash": "Empty trash", - "confirm_permanent_delete": "Delete permanently", - "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", - "confirm_title": "Confirm action", - "go_to_parent": ".. (parent folder)", - "move_folder": "Move folder", - "no_subfolders": "No subfolders", - "rename_file": "Rename file", - "select_this_folder": "Select this folder", - "move_to_home": "انتقال به پوشه خانگی" - }, - "dropzone": { - "drag_files": "پرونده‌ها را اینجا بکشید یا برای انتخاب کلیک کنید", - "drop_files": "پرونده‌ها را رها کنید تا بارگذاری شوند" - }, - "permissions": { - "read": "خواندن", - "write": "نوشتن", - "reshare": "هم‌رسانی دوباره" - }, - "errors": { - "file_not_found": "پرونده پیدا نشد", - "folder_not_found": "پوشه پیدا نشد", - "delete_error": "خطا در پاک کردن", - "upload_error": "خطا در بارگذاری پرونده", - "rename_error": "خطا در تغییر نام", - "move_error": "خطا در انتقال", - "empty_name": "نام نمی‌تواند خالی باشد", - "name_exists": "پرونده یا پوشه‌ای با این نام قبلا وجود دارد", - "generic_error": "خطایی رخ داده است", - "group_name_invalid": "نام گروه باید با قالب پیشوند ایمیل مطابقت داشته باشد (حروف، ارقام، نقطه، خط تیره، زیرخط؛ 1–64 نویسه).", - "group_cycle": "این عضو باعث ایجاد ارجاع چرخه‌ای بین گروه‌ها می‌شود.", - "group_depth_exceeded": "عمق تودرتو بیش از حداکثر مجاز (8) است.", - "group_virtual_immutable": "گروه «Internal» توسط سامانه مدیریت می‌شود و قابل تغییر نیست.", - "group_not_found": "گروه پیدا نشد.", - "group_name_taken": "گروهی با این نام پیش‌از این وجود دارد." - }, - "breadcrumb": { - "home": "صفحه اصلی" - }, - "trash": { - "empty_trash": "خالی کردن سطل زباله", - "empty_state": "سطل زباله خالی است", - "original_location": "محل اصلی", - "deleted_date": "تاریخ حذف", - "remaining": "باقی‌مانده", - "actions": "عملیات", - "restore": "بازیابی", - "delete_permanently": "حذف دائمی", - "empty_confirm": "آیا مطمئن هستید که می‌خواهید سطل زباله را خالی کنید؟ این کار همهٔ موارد را به‌طور دائمی حذف خواهد کرد.", - "groupby": { - "remaining_days": "روزهای باقی‌مانده", - "trashed_time": "زمان حذف" - } - }, - "daysRemaining": { - "expired": "منقضی شده", - "today": "امروز", - "tomorrow": "فردا", - "inDays": "{{count}} روز" - }, - "expiryChip": { - "never": "هرگز منقضی نمی‌شود", - "expired": "منقضی شده", - "today": "امروز منقضی می‌شود", - "tomorrow": "فردا منقضی می‌شود", - "inDays": "در {{count}} روز منقضی می‌شود", - "onDate": "در {{date}} منقضی می‌شود" - }, - "auth": { - "login_title": "ورود", - "username": "نام‌کاربری", - "username_placeholder": "نام‌کاربری خود را وارد کنید", - "login_identifier": "نام کاربری یا ایمیل", - "login_identifier_placeholder": "نام کاربری یا ایمیل خود را وارد کنید", - "password": "گذرواژه", - "password_placeholder": "گذرواژه خود را وارد کنید", - "login_button": "ورود", - "no_account": "حساب کاربری ندارید؟", - "register": "نام‌نویسی", - "admin_setup": "اولین بار است؟", - "setup": "راه‌اندازی اولیه مدیریت", - "register_title": "ایجاد حساب کاربری", - "email": "رایانامه", - "email_placeholder": "رایانامه خود را وارد کنید", - "confirm_password": "تأیید گذرواژه", - "confirm_password_placeholder": "گذرواژه خود را تأیید کنید", - "register_button": "ایجاد حساب کاربری", - "have_account": "حساب کاربری دارید؟", - "login": "ورود", - "setup_title": "راه‌اندازی اولیه", - "setup_step1": "مدیر", - "setup_step2": "سیستم", - "setup_step3": "تکمیل", - "admin_username": "نام‌کاربری مدیر", - "admin_email": "رایانامه مدیر", - "admin_password": "گذرواژه مدیر", - "create_admin": "ایجاد مدیر", - "back_to_login": "قبلا راه‌اندازی شده است؟", - "admin_success": "حساب کاربری مدیر با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", - "account_success": "حساب کاربری با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", - "passwords_mismatch": "گذرواژه‌ها مطابقت ندارند", - "admin_create_error": "خطا در ایجاد حساب کاربری مدیر", - "or": "یا", - "sso_login": "ورود با SSO", - "sso_login_provider": "ورود با {{provider}}", - "magicLinkHint": "رمز عبور ندارید؟ ایمیل خود را وارد کنید تا یک پیوند ورود یک‌بار‌مصرف برایتان ارسال شود.", - "magicLinkEmailLabel": "آدرس ایمیل", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "ارسال پیوند ورود", - "magicLinkSent": "اگر برای این ایمیل حسابی وجود داشته باشد، پیوند ورود ارسال شده است. صندوق ورودی خود را بررسی کنید.", - "magicLinkUnavailable": "ورود با ایمیل در این سرور در دسترس نیست.", - "magicLinkNetworkError": "ارتباط با سرور برقرار نشد: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "فضای ذخیره‌سازی", - "calculating": "در حال محاسبه...", - "used": "{{percentage}}% استفاده شده ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "این نوع پرونده قابل پیش‌نمایش نیست.", - "download_file": "بارگیری پرونده", - "zoom_in": "بزرگ‌نمایی", - "zoom_out": "کوچک‌نمایی", - "zoom_reset": "بازنشانی بزرگ‌نمایی" - }, - "language_selector": { - "title": "!خوش آمدید", - "subtitle": "زبان خود را برای ادامه انتخاب کنید", - "continue": "ادامه", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "هنوز هیچ مورد علاقه‌ای وجود ندارد", - "empty_hint": "برای افزودن به موارد علاقه‌مند، پرونده‌ها یا پوشه‌ها را ستاره‌دار کنید", - "add": "افزودن به موارد علاقه‌مند", - "remove": "حذف از موارد علاقه‌مند", - "added_title": "به موارد علاقه‌مند افزوده شد", - "added_msg": "به موارد علاقه‌مند افزوده شد", - "removed_title": "از موارد علاقه‌مند حذف شد", - "removed_msg": "از موارد علاقه‌مند حذف شد" - }, - "recent": { - "title": "اخیر", - "clear": "پاک کردن اخیر", - "accessed": "دسترسی یافته", - "empty_state": "هنوز هیچ پروندهٔ اخیر وجود ندارد", - "empty_hint": "پرونده‌هایی که باز می‌کنید اینجا ظاهر می‌شوند", - "loadMore": "بارگذاری بیشتر" - }, - "batch": { - "one_selected": "۱ مورد انتخاب شده", - "n_selected": "{{count}} مورد انتخاب شده", - "confirm_delete": "آیا مطمئنید که می‌خواهید {{count}} مورد را به سطل زباله منتقل کنید؟", - "move_title": "انتقال {{count}} مورد", - "add_favorites": "افزودن به موارد علاقه‌مند", - "move_copy": "انتقال یا کپی" - }, - "admin": { - "page_title": "پنل مدیریت", - "back_to_app": "بازگشت به OxiCloud", - "loading": "در حال بارگذاری…", - "access_denied": "دسترسی ممنوع", - "access_denied_desc": "امتیازات مدیر لازم است.", - "sign_in": "ورود", - "tab_dashboard": "داشبورد", - "tab_users": "کاربران", - "tab_oidc": "SSO / OIDC", - "total_users": "کل کاربران", - "active_users": "کاربران فعال", - "admins": "مدیران", - "version": "نسخه", - "storage_overview": "نمای کلی فضا", - "used": "استفاده شده", - "total_quota": "سهمیه کل", - "usage_pct": "درصد استفاده", - "users_over_80": "کاربران بالای ۸۰٪", - "users_over_quota": "کاربران بالای سهمیه", - "system": "سیستم", - "auth_label": "احراز هویت", - "oidc_label": "OIDC", - "quotas_label": "سهمیه‌ها", - "enabled": "فعال", - "disabled": "غیرفعال", - "active": "فعال", - "off": "خاموش", - "allow_registration": "اجازه ثبت‌نام عمومی", - "registration_warning": "ثبت‌نام عمومی غیرفعال است. فقط مدیران می‌توانند کاربر جدید بسازند.", - "user_management": "مدیریت کاربران", - "create_user": "ایجاد کاربر", - "col_user": "کاربر", - "col_role": "نقش", - "col_auth": "احراز هویت", - "col_status": "وضعیت", - "col_storage": "فضا", - "col_last_login": "آخرین ورود", - "col_actions": "عملیات", - "loading_users": "در حال بارگذاری…", - "failed_load_users": "خطا در بارگذاری", - "no_users_found": "کاربری یافت نشد", - "showing_users": "نمایش {{from}}-{{to}} از {{total}}", - "prev": "قبلی", - "next": "بعدی", - "inactive": "غیرفعال", - "you_badge": "(شما)", - "local": "محلی", - "never": "هرگز", - "just_now": "همین الان", - "minutes_ago": "{{n}} دقیقه پیش", - "hours_ago": "{{n}} ساعت پیش", - "days_ago": "{{n}} روز پیش", - "edit_quota_title": "ویرایش سهمیه", - "reset_password_title": "بازنشانی رمز", - "toggle_role_title": "تغییر نقش", - "deactivate_title": "غیرفعال کردن", - "activate_title": "فعال کردن", - "delete_title": "حذف", - "sso_title": "ورود یکپارچه (OIDC / SSO)", - "enable_sso": "فعال‌سازی SSO", - "provider_name": "نام ارائه‌دهنده", - "issuer_url": "آدرس صادرکننده", - "issuer_url_hint": "آدرس صادرکننده OpenID Connect", - "auto_discover": "کشف خودکار", - "discovering": "در حال کشف…", - "client_id": "شناسه مشتری", - "client_secret": "رمز مشتری", - "client_secret_placeholder": "خالی بگذارید تا مقدار فعلی حفظ شود", - "secret_configured": "رمز مشتری قبلاً پیکربندی شده", - "callback_url": "آدرس بازگشت", - "callback_url_hint": "(در IdP ثبت کنید)", - "advanced_settings": "تنظیمات پیشرفته", - "scopes": "محدوده‌ها", - "auto_provision": "تامین خودکار کاربران", - "admin_groups": "گروه‌های مدیر", - "admin_groups_hint": "نام گروه‌های OIDC جدا شده با کاما", - "disable_password": "غیرفعال‌سازی ورود با رمز (فقط OIDC)", - "password_warning": "تمام ورودهای رمزی متوقف می‌شود!", - "test_btn": "آزمایش", - "save_btn": "ذخیره", - "saving": "در حال ذخیره…", - "settings_saved": "تنظیمات ذخیره شد — OIDC اکنون {{status}}", - "quota_modal_title": "به‌روزرسانی سهمیه", - "quota_user_label": "کاربر:", - "new_quota": "سهمیه جدید", - "quota_unlimited_hint": "۰ برای نامحدود", - "cancel": "انصراف", - "create_user_title": "ایجاد کاربر جدید", - "username_label": "نام کاربری", - "username_placeholder": "نام‌کاربری", - "username_hint": "۳ تا ۳۲ کاراکتر", - "password_label": "رمز عبور", - "password_placeholder": "حداقل ۸ کاراکتر", - "email_label": "ایمیل", - "email_optional": "(اختیاری)", - "email_placeholder": "user@example.com (خودکار اگر خالی)", - "role_label": "نقش", - "role_user": "کاربر", - "role_admin": "مدیر", - "quota_label": "سهمیه", - "creating": "در حال ایجاد…", - "reset_pw_title": "بازنشانی رمز عبور", - "new_password_label": "رمز عبور جدید", - "resetting": "در حال بازنشانی…", - "reset_btn": "بازنشانی", - "confirm_role_change": "نقش به {{role}} تغییر یابد؟", - "confirm_deactivate": "آیا از غیرفعال‌سازی این کاربر مطمئنید؟", - "confirm_activate": "آیا از فعال‌سازی این کاربر مطمئنید؟", - "confirm_delete_user": "کاربر «{{name}}» حذف شود؟ قابل بازگشت نیست!", - "confirm_action": "تأیید عملیات", - "confirm_yes": "تأیید", - "confirm_no": "انصراف", - "error_username_short": "نام کاربری حداقل ۳ کاراکتر", - "error_password_short": "رمز عبور حداقل ۸ کاراکتر", - "error_generic": "خطا", - "error_network": "خطای شبکه: {{message}}", - "error_create_user": "خطا در ایجاد کاربر", - "tab_storage": "فضای ذخیره‌سازی", - "storage_title": "تنظیمات فضای ذخیره‌سازی", - "storage_current_backend": "بک‌اند فعلی", - "storage_total_blobs": "مجموع بلوب‌ها", - "storage_total_size": "حجم کل", - "storage_dedup_ratio": "نسبت حذف تکراری", - "storage_backend": "بک‌اند", - "storage_local": "محلی", - "storage_s3": "سازگار با S3", - "storage_provider_preset": "پیش‌تنظیم ارائه‌دهنده", - "storage_preset_custom": "سفارشی", - "storage_endpoint_url": "آدرس نقطه پایانی", - "storage_endpoint_hint": "برای AWS S3 خالی بگذارید", - "storage_bucket": "باکت", - "storage_region": "منطقه", - "storage_access_key": "کلید دسترسی", - "storage_secret_key": "کلید مخفی", - "storage_secret_configured": "کلید تنظیم شد", - "storage_key_placeholder": "کلید جدید وارد کنید", - "storage_path_style": "اجبار سبک مسیر", - "storage_path_style_hint": "برای MinIO و برخی سرویس‌های سازگار با S3 لازم است", - "storage_test_connection": "آزمایش اتصال", - "storage_test_success": "اتصال موفق", - "storage_test_failure": "اتصال ناموفق", - "storage_save": "ذخیره تنظیمات", - "storage_saved": "تنظیمات ذخیره شد", - "storage_migration": "انتقال داده", - "storage_migration_coming_soon": "ابزارهای انتقال به زودی", - "migration_status_label": "وضعیت انتقال", - "migration_start": "شروع انتقال", - "migration_pause": "توقف", - "migration_resume": "ادامه", - "migration_verify": "تأیید", - "migration_complete": "تکمیل", - "migration_started": "انتقال شروع شد", - "migration_paused_msg": "انتقال متوقف شد", - "migration_resumed_msg": "انتقال ادامه یافت", - "migration_completed_msg": "انتقال با موفقیت تکمیل شد", - "migration_verifying": "در حال تأیید...", - "migration_verify_passed": "تأیید موفق", - "migration_verify_failed": "تأیید ناموفق", - "migration_failed_blobs": "بلوب‌های ناموفق", - "testing": "در حال آزمایش...", - "smtp_disabled": "غیرفعال (میزبان تنظیم نشده)", - "smtp_enabled": "فعال", - "smtp_enabled_label": "وضعیت", - "smtp_intro": "SMTP فقط از طریق متغیرهای محیطی (OXICLOUD_SMTP_*) پیکربندی می‌شود. مقادیر زیر از سرور در حال اجرا خوانده می‌شوند — برای تغییر آن‌ها، محیط را ویرایش کرده و OxiCloud را راه‌اندازی مجدد کنید.", - "smtp_not_configured": "SMTP روی این سرور پیکربندی نشده است.", - "smtp_send_failed": "ارسال ناموفق.", - "smtp_send_test": "ارسال ایمیل آزمایشی", - "smtp_sending": "در حال ارسال…", - "smtp_sent": "ایمیل آزمایشی ارسال شد.", - "smtp_server_code": "پاسخ سرور", - "smtp_test_intro": "یک پیام تشخیصی از پیش تعریف‌شده را به گیرنده زیر ارسال می‌کند و پاسخ سرور SMTP را گزارش می‌دهد تا بتوانید آن را با گزارش‌های ریلی خود مطابقت دهید.", - "smtp_test_missing_to": "آدرس گیرنده را وارد کنید.", - "smtp_test_title": "ارسال ایمیل آزمایشی", - "smtp_test_to": "آدرس گیرنده", - "smtp_title": "ایمیل خروجی (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "پروفایل", - "back_to_app": "بازگشت به OxiCloud", - "loading": "در حال بارگذاری…", - "not_authenticated": "احراز هویت نشده", - "not_authenticated_desc": "برای مشاهده پروفایل وارد شوید.", - "sign_in": "ورود", - "role_admin": "مدیر", - "role_user": "کاربر", - "account_details": "جزئیات حساب", - "username": "نام کاربری", - "email": "ایمیل", - "role": "نقش", - "last_login": "آخرین ورود", - "storage": "فضای ذخیره‌سازی", - "used": "استفاده شده", - "quota": "سهمیه", - "usage": "مصرف", - "unlimited": "نامحدود", - "app_passwords": "رمزهای برنامه", - "app_pw_desc": "رمزهایی برای کلاینت‌های WebDAV، CalDAV و CardDAV ایجاد کنید. هر رمز فقط یک بار نمایش داده می‌شود.", - "app_pw_label_placeholder": "برچسب (مثلاً Thunderbird، macOS)", - "generate": "ایجاد", - "generating": "در حال ایجاد…", - "new_password_for": "رمز جدید برای", - "copy_warning": "این رمز را اکنون کپی کنید. دوباره قابل مشاهده نیست.", - "copy_to_clipboard": "کپی به کلیپ‌بورد", - "col_label": "برچسب", - "col_created": "ایجاد شده", - "col_last_used": "آخرین استفاده", - "col_status": "وضعیت", - "active": "فعال", - "revoked": "ابطال شده", - "revoke_title": "ابطال", - "no_app_passwords": "هنوز رمز برنامه‌ای وجود ندارد.", - "client_sessions": "نشست‌های کلاینت", - "client_sessions_desc": "هنگام اتصال کلاینت سازگار با Nextcloud به صورت خودکار ایجاد می‌شود.", - "col_client": "کلاینت", - "never": "هرگز", - "just_now": "همین الان", - "minutes_ago": "{{n}} دقیقه پیش", - "hours_ago": "{{n}} ساعت پیش", - "days_ago": "{{n}} روز پیش", - "edit_profile": "ویرایش نمایه", - "edit_oidc_managed": "برای تغییر اطلاعات خود (نام، نام خانوادگی، عکس نمایه، …)، لطفاً آن‌ها را در ارائه‌دهنده هویت خود به‌روز کنید. تغییرات شما در ورود بعدی ظاهر خواهد شد.", - "username_claim_hint": "۲ تا ۶۴ کاراکتر، حروف / ارقام / نقطه / خط تیره / زیرخط. پس از انتخاب، نام کاربری قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", - "username_already_claimed": "نام کاربری تنظیم شده و قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", - "given_name": "نام", - "family_name": "نام خانوادگی", - "notify_on_share": "وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن", - "notify_on_share_hint": "وقتی تیک‌خورده نباشد، اشتراک‌گذاری‌ها همچنان در حساب شما نمایش داده می‌شوند — فقط ایمیلی درباره آنها دریافت نخواهید کرد.", - "save_profile": "ذخیره تغییرات", - "profile_saved": "نمایه به‌روز شد", - "profile_no_changes": "تغییری برای ذخیره وجود ندارد.", - "profile_save_failed": "ذخیره ناموفق بود", - "username_taken_error": "این نام کاربری قبلاً گرفته شده است.", - "username_immutable_error": "نام کاربری شما قبلاً تنظیم شده و در اینجا قابل تغییر نیست. در صورت نیاز به تغییر نام، با مدیر تماس بگیرید.", - "change_password": "تغییر رمز عبور", - "current_password": "رمز فعلی", - "new_password": "رمز جدید", - "min_8_chars": "حداقل ۸ کاراکتر", - "confirm_password": "تأیید رمز جدید", - "update_password": "به‌روزرسانی رمز", - "updating": "در حال به‌روزرسانی…", - "password_updated": "رمز عبور با موفقیت به‌روز شد", - "passwords_no_match": "رمزها مطابقت ندارند", - "password_too_short": "رمز باید حداقل ۸ کاراکتر باشد", - "password_change_failed": "تغییر رمز ناموفق بود", - "error_network": "خطای شبکه: {{message}}", - "error_label_required": "لطفاً برچسب وارد کنید", - "error_create_pw": "ایجاد رمز ناموفق بود", - "confirm_revoke": "رمز «{{label}}» ابطال شود؟ کلاینت‌ها از کار می‌افتند.", - "error_revoke": "ابطال ناموفق بود", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "notifications": { - "file_renamed": "فایل تغییر نام داد", - "file_renamed_to": "فایل به \"{{name}}\" تغییر نام داد", - "folder_renamed": "پوشه تغییر نام داد", - "folder_renamed_to": "پوشه به \"{{name}}\" تغییر نام داد", - "file_uploaded": "فایل آپلود شد", - "file_deleted": "فایل به زباله‌دان منتقل شد", - "folder_deleted": "پوشه به زباله‌دان منتقل شد", - "item_deleted_permanently": "آیتم برای همیشه حذف شد", - "trash_emptied": "زباله‌دان خالی شد", - "title": "اعلان‌ها", - "empty": "بدون اعلان", - "link_created": "پیوند ایجاد شد", - "share_success": "پیوند اشتراک‌گذاری با موفقیت ایجاد شد", - "upload_files_section_title": "بارگذاری اینجا در دسترس نیست", - "upload_files_section_body": "برای بارگذاری فایل‌ها به بخش فایل‌ها بروید" - }, - "upload": { - "uploading": "در حال آپلود...", - "files": "فایل‌ها", - "complete": "{{count}} / {{total}} آپلود شد" - }, - "storage_quota_exceeded": "سهمیه فضای ذخیره‌سازی تجاوز کرده است", - "sharedwithme": { - "pageTitle": "به اشتراک‌گذاشته شده با من", - "pageDescription": "فایل‌ها و پوشه‌هایی که کاربران دیگر با شما به اشتراک گذاشته‌اند", - "emptyStateTitle": "هنوز چیزی با شما به اشتراک گذاشته نشده", - "emptyStateDesc": "مواردی که کاربران دیگر با شما به اشتراک می‌گذارند اینجا نمایش داده می‌شوند", - "loadMore": "بارگذاری بیشتر", - "sharedBy": "به اشتراک‌گذاشته توسط", - "colName": "نام", - "colType": "نوع", - "colSharedBy": "به اشتراک‌گذاشته توسط", - "colDate": "تاریخ اشتراک‌گذاری", - "colPermissions": "مجوزها" - }, - "groupby": { - "none": "هیچ", - "title": "گروه‌بندی بر اساس", - "owner": "مالک", - "shareDate": "تاریخ اشتراک", - "type": "نوع", - "type.folders": "پوشه‌ها", - "accessedAt": "تاریخ دسترسی", - "modifiedAt": "تاریخ تغییر", - "createdAt": "تاریخ ایجاد", - "size": "اندازه", - "favoriteDate": "تاریخ مورد علاقه", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "جدید" - }, - "dateBucket": { - "today": "امروز", - "last7days": "۷ روز گذشته", - "last30days": "۳۰ روز گذشته" - }, - "groups": { - "title": "مدیریت گروه‌ها", - "create_button": "ایجاد گروه", - "create_dialog_title": "گروه جدید", - "edit_dialog_title": "تغییر نام گروه", - "name_label": "نام", - "name_placeholder": "engineering", - "description_label": "توضیحات (اختیاری)", - "members_section": "اعضا", - "add_member_placeholder": "افزودن کاربر یا گروه…", - "no_members": "هنوز عضوی وجود ندارد.", - "remove_member": "حذف", - "delete_group": "حذف گروه", - "delete_confirm": "گروه «{name}» حذف شود؟ مجوزهای مرتبط با این گروه باطل خواهند شد.", - "empty_state": "هنوز گروهی وجود ندارد.", - "load_more": "بارگیری بیشتر", - "back_to_list": "بازگشت", - "loading": "در حال بارگذاری…", - "virtual_badge": "سامانه", - "member_count_zero": "بدون عضو", - "member_count_one": "۱ عضو", - "member_count_other": "{count} عضو", - "delete_confirm_label": "نام گروه را برای تأیید وارد کنید:", - "delete_confirm_mismatch": "نام گروه را دقیقاً برای تأیید وارد کنید.", - "virtual_internal_name": "داخلی", - "members_loading": "در حال بارگیری اعضا…", - "members_empty": "بدون عضو", - "virtual_internal_explanation": "هر کاربر داخلی روی این سرور" - }, - "myshares": { - "copyLink": "کپی پیوند", - "deleteLink": "حذف پیوند", - "notifyByEmail": "اطلاع‌رسانی از طریق ایمیل", - "notifyFailed": "ارسال اعلان ممکن نشد.", - "notifyGroupMembers": "اطلاع‌رسانی به اعضای گروه", - "notifyRateLimited": "اعلان‌های زیادی برای این گیرنده — بعداً دوباره تلاش کنید.", - "removeAccess": "حذف دسترسی", - "resendInvitation": "ارسال مجدد ایمیل دعوت" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", + "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبرای دیدن اشتراک‌گذاری جدید خود، OxiCloud را باز کنید:\n{{login_link}}\n\nممکن است اشتراک‌گذاری‌های جدید دیگری از {{inviter}} داشته باشید — وارد شوید تا همه موارد به اشتراک گذاشته‌شده با خود را ببینید.\n\n— OxiCloud\n\nشما این پیام را دریافت می‌کنید زیرا حساب OxiCloud دارید و گزینه اعلان اشتراک‌گذاری شما روشن است. می‌توانید آن را در پروفایل خود خاموش کنید (وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "سیستم ذخیره‌سازی ابری ساده‌گرا" + }, + "nav": { + "files": "پرونده‌ها", + "shared": "هم‌رسانی‌های من", + "recent": "اخیر", + "favorites": "موردعلاقه‌ها", + "photos": "عکس‌ها", + "music": "موسیقی", + "trash": "سطل زباله", + "sharedwithme": "به اشتراک‌گذاشته شده با من", + "profile": "پروفایل", + "shared_with_me": "به اشتراک‌گذاشته شده با من" + }, + "photos": { + "empty_state": "هنوز عکسی نیست", + "empty_hint": "تصاویر یا ویدیوها را آپلود کنید تا اینجا نمایش داده شوند", + "items_selected": "انتخاب شده", + "view_daily": "روز", + "view_monthly": "ماه", + "view_yearly": "سال", + "group_by": "گروه‌بندی بر اساس" + }, + "music": { + "create_playlist": "ایجاد فهرست پخش", + "playlists": "فهرست‌های پخش", + "no_playlists": "هنوز فهرست پخشی نیست", + "select_playlist": "یک فهرست پخش انتخاب کنید", + "select_hint": "از نوار کناری یک فهرست پخش انتخاب کنید یا یکی جدید بسازید", + "add_tracks": "افزودن آهنگ‌ها", + "no_tracks": "هیچ آهنگی در این فهرست پخش نیست", + "unknown_artist": "هنرمند ناشناس", + "unknown_title": "ناشناس", + "confirm_delete": "این فهرست پخش حذف شود؟", + "playlist_name": "نام فهرست پخش", + "create": "ایجاد", + "delete": "حذف", + "share": "هم‌رسانی", + "edit": "ویرایش", + "play_all": "پخش همه", + "shuffle": "تصادفی", + "repeat": "تکرار", + "repeat_one": "تکرار یک", + "queue": "صف", + "queue_empty": "صف خالی است", + "not_playing": "در حال پخش نیست", + "play": "پخش", + "pause": "توقف", + "previous": "قبلی", + "next": "بعدی", + "volume": "صدا", + "mute": "بی‌صدا", + "unmute": "صدا فعال", + "title": "عنوان", + "artist": "هنرمند", + "album": "آلبوم", + "tracks": "آهنگ", + "add": "افزودن", + "added": "افزوده شد!", + "added_to_playlist": "به فهرست پخش افزوده شد", + "add_to_playlist": "افزودن به فهرست پخش", + "load_error": "خطا در بارگیری فهرست پخش", + "add_error": "امکان افزودن آهنگ‌ها به فهرست پخش نیست", + "no_playlists_yet": "فهرست پخشی وجود ندارد. اول یکی بسازید!", + "selected_files": "انتخاب شده:", + "error": "خطا", + "search_audio": "جستجوی فایل‌های صوتی…", + "no_audio_files": "فایل صوتی یافت نشد", + "selected": "انتخاب شده", + "loading": "در حال بارگذاری…", + "search_error": "بارگذاری فایل‌های صوتی ممکن نشد", + "adding": "در حال افزودن…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "قبلی" + }, + "actions": { + "search": "جست‌و‌جوی پرونده‌ها..", + "new_folder": "پوشهٔ جدید", + "upload": "بارگذاری", + "upload_files": "بارگذاری پرونده‌ها", + "upload_folder": "بارگذاری پوشه", + "upload.uploading": "...در حال بارگذاری", + "upload.complete": "{count} / {total} بارگذاری شد", + "upload.files": "فایل‌ها", + "rename": "تغییر نام", + "move": "انتقال به...", + "move_to": "انتقال به", + "delete": "حذف", + "download": "بارگیری", + "view": "مشاهده", + "cancel": "لغو", + "confirm": "تأیید", + "share": "هم‌رسانی", + "favorite": "افزودن به موردعلاقه‌ها", + "unfavorite": "حذف از موردعلاقه‌ها", + "copy": "رونوشت", + "notify": "آگاه‌سازی", + "send": "ارسال", + "clear_recent": "پاک‌کردن موارد اخیر", + "logout": "خروج", + "create": "ایجاد", + "search_btn": "جست‌و‌جو", + "close": "بستن", + "delete_permanently": "Delete permanently", + "empty_trash": "Empty trash", + "open_parent_folder": "رفتن به پوشه والد", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "ظاهر", + "about": "درباره OxiCloud", + "about_description": "پلتفرم ذخیره‌سازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی.", + "admin_panel": "پنل مدیریت", + "profile": "نمایه من", + "role_user": "کاربر", + "theme": { + "light": "روشن", + "dark": "تاریک", + "auto": "مانند سیستم" + }, + "manage_groups": "مدیریت گروه‌ها", + "admin": "مدیر" + }, + "share": { + "dialogTitle": "پیوند هم‌رسانی", + "linkLabel": "پیوند هم‌سانی:", + "copyLink": "رونوشت", + "permissions": "دسترسی‌ها:", + "permissionRead": "خواندن", + "permissionWrite": "نوشتن", + "permissionReshare": "هم‌رسانی دوباره", + "password": "محافظت با گذرواژه:", + "generatePassword": "تولید", + "expiration": "تاریخ انقضا:", + "update": "به‌روزرسانی هم‌رسانی", + "remove": "پاک‌کردن هم‌رسانی", + "notifyTitle": "ارسال آگاه‌سازی", + "notifyEmailLabel": "نشانی رایانامه:", + "notifyMessageLabel": "پیام (اختیاری):", + "notifySend": "ارسال آگاه‌سازی", + "shareWithOthers": "هم‌رسانی با دیگران", + "sharePublicly": "هم‌رسانی عمومی", + "shareSettings": "تنظیمات هم‌رسانی", + "shareCopied": "پیوند به بُریده‌دان رونوشت شد", + "shareCreated": "پیوند هم‌رسانی با موفقیت ایجاد شد", + "shareUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", + "shareRemoved": "هم‌رسانی با موفقیت پاک شد", + "inviteByEmail": "دعوت از طریق ایمیل — دعوت ارسال خواهد شد", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "رونوشت", + "copy_failed": "Could not copy link", + "download": "بارگیری", + "files": "پرونده‌ها", + "folders": "پوشه‌ها", + "link_name": "Link name (optional)", + "notifyByEmail": "اطلاع‌رسانی از طریق ایمیل", + "revoke": "Remove", + "role_label": "نقش" + }, + "share_dialogTitle": "پیوند هم‌رسانی", + "share_linkLabel": "پیوند هم‌رسانی:", + "share_copyLink": "رونوشت", + "share_permissions": "دسترسی‌ها:", + "share_permissionRead": "خواندن", + "share_permissionWrite": "نوشتن", + "share_permissionReshare": "هم‌رسانی دوباره", + "share_password": "محافظت با گذرواژه:", + "share_generatePassword": "تولید", + "share_expiration": "تاریخ انقضا:", + "share_update": "به‌روزرسانی هم‌رسانی", + "share_remove": "پاک‌کردن هم‌رسانی", + "share_notifyTitle": "ارسال آگاه‌سازی", + "share_notifyEmailLabel": "نشانی رایانامه:", + "share_notifyMessageLabel": "پیام (اختیاری):", + "share_notifySend": "ارسال آگاه‌سازی", + "shared": { + "backToFiles": "بازگشت به پرونده‌ها", + "pageTitle": "منابع هم‌رسانی شده", + "pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", + "filterType": "نوع:", + "filterAll": "همه", + "filterFiles": "پرونده‌ها", + "filterFolders": "پوشه‌ها", + "sortBy": "مرتب‌سازی بر اساس:", + "sortByName": "نام", + "sortByDate": "تاریخ هم‌رسانی", + "sortByExpiration": "تاریخ انقضا", + "search": "جست‌و‌جو", + "colName": "نام", + "colType": "نوع", + "colDateShared": "تاریخ هم‌رسانی", + "colExpiration": "تاریخ انقضا", + "colPermissions": "دسترسی‌ها", + "colPassword": "گذرواژه", + "colActions": "عملیات", + "emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", + "emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", + "goToFiles": "رفتن به پرونده‌ها", + "typeFile": "پرونده", + "typeFolder": "پوشه", + "noExpiration": "بدون انقضا", + "hasPassword": "بله", + "noPassword": "خیر", + "editShare": "ویرایش هم‌رسانی", + "notifyShare": "آگاه‌سازی کسی", + "copyLink": "رونوشت پیوند", + "removeShare": "حذف هم‌رسانی", + "linkCopied": "پیوند به بُریده‌دان رونوشت شد", + "linkCopyFailed": "رونوشت پیوند ناموفق بود", + "itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", + "itemRemoved": "هم‌رسانی با موفقیت پاک شد", + "invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", + "notificationSent": "آگاه‌سازی با موفقیت ارسال شد", + "notificationFailed": "ارسال آگاه‌سازی ناموفق بود", + "shared_backToFiles": "بازگشت به پرونده‌ها", + "shared_pageTitle": "منابع هم‌رسانی شده", + "shared_pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", + "shared_filterType": "نوع:", + "shared_filterAll": "همه", + "shared_filterFiles": "پرونده‌ها", + "shared_filterFolders": "پوشه‌ها", + "shared_sortBy": "مرتب‌سازی بر اساس:", + "shared_sortByName": "نام", + "shared_sortByDate": "تاریخ هم‌رسانی", + "shared_sortByExpiration": "تاریخ انقضا", + "shared_search": "جست‌و‌جو", + "shared_colName": "نام", + "shared_colType": "نوع", + "shared_colDateShared": "تاریخ هم‌رسانی", + "shared_colExpiration": "تاریخ انقضا", + "shared_colPermissions": "دسترسی‌ها", + "shared_colPassword": "گذرواژه", + "shared_colActions": "عملیات", + "shared_emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", + "shared_emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", + "shared_goToFiles": "رفتن به پرونده‌ها", + "shared_typeFile": "پرونده", + "shared_typeFolder": "پوشه", + "shared_noExpiration": "بدون انقضا", + "shared_hasPassword": "بله", + "shared_noPassword": "خیر", + "shared_editShare": "ویرایش هم‌رسانی", + "shared_notifyShare": "آگاه‌سازی کسی", + "shared_copyLink": "رونوشت پیوند", + "shared_removeShare": "حذف هم‌رسانی", + "shared_linkCopied": "پیوند به بُریده‌دان رونوشت شد", + "shared_linkCopyFailed": "رونوشت پیوند ناموفق بود", + "shared_itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", + "shared_itemRemoved": "هم‌رسانی با موفقیت پاک شد", + "shared_invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", + "shared_notificationSent": "آگاه‌سازی با موفقیت ارسال شد", + "shared_notificationFailed": "ارسال آگاه‌سازی ناموفق بود" + }, + "files": { + "name": "نام", + "type": "نوع", + "size": "اندازه", + "modified": "تاریخ تغییر", + "no_files": "هنوز هیچ پرونده‌ای در این پوشه وجود ندارد", + "empty_hint": "برای شروع، فایل‌ها را آپلود کنید یا پوشه بسازید", + "loading": "در حال بارگذاری فایل‌ها…", + "view_grid": "نمای شبکه‌ای", + "view_list": "نمای فهرستی", + "file_types": { + "document": "سند", + "image": "تصویر", + "video": "ویدیو", + "audio": "صوتی", + "pdf": "PDF", + "text": "متن", + "folder": "پوشه", + "spreadsheet": "صفحه گسترده", + "presentation": "ارائه", + "archive": "بایگانی", + "installer": "نصب‌کننده", + "code": "کد" + }, + "owner": "مالک", + "add_favorites": "افزودن به موردعلاقه‌ها", + "added_favorites": "به موارد علاقه‌مند افزوده شد", + "col_name": "نام", + "col_owner": "مالک", + "col_size": "اندازه", + "col_type": "نوع", + "copy": "رونوشت", + "edit": "ویرایش", + "file": "پرونده", + "folder": "پوشه", + "new_folder": "پوشهٔ جدید", + "share": "هم‌رسانی", + "view": "مشاهده" + }, + "dialogs": { + "rename_folder": "تغییر نام پوشه", + "new_name": "نام جدید", + "new_folder_title": "پوشه جدید", + "folder_name": "نام پوشه", + "folder_placeholder": "پوشه من", + "rename_title": "تغییر نام", + "move_file": "انتقال پرونده", + "select_destination": "انتخاب پوشهٔ مقصد", + "root": "ریشه", + "delete_confirmation": "آیا مطمئن هستید که می‌خواهید حذف کنید", + "and_contents": "و همهٔ محتویات آن", + "no_undo": "این عملیات قابل بازگردانی نیست", + "share_file": "هم‌رسانی پرونده", + "share_folder": "هم‌رسانی پوشه", + "existing_shares": "هم‌رسانی موجود", + "share_options": "گزینه‌های هم‌رسانی", + "password": "گذرواژه", + "expiration": "تاریخ انقضا", + "permissions": "دسترسی‌ها", + "generated_link": "پیوند تولید شده", + "notify": "ارسال آگاه‌سازی", + "recipient": "گیرنده", + "message": "پیام", + "confirm_delete": "Move to trash", + "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", + "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", + "confirm_delete_share": "Delete share link", + "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", + "confirm_empty_trash": "Empty trash", + "confirm_permanent_delete": "Delete permanently", + "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", + "confirm_title": "Confirm action", + "go_to_parent": ".. (parent folder)", + "move_folder": "Move folder", + "no_subfolders": "No subfolders", + "rename_file": "Rename file", + "select_this_folder": "Select this folder", + "move_to_home": "انتقال به پوشه خانگی" + }, + "dropzone": { + "drag_files": "پرونده‌ها را اینجا بکشید یا برای انتخاب کلیک کنید", + "drop_files": "پرونده‌ها را رها کنید تا بارگذاری شوند" + }, + "permissions": { + "read": "خواندن", + "write": "نوشتن", + "reshare": "هم‌رسانی دوباره" + }, + "errors": { + "file_not_found": "پرونده پیدا نشد", + "folder_not_found": "پوشه پیدا نشد", + "delete_error": "خطا در پاک کردن", + "upload_error": "خطا در بارگذاری پرونده", + "rename_error": "خطا در تغییر نام", + "move_error": "خطا در انتقال", + "empty_name": "نام نمی‌تواند خالی باشد", + "name_exists": "پرونده یا پوشه‌ای با این نام قبلا وجود دارد", + "generic_error": "خطایی رخ داده است", + "group_name_invalid": "نام گروه باید با قالب پیشوند ایمیل مطابقت داشته باشد (حروف، ارقام، نقطه، خط تیره، زیرخط؛ 1–64 نویسه).", + "group_cycle": "این عضو باعث ایجاد ارجاع چرخه‌ای بین گروه‌ها می‌شود.", + "group_depth_exceeded": "عمق تودرتو بیش از حداکثر مجاز (8) است.", + "group_virtual_immutable": "گروه «Internal» توسط سامانه مدیریت می‌شود و قابل تغییر نیست.", + "group_not_found": "گروه پیدا نشد.", + "group_name_taken": "گروهی با این نام پیش‌از این وجود دارد." + }, + "breadcrumb": { + "home": "صفحه اصلی" + }, + "trash": { + "empty_trash": "خالی کردن سطل زباله", + "empty_state": "سطل زباله خالی است", + "original_location": "محل اصلی", + "deleted_date": "تاریخ حذف", + "remaining": "باقی‌مانده", + "actions": "عملیات", + "restore": "بازیابی", + "delete_permanently": "حذف دائمی", + "empty_confirm": "آیا مطمئن هستید که می‌خواهید سطل زباله را خالی کنید؟ این کار همهٔ موارد را به‌طور دائمی حذف خواهد کرد.", + "groupby": { + "remaining_days": "روزهای باقی‌مانده", + "trashed_time": "زمان حذف" + }, + "delete": "حذف دائمی", + "empty_action": "Empty trash" + }, + "daysRemaining": { + "expired": "منقضی شده", + "today": "امروز", + "tomorrow": "فردا", + "inDays": "{{count}} روز" + }, + "expiryChip": { + "never": "هرگز منقضی نمی‌شود", + "expired": "منقضی شده", + "today": "امروز منقضی می‌شود", + "tomorrow": "فردا منقضی می‌شود", + "inDays": "در {{count}} روز منقضی می‌شود", + "onDate": "در {{date}} منقضی می‌شود" + }, + "auth": { + "login_title": "ورود", + "username": "نام‌کاربری", + "username_placeholder": "نام‌کاربری خود را وارد کنید", + "login_identifier": "نام کاربری یا ایمیل", + "login_identifier_placeholder": "نام کاربری یا ایمیل خود را وارد کنید", + "password": "گذرواژه", + "password_placeholder": "گذرواژه خود را وارد کنید", + "login_button": "ورود", + "no_account": "حساب کاربری ندارید؟", + "register": "نام‌نویسی", + "admin_setup": "اولین بار است؟", + "setup": "راه‌اندازی اولیه مدیریت", + "register_title": "ایجاد حساب کاربری", + "email": "رایانامه", + "email_placeholder": "رایانامه خود را وارد کنید", + "confirm_password": "تأیید گذرواژه", + "confirm_password_placeholder": "گذرواژه خود را تأیید کنید", + "register_button": "ایجاد حساب کاربری", + "have_account": "حساب کاربری دارید؟", + "login": "ورود", + "setup_title": "راه‌اندازی اولیه", + "setup_step1": "مدیر", + "setup_step2": "سیستم", + "setup_step3": "تکمیل", + "admin_username": "نام‌کاربری مدیر", + "admin_email": "رایانامه مدیر", + "admin_password": "گذرواژه مدیر", + "create_admin": "ایجاد مدیر", + "back_to_login": "قبلا راه‌اندازی شده است؟", + "admin_success": "حساب کاربری مدیر با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", + "account_success": "حساب کاربری با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", + "passwords_mismatch": "گذرواژه‌ها مطابقت ندارند", + "admin_create_error": "خطا در ایجاد حساب کاربری مدیر", + "or": "یا", + "sso_login": "ورود با SSO", + "sso_login_provider": "ورود با {{provider}}", + "magicLinkHint": "رمز عبور ندارید؟ ایمیل خود را وارد کنید تا یک پیوند ورود یک‌بار‌مصرف برایتان ارسال شود.", + "magicLinkEmailLabel": "آدرس ایمیل", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "ارسال پیوند ورود", + "magicLinkSent": "اگر برای این ایمیل حسابی وجود داشته باشد، پیوند ورود ارسال شده است. صندوق ورودی خود را بررسی کنید.", + "magicLinkUnavailable": "ورود با ایمیل در این سرور در دسترس نیست.", + "magicLinkNetworkError": "ارتباط با سرور برقرار نشد: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "آدرس ایمیل", + "magic_hint": "رمز عبور ندارید؟ ایمیل خود را وارد کنید تا یک پیوند ورود یک‌بار‌مصرف برایتان ارسال شود.", + "magic_unavailable": "ورود با ایمیل در این سرور در دسترس نیست.", + "passwords_match": "Passwords match", + "sign_in": "ورود" + }, + "storage": { + "title": "فضای ذخیره‌سازی", + "calculating": "در حال محاسبه...", + "used": "{{percentage}}% استفاده شده ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "این نوع پرونده قابل پیش‌نمایش نیست.", + "download_file": "بارگیری پرونده", + "zoom_in": "بزرگ‌نمایی", + "zoom_out": "کوچک‌نمایی", + "zoom_reset": "بازنشانی بزرگ‌نمایی" + }, + "language_selector": { + "title": "!خوش آمدید", + "subtitle": "زبان خود را برای ادامه انتخاب کنید", + "continue": "ادامه", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "هنوز هیچ مورد علاقه‌ای وجود ندارد", + "empty_hint": "برای افزودن به موارد علاقه‌مند، پرونده‌ها یا پوشه‌ها را ستاره‌دار کنید", + "add": "افزودن به موارد علاقه‌مند", + "remove": "حذف از موارد علاقه‌مند", + "added_title": "به موارد علاقه‌مند افزوده شد", + "added_msg": "به موارد علاقه‌مند افزوده شد", + "removed_title": "از موارد علاقه‌مند حذف شد", + "removed_msg": "از موارد علاقه‌مند حذف شد" + }, + "recent": { + "title": "اخیر", + "clear": "پاک کردن اخیر", + "accessed": "دسترسی یافته", + "empty_state": "هنوز هیچ پروندهٔ اخیر وجود ندارد", + "empty_hint": "پرونده‌هایی که باز می‌کنید اینجا ظاهر می‌شوند", + "loadMore": "بارگذاری بیشتر" + }, + "batch": { + "one_selected": "۱ مورد انتخاب شده", + "n_selected": "{{count}} مورد انتخاب شده", + "confirm_delete": "آیا مطمئنید که می‌خواهید {{count}} مورد را به سطل زباله منتقل کنید؟", + "move_title": "انتقال {{count}} مورد", + "add_favorites": "افزودن به موارد علاقه‌مند", + "move_copy": "انتقال یا کپی" + }, + "admin": { + "page_title": "پنل مدیریت", + "back_to_app": "بازگشت به OxiCloud", + "loading": "در حال بارگذاری…", + "access_denied": "دسترسی ممنوع", + "access_denied_desc": "امتیازات مدیر لازم است.", + "sign_in": "ورود", + "tab_dashboard": "داشبورد", + "tab_users": "کاربران", + "tab_oidc": "SSO / OIDC", + "total_users": "کل کاربران", + "active_users": "کاربران فعال", + "admins": "مدیران", + "version": "نسخه", + "storage_overview": "نمای کلی فضا", + "used": "استفاده شده", + "total_quota": "سهمیه کل", + "usage_pct": "درصد استفاده", + "users_over_80": "کاربران بالای ۸۰٪", + "users_over_quota": "کاربران بالای سهمیه", + "system": "سیستم", + "auth_label": "احراز هویت", + "oidc_label": "OIDC", + "quotas_label": "سهمیه‌ها", + "enabled": "فعال", + "disabled": "غیرفعال", + "active": "فعال", + "off": "خاموش", + "allow_registration": "اجازه ثبت‌نام عمومی", + "registration_warning": "ثبت‌نام عمومی غیرفعال است. فقط مدیران می‌توانند کاربر جدید بسازند.", + "user_management": "مدیریت کاربران", + "create_user": "ایجاد کاربر", + "col_user": "کاربر", + "col_role": "نقش", + "col_auth": "احراز هویت", + "col_status": "وضعیت", + "col_storage": "فضا", + "col_last_login": "آخرین ورود", + "col_actions": "عملیات", + "loading_users": "در حال بارگذاری…", + "failed_load_users": "خطا در بارگذاری", + "no_users_found": "کاربری یافت نشد", + "showing_users": "نمایش {{from}}-{{to}} از {{total}}", + "prev": "قبلی", + "next": "بعدی", + "inactive": "غیرفعال", + "you_badge": "(شما)", + "local": "محلی", + "never": "هرگز", + "just_now": "همین الان", + "minutes_ago": "{{n}} دقیقه پیش", + "hours_ago": "{{n}} ساعت پیش", + "days_ago": "{{n}} روز پیش", + "edit_quota_title": "ویرایش سهمیه", + "reset_password_title": "بازنشانی رمز", + "toggle_role_title": "تغییر نقش", + "deactivate_title": "غیرفعال کردن", + "activate_title": "فعال کردن", + "delete_title": "حذف", + "sso_title": "ورود یکپارچه (OIDC / SSO)", + "enable_sso": "فعال‌سازی SSO", + "provider_name": "نام ارائه‌دهنده", + "issuer_url": "آدرس صادرکننده", + "issuer_url_hint": "آدرس صادرکننده OpenID Connect", + "auto_discover": "کشف خودکار", + "discovering": "در حال کشف…", + "client_id": "شناسه مشتری", + "client_secret": "رمز مشتری", + "client_secret_placeholder": "خالی بگذارید تا مقدار فعلی حفظ شود", + "secret_configured": "رمز مشتری قبلاً پیکربندی شده", + "callback_url": "آدرس بازگشت", + "callback_url_hint": "(در IdP ثبت کنید)", + "advanced_settings": "تنظیمات پیشرفته", + "scopes": "محدوده‌ها", + "auto_provision": "تامین خودکار کاربران", + "admin_groups": "گروه‌های مدیر", + "admin_groups_hint": "نام گروه‌های OIDC جدا شده با کاما", + "disable_password": "غیرفعال‌سازی ورود با رمز (فقط OIDC)", + "password_warning": "تمام ورودهای رمزی متوقف می‌شود!", + "test_btn": "آزمایش", + "save_btn": "ذخیره", + "saving": "در حال ذخیره…", + "settings_saved": "تنظیمات ذخیره شد — OIDC اکنون {{status}}", + "quota_modal_title": "به‌روزرسانی سهمیه", + "quota_user_label": "کاربر:", + "new_quota": "سهمیه جدید", + "quota_unlimited_hint": "۰ برای نامحدود", + "cancel": "انصراف", + "create_user_title": "ایجاد کاربر جدید", + "username_label": "نام کاربری", + "username_placeholder": "نام‌کاربری", + "username_hint": "۳ تا ۳۲ کاراکتر", + "password_label": "رمز عبور", + "password_placeholder": "حداقل ۸ کاراکتر", + "email_label": "ایمیل", + "email_optional": "(اختیاری)", + "email_placeholder": "user@example.com (خودکار اگر خالی)", + "role_label": "نقش", + "role_user": "کاربر", + "role_admin": "مدیر", + "quota_label": "سهمیه", + "creating": "در حال ایجاد…", + "reset_pw_title": "بازنشانی رمز عبور", + "new_password_label": "رمز عبور جدید", + "resetting": "در حال بازنشانی…", + "reset_btn": "بازنشانی", + "confirm_role_change": "نقش به {{role}} تغییر یابد؟", + "confirm_deactivate": "آیا از غیرفعال‌سازی این کاربر مطمئنید؟", + "confirm_activate": "آیا از فعال‌سازی این کاربر مطمئنید؟", + "confirm_delete_user": "کاربر «{{name}}» حذف شود؟ قابل بازگشت نیست!", + "confirm_action": "تأیید عملیات", + "confirm_yes": "تأیید", + "confirm_no": "انصراف", + "error_username_short": "نام کاربری حداقل ۳ کاراکتر", + "error_password_short": "رمز عبور حداقل ۸ کاراکتر", + "error_generic": "خطا", + "error_network": "خطای شبکه: {{message}}", + "error_create_user": "خطا در ایجاد کاربر", + "tab_storage": "فضای ذخیره‌سازی", + "storage_title": "تنظیمات فضای ذخیره‌سازی", + "storage_current_backend": "بک‌اند فعلی", + "storage_total_blobs": "مجموع بلوب‌ها", + "storage_total_size": "حجم کل", + "storage_dedup_ratio": "نسبت حذف تکراری", + "storage_backend": "بک‌اند", + "storage_local": "محلی", + "storage_s3": "سازگار با S3", + "storage_provider_preset": "پیش‌تنظیم ارائه‌دهنده", + "storage_preset_custom": "سفارشی", + "storage_endpoint_url": "آدرس نقطه پایانی", + "storage_endpoint_hint": "برای AWS S3 خالی بگذارید", + "storage_bucket": "باکت", + "storage_region": "منطقه", + "storage_access_key": "کلید دسترسی", + "storage_secret_key": "کلید مخفی", + "storage_secret_configured": "کلید تنظیم شد", + "storage_key_placeholder": "کلید جدید وارد کنید", + "storage_path_style": "اجبار سبک مسیر", + "storage_path_style_hint": "برای MinIO و برخی سرویس‌های سازگار با S3 لازم است", + "storage_test_connection": "آزمایش اتصال", + "storage_test_success": "اتصال موفق", + "storage_test_failure": "اتصال ناموفق", + "storage_save": "ذخیره تنظیمات", + "storage_saved": "تنظیمات ذخیره شد", + "storage_migration": "انتقال داده", + "storage_migration_coming_soon": "ابزارهای انتقال به زودی", + "migration_status_label": "وضعیت انتقال", + "migration_start": "شروع انتقال", + "migration_pause": "توقف", + "migration_resume": "ادامه", + "migration_verify": "تأیید", + "migration_complete": "تکمیل", + "migration_started": "انتقال شروع شد", + "migration_paused_msg": "انتقال متوقف شد", + "migration_resumed_msg": "انتقال ادامه یافت", + "migration_completed_msg": "انتقال با موفقیت تکمیل شد", + "migration_verifying": "در حال تأیید...", + "migration_verify_passed": "تأیید موفق", + "migration_verify_failed": "تأیید ناموفق", + "migration_failed_blobs": "بلوب‌های ناموفق", + "testing": "در حال آزمایش...", + "smtp_disabled": "غیرفعال (میزبان تنظیم نشده)", + "smtp_enabled": "فعال", + "smtp_enabled_label": "وضعیت", + "smtp_intro": "SMTP فقط از طریق متغیرهای محیطی (OXICLOUD_SMTP_*) پیکربندی می‌شود. مقادیر زیر از سرور در حال اجرا خوانده می‌شوند — برای تغییر آن‌ها، محیط را ویرایش کرده و OxiCloud را راه‌اندازی مجدد کنید.", + "smtp_not_configured": "SMTP روی این سرور پیکربندی نشده است.", + "smtp_send_failed": "ارسال ناموفق.", + "smtp_send_test": "ارسال ایمیل آزمایشی", + "smtp_sending": "در حال ارسال…", + "smtp_sent": "ایمیل آزمایشی ارسال شد.", + "smtp_server_code": "پاسخ سرور", + "smtp_test_intro": "یک پیام تشخیصی از پیش تعریف‌شده را به گیرنده زیر ارسال می‌کند و پاسخ سرور SMTP را گزارش می‌دهد تا بتوانید آن را با گزارش‌های ریلی خود مطابقت دهید.", + "smtp_test_missing_to": "آدرس گیرنده را وارد کنید.", + "smtp_test_title": "ارسال ایمیل آزمایشی", + "smtp_test_to": "آدرس گیرنده", + "smtp_title": "ایمیل خروجی (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "مدیران", + "confirm_role": "نقش به {{role}} تغییر یابد؟", + "dashboard": "داشبورد", + "email": "ایمیل", + "mig_complete": "تکمیل", + "mig_pause": "توقف", + "mig_resume": "ادامه", + "mig_verify_failed": "تأیید ناموفق", + "mig_verify_passed": "تأیید موفق", + "mig_verifying": "در حال تأیید...", + "oidc_auto_provision": "تامین خودکار کاربران", + "oidc_callback": "آدرس بازگشت", + "oidc_client_id": "شناسه مشتری", + "oidc_disable_pw": "غیرفعال‌سازی ورود با رمز (فقط OIDC)", + "oidc_issuer": "آدرس صادرکننده", + "oidc_scopes": "محدوده‌ها", + "password": "رمز عبور", + "quotas": "سهمیه‌ها", + "reset_pw_for": "رمز جدید برای", + "role": "نقش", + "smtp_fail": "ارسال ناموفق.", + "smtp_send": "ارسال", + "smtp_test": "ارسال ایمیل آزمایشی", + "smtp_user_state": "احراز هویت", + "status": "وضعیت", + "storage": "فضا", + "storage_endpoint": "آدرس نقطه پایانی", + "storage_tab": "فضا", + "time_min_ago": "{{n}} دقیقه پیش", + "title": "مدیر", + "user": "کاربر", + "username": "نام کاربری", + "users": "کاربران" + }, + "profile": { + "page_title": "پروفایل", + "back_to_app": "بازگشت به OxiCloud", + "loading": "در حال بارگذاری…", + "not_authenticated": "احراز هویت نشده", + "not_authenticated_desc": "برای مشاهده پروفایل وارد شوید.", + "sign_in": "ورود", + "role_admin": "مدیر", + "role_user": "کاربر", + "account_details": "جزئیات حساب", + "username": "نام کاربری", + "email": "ایمیل", + "role": "نقش", + "last_login": "آخرین ورود", + "storage": "فضای ذخیره‌سازی", + "used": "استفاده شده", + "quota": "سهمیه", + "usage": "مصرف", + "unlimited": "نامحدود", + "app_passwords": "رمزهای برنامه", + "app_pw_desc": "رمزهایی برای کلاینت‌های WebDAV، CalDAV و CardDAV ایجاد کنید. هر رمز فقط یک بار نمایش داده می‌شود.", + "app_pw_label_placeholder": "برچسب (مثلاً Thunderbird، macOS)", + "generate": "ایجاد", + "generating": "در حال ایجاد…", + "new_password_for": "رمز جدید برای", + "copy_warning": "این رمز را اکنون کپی کنید. دوباره قابل مشاهده نیست.", + "copy_to_clipboard": "کپی به کلیپ‌بورد", + "col_label": "برچسب", + "col_created": "ایجاد شده", + "col_last_used": "آخرین استفاده", + "col_status": "وضعیت", + "active": "فعال", + "revoked": "ابطال شده", + "revoke_title": "ابطال", + "no_app_passwords": "هنوز رمز برنامه‌ای وجود ندارد.", + "client_sessions": "نشست‌های کلاینت", + "client_sessions_desc": "هنگام اتصال کلاینت سازگار با Nextcloud به صورت خودکار ایجاد می‌شود.", + "col_client": "کلاینت", + "never": "هرگز", + "just_now": "همین الان", + "minutes_ago": "{{n}} دقیقه پیش", + "hours_ago": "{{n}} ساعت پیش", + "days_ago": "{{n}} روز پیش", + "edit_profile": "ویرایش نمایه", + "edit_oidc_managed": "برای تغییر اطلاعات خود (نام، نام خانوادگی، عکس نمایه، …)، لطفاً آن‌ها را در ارائه‌دهنده هویت خود به‌روز کنید. تغییرات شما در ورود بعدی ظاهر خواهد شد.", + "username_claim_hint": "۲ تا ۶۴ کاراکتر، حروف / ارقام / نقطه / خط تیره / زیرخط. پس از انتخاب، نام کاربری قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", + "username_already_claimed": "نام کاربری تنظیم شده و قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", + "given_name": "نام", + "family_name": "نام خانوادگی", + "notify_on_share": "وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن", + "notify_on_share_hint": "وقتی تیک‌خورده نباشد، اشتراک‌گذاری‌ها همچنان در حساب شما نمایش داده می‌شوند — فقط ایمیلی درباره آنها دریافت نخواهید کرد.", + "save_profile": "ذخیره تغییرات", + "profile_saved": "نمایه به‌روز شد", + "profile_no_changes": "تغییری برای ذخیره وجود ندارد.", + "profile_save_failed": "ذخیره ناموفق بود", + "username_taken_error": "این نام کاربری قبلاً گرفته شده است.", + "username_immutable_error": "نام کاربری شما قبلاً تنظیم شده و در اینجا قابل تغییر نیست. در صورت نیاز به تغییر نام، با مدیر تماس بگیرید.", + "change_password": "تغییر رمز عبور", + "current_password": "رمز فعلی", + "new_password": "رمز جدید", + "min_8_chars": "حداقل ۸ کاراکتر", + "confirm_password": "تأیید رمز جدید", + "update_password": "به‌روزرسانی رمز", + "updating": "در حال به‌روزرسانی…", + "password_updated": "رمز عبور با موفقیت به‌روز شد", + "passwords_no_match": "رمزها مطابقت ندارند", + "password_too_short": "رمز باید حداقل ۸ کاراکتر باشد", + "password_change_failed": "تغییر رمز ناموفق بود", + "error_network": "خطای شبکه: {{message}}", + "error_label_required": "لطفاً برچسب وارد کنید", + "error_create_pw": "ایجاد رمز ناموفق بود", + "confirm_revoke": "رمز «{{label}}» ابطال شود؟ کلاینت‌ها از کار می‌افتند.", + "error_revoke": "ابطال ناموفق بود", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "رمزها مطابقت ندارند" + }, + "notifications": { + "file_renamed": "فایل تغییر نام داد", + "file_renamed_to": "فایل به \"{{name}}\" تغییر نام داد", + "folder_renamed": "پوشه تغییر نام داد", + "folder_renamed_to": "پوشه به \"{{name}}\" تغییر نام داد", + "file_uploaded": "فایل آپلود شد", + "file_deleted": "فایل به زباله‌دان منتقل شد", + "folder_deleted": "پوشه به زباله‌دان منتقل شد", + "item_deleted_permanently": "آیتم برای همیشه حذف شد", + "trash_emptied": "زباله‌دان خالی شد", + "title": "اعلان‌ها", + "empty": "بدون اعلان", + "link_created": "پیوند ایجاد شد", + "share_success": "پیوند اشتراک‌گذاری با موفقیت ایجاد شد", + "upload_files_section_title": "بارگذاری اینجا در دسترس نیست", + "upload_files_section_body": "برای بارگذاری فایل‌ها به بخش فایل‌ها بروید" + }, + "upload": { + "uploading": "در حال آپلود...", + "files": "فایل‌ها", + "complete": "{{count}} / {{total}} آپلود شد" + }, + "storage_quota_exceeded": "سهمیه فضای ذخیره‌سازی تجاوز کرده است", + "sharedwithme": { + "pageTitle": "به اشتراک‌گذاشته شده با من", + "pageDescription": "فایل‌ها و پوشه‌هایی که کاربران دیگر با شما به اشتراک گذاشته‌اند", + "emptyStateTitle": "هنوز چیزی با شما به اشتراک گذاشته نشده", + "emptyStateDesc": "مواردی که کاربران دیگر با شما به اشتراک می‌گذارند اینجا نمایش داده می‌شوند", + "loadMore": "بارگذاری بیشتر", + "sharedBy": "به اشتراک‌گذاشته توسط", + "colName": "نام", + "colType": "نوع", + "colSharedBy": "به اشتراک‌گذاشته توسط", + "colDate": "تاریخ اشتراک‌گذاری", + "colPermissions": "مجوزها" + }, + "groupby": { + "none": "هیچ", + "title": "گروه‌بندی بر اساس", + "owner": "مالک", + "shareDate": "تاریخ اشتراک", + "type": "نوع", + "type.folders": "پوشه‌ها", + "accessedAt": "تاریخ دسترسی", + "modifiedAt": "تاریخ تغییر", + "createdAt": "تاریخ ایجاد", + "size": "اندازه", + "favoriteDate": "تاریخ مورد علاقه", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "جدید", + "folders": "پوشه‌ها" + }, + "dateBucket": { + "today": "امروز", + "last7days": "۷ روز گذشته", + "last30days": "۳۰ روز گذشته", + "unknown": "ناشناس" + }, + "groups": { + "title": "مدیریت گروه‌ها", + "create_button": "ایجاد گروه", + "create_dialog_title": "گروه جدید", + "edit_dialog_title": "تغییر نام گروه", + "name_label": "نام", + "name_placeholder": "engineering", + "description_label": "توضیحات (اختیاری)", + "members_section": "اعضا", + "add_member_placeholder": "افزودن کاربر یا گروه…", + "no_members": "هنوز عضوی وجود ندارد.", + "remove_member": "حذف", + "delete_group": "حذف گروه", + "delete_confirm": "گروه «{name}» حذف شود؟ مجوزهای مرتبط با این گروه باطل خواهند شد.", + "empty_state": "هنوز گروهی وجود ندارد.", + "load_more": "بارگیری بیشتر", + "back_to_list": "بازگشت", + "loading": "در حال بارگذاری…", + "virtual_badge": "سامانه", + "member_count_zero": "بدون عضو", + "member_count_one": "۱ عضو", + "member_count_other": "{count} عضو", + "delete_confirm_label": "نام گروه را برای تأیید وارد کنید:", + "delete_confirm_mismatch": "نام گروه را دقیقاً برای تأیید وارد کنید.", + "virtual_internal_name": "داخلی", + "members_loading": "در حال بارگیری اعضا…", + "members_empty": "بدون عضو", + "virtual_internal_explanation": "هر کاربر داخلی روی این سرور", + "create": "ایجاد گروه", + "empty": "هنوز گروهی وجود ندارد.", + "members": "اعضا" + }, + "myshares": { + "copyLink": "کپی پیوند", + "deleteLink": "حذف پیوند", + "notifyByEmail": "اطلاع‌رسانی از طریق ایمیل", + "notifyFailed": "ارسال اعلان ممکن نشد.", + "notifyGroupMembers": "اطلاع‌رسانی به اعضای گروه", + "notifyRateLimited": "اعلان‌های زیادی برای این گیرنده — بعداً دوباره تلاش کنید.", + "removeAccess": "حذف دسترسی", + "resendInvitation": "ارسال مجدد ایمیل دعوت", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "صوتی", + "code": "کد", + "text": "متن" + }, + "common": { + "add": "افزودن", + "cancel": "لغو", + "clear": "Clear", + "close": "بستن", + "confirm": "تأیید", + "copy": "رونوشت", + "create": "ایجاد", + "delete": "حذف", + "download": "بارگیری", + "load_more": "بارگذاری بیشتر", + "loading": "در حال بارگذاری…", + "next": "بعدی", + "no": "خیر", + "previous": "قبلی", + "remove": "Remove", + "rename": "تغییر نام", + "save": "ذخیره", + "search": "جست‌و‌جو", + "yes": "بله" + }, + "device": { + "continue": "ادامه", + "unknown": "ناشناس" + }, + "expiryBucket": { + "expired": "منقضی شده", + "noExpiry": "بدون انقضا", + "today": "امروز", + "tomorrow": "فردا" + }, + "nextcloud": { + "error_title": "خطا", + "sign_in_with": "ورود با {{provider}}" + }, + "search": { + "size_label": "اندازه", + "title": "جست‌و‌جو", + "type": { + "audio": "صوتی" + }, + "type_label": "نوع" + }, + "sizeBucket": { + "folders": "پوشه‌ها" + }, + "view": { + "grid": "نمای شبکه‌ای", + "list": "نمای فهرستی" + } } diff --git a/frontend/static/locales/fr.json b/frontend/static/locales/fr.json index 10e04013..787da1ac 100644 --- a/frontend/static/locales/fr.json +++ b/frontend/static/locales/fr.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Ce lien de connexion n'est plus valide", - "expired_body": "Le lien a peut-être expiré ou a déjà été utilisé. Nous pouvons vous en envoyer un nouveau — il arrivera dans votre boîte de réception dans quelques secondes.", - "resend_to": "Envoyer un nouveau lien à {{email}}", - "generic_unavailable": "Ce lien de connexion n'est plus valide. Il a peut-être déjà été utilisé ou a expiré. Demandez un nouveau lien depuis la page de connexion.", - "service_unavailable": "La connexion par lien magique n'est pas activée sur ce serveur.", - "internal_error": "Une erreur s'est produite lors de votre connexion. Veuillez réessayer.", - "resend_failure": "Une erreur s'est produite lors de l'envoi du lien. Veuillez réessayer.", - "cross_browser_title": "Continuer la connexion sur cet appareil ?", - "cross_browser_body": "Vous avez ouvert ce lien de connexion dans un navigateur ou un appareil différent de celui où vous l'avez demandé.", - "cross_browser_warning": "Si vous avez demandé ce lien, vous pouvez continuer en toute sécurité. Sinon, fermez cette page — cliquer sur Continuer connecterait quelqu'un d'autre à votre compte.", - "cross_browser_continue": "Continuer et se connecter", - "resend_confirmation_title": "Vérifiez votre boîte de réception", - "resend_confirmation_body": "Si le lien de connexion correspondait à un compte actif, un nouveau lien vient d'être envoyé. Veuillez vérifier votre boîte de réception.", - "return_link": "Retour à OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", - "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez-le en cliquant sur le lien ci-dessous :\n{{link}}\n\nLe lien est à usage unique et expire dans {{ttl_hours}} heures.\nSi vous n'attendiez pas cette invitation, vous pouvez ignorer ce message.\n\n— OxiCloud" - }, - "login": { - "subject": "Connexion à OxiCloud", - "body": "Bonjour,\n\nUtilisez le lien ci-dessous pour vous connecter à OxiCloud. Le lien est à usage unique et expire dans {{ttl_minutes}} minutes. Ouvrez-le sur le même appareil que celui où vous l'avez demandé.\n\n{{link}}\n\nSi vous n'avez pas demandé ce lien de connexion, vous pouvez ignorer ce message — aucune action supplémentaire n'est nécessaire.\n\n— OxiCloud" - }, - "kind_file": "fichier", - "kind_folder": "dossier", - "english_fallback_divider": "--- Version anglaise ci-dessous ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Ce lien de connexion n'est plus valide", + "expired_body": "Le lien a peut-être expiré ou a déjà été utilisé. Nous pouvons vous en envoyer un nouveau — il arrivera dans votre boîte de réception dans quelques secondes.", + "resend_to": "Envoyer un nouveau lien à {{email}}", + "generic_unavailable": "Ce lien de connexion n'est plus valide. Il a peut-être déjà été utilisé ou a expiré. Demandez un nouveau lien depuis la page de connexion.", + "service_unavailable": "La connexion par lien magique n'est pas activée sur ce serveur.", + "internal_error": "Une erreur s'est produite lors de votre connexion. Veuillez réessayer.", + "resend_failure": "Une erreur s'est produite lors de l'envoi du lien. Veuillez réessayer.", + "cross_browser_title": "Continuer la connexion sur cet appareil ?", + "cross_browser_body": "Vous avez ouvert ce lien de connexion dans un navigateur ou un appareil différent de celui où vous l'avez demandé.", + "cross_browser_warning": "Si vous avez demandé ce lien, vous pouvez continuer en toute sécurité. Sinon, fermez cette page — cliquer sur Continuer connecterait quelqu'un d'autre à votre compte.", + "cross_browser_continue": "Continuer et se connecter", + "resend_confirmation_title": "Vérifiez votre boîte de réception", + "resend_confirmation_body": "Si le lien de connexion correspondait à un compte actif, un nouveau lien vient d'être envoyé. Veuillez vérifier votre boîte de réception.", + "return_link": "Retour à OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", + "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez-le en cliquant sur le lien ci-dessous :\n{{link}}\n\nLe lien est à usage unique et expire dans {{ttl_hours}} heures.\nSi vous n'attendiez pas cette invitation, vous pouvez ignorer ce message.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", - "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez OxiCloud pour voir votre nouveau partage :\n{{login_link}}\n\nVous avez peut-être d'autres nouveaux partages de {{inviter}} — connectez-vous pour voir tous vos éléments partagés.\n\n— OxiCloud\n\nVous recevez ce message parce que vous avez un compte OxiCloud et que la préférence de notification de partage est activée. Vous pouvez la désactiver dans votre profil (M'avertir par e-mail quand quelqu'un partage avec moi)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Système de stockage cloud minimaliste" - }, - "nav": { - "files": "Fichiers", - "shared": "Partages", - "recent": "Récents", - "favorites": "Favoris", - "photos": "Photos", - "music": "Musique", - "trash": "Corbeille", - "sharedwithme": "Partages avec moi" - }, - "photos": { - "empty_state": "Pas encore de photos", - "empty_hint": "Téléchargez des images ou des vidéos pour les voir ici", - "items_selected": "sélectionnés", - "view_daily": "Jour", - "view_monthly": "Mois", - "view_yearly": "Année" - }, - "music": { - "create_playlist": "Créer une Playlist", - "playlists": "Playlists", - "no_playlists": "Aucune playlist", - "select_playlist": "Sélectionnez une playlist", - "select_hint": "Choisissez une playlist dans la barre latérale ou créez-en une nouvelle", - "add_tracks": "Ajouter des Pistes", - "no_tracks": "Aucune piste dans cette playlist", - "unknown_artist": "Artiste Inconnu", - "unknown_title": "Inconnu", - "confirm_delete": "Supprimer cette playlist ?", - "playlist_name": "Nom de la playlist", - "create": "Créer", - "delete": "Supprimer", - "share": "Partager", - "edit": "Modifier", - "play_all": "Tout Lire", - "shuffle": "Aléatoire", - "repeat": "Répéter", - "repeat_one": "Répéter Une", - "queue": "File d'attente", - "queue_empty": "File d'attente vide", - "not_playing": "Pas en lecture", - "play": "Lecture", - "pause": "Pause", - "previous": "Précédent", - "next": "Suivant", - "volume": "Volume", - "mute": "Muet", - "unmute": "Activer le son", - "title": "Titre", - "artist": "Artiste", - "album": "Album", - "tracks": "pistes", - "add": "Ajouter", - "added": "Ajouté !", - "added_to_playlist": "ajouté à la playlist", - "add_to_playlist": "Ajouter à la playlist", - "load_error": "Erreur de chargement des playlists", - "add_error": "Impossible d'ajouter les pistes", - "no_playlists_yet": "Pas encore de playlists. Créez-en une d'abord !", - "selected_files": "Sélectionnés :", - "error": "Erreur", - "search_audio": "Rechercher des fichiers audio…", - "no_audio_files": "Aucun fichier audio trouvé", - "selected": "sélectionnés", - "loading": "Chargement…", - "search_error": "Impossible de charger les fichiers audio", - "adding": "Ajout en cours…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Rechercher des fichiers...", - "new_folder": "Nouveau dossier", - "upload": "Téléverser", - "upload_files": "Téléverser des fichiers", - "upload_folder": "Téléverser un dossier", - "upload.uploading": "Envoi en cours...", - "upload.complete": "{count} / {total} envoyés", - "upload.files": "fichiers", - "rename": "Renommer", - "move": "Déplacer vers...", - "move_to": "Déplacer vers", - "delete": "Supprimer", - "download": "Télécharger", - "view": "Afficher", - "cancel": "Annuler", - "confirm": "Confirmer", - "share": "Partager", - "favorite": "Ajouter aux favoris", - "unfavorite": "Retirer des favoris", - "copy": "Copier", - "notify": "Notifier", - "send": "Envoyer", - "clear_recent": "Effacer les récents", - "logout": "Se déconnecter", - "create": "Créer", - "search_btn": "Rechercher", - "close": "Fermer", - "delete_permanently": "Supprimer définitivement", - "empty_trash": "Vider la corbeille", - "open_parent_folder": "Aller au dossier parent", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Apparence", - "about": "À propos d'OxiCloud", - "about_description": "Plateforme de stockage cloud construite avec Rust et Architecture Propre. Rapide, sécurisée et privée.", - "admin_panel": "Panneau d'administration", - "profile": "Mon profil", - "role_user": "Utilisateur", - "theme": { - "light": "Clair", - "dark": "Sombre", - "auto": "Comme le système" + "login": { + "subject": "Connexion à OxiCloud", + "body": "Bonjour,\n\nUtilisez le lien ci-dessous pour vous connecter à OxiCloud. Le lien est à usage unique et expire dans {{ttl_minutes}} minutes. Ouvrez-le sur le même appareil que celui où vous l'avez demandé.\n\n{{link}}\n\nSi vous n'avez pas demandé ce lien de connexion, vous pouvez ignorer ce message — aucune action supplémentaire n'est nécessaire.\n\n— OxiCloud" }, - "manage_groups": "Gérer les groupes" + "kind_file": "fichier", + "kind_folder": "dossier", + "english_fallback_divider": "--- Version anglaise ci-dessous ---" + } }, - "share": { - "dialogTitle": "Lien de partage", - "linkLabel": "Lien partagé :", - "copyLink": "Copier", - "permissions": "Permissions :", - "permissionRead": "Lecture", - "permissionWrite": "Écriture", - "permissionReshare": "Repartager", - "password": "Protection par mot de passe :", - "generatePassword": "Générer", - "expiration": "Date d'expiration :", - "update": "Mettre à jour le partage", - "remove": "Supprimer le partage", - "notifyTitle": "Envoyer une notification", - "notifyEmailLabel": "Adresse e-mail :", - "notifyMessageLabel": "Message (facultatif) :", - "notifySend": "Envoyer la notification", - "shareWithOthers": "Partager avec d'autres", - "sharePublicly": "Partager publiquement", - "shareSettings": "Paramètres de partage", - "shareCopied": "Lien copié dans le presse-papiers", - "shareCreated": "Lien de partage créé avec succès", - "shareUpdated": "Paramètres de partage mis à jour", - "shareRemoved": "Partage supprimé avec succès", - "inviteByEmail": "Inviter par e-mail — une invitation sera envoyée", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Lien de partage", - "share_linkLabel": "Lien partagé :", - "share_copyLink": "Copier", - "share_permissions": "Permissions :", - "share_permissionRead": "Lecture", - "share_permissionWrite": "Écriture", - "share_permissionReshare": "Repartager", - "share_password": "Protection par mot de passe :", - "share_generatePassword": "Générer", - "share_expiration": "Date d'expiration :", - "share_update": "Mettre à jour le partage", - "share_remove": "Supprimer le partage", - "share_notifyTitle": "Envoyer une notification", - "share_notifyEmailLabel": "Adresse e-mail :", - "share_notifyMessageLabel": "Message (facultatif) :", - "share_notifySend": "Envoyer la notification", - "shared": { - "backToFiles": "Retour aux fichiers", - "pageTitle": "Ressources partagées", - "pageDescription": "Gérez vos fichiers et dossiers partagés", - "filterType": "Type :", - "filterAll": "Tous", - "filterFiles": "Fichiers", - "filterFolders": "Dossiers", - "sortBy": "Trier par :", - "sortByName": "Nom", - "sortByDate": "Date de partage", - "sortByExpiration": "Expiration", - "search": "Rechercher", - "colName": "Nom", - "colType": "Type", - "colDateShared": "Date de partage", - "colExpiration": "Expiration", - "colPermissions": "Permissions", - "colPassword": "Mot de passe", - "colActions": "Actions", - "emptyStateTitle": "Aucune ressource partagée", - "emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", - "goToFiles": "Aller aux fichiers", - "typeFile": "Fichier", - "typeFolder": "Dossier", - "noExpiration": "Sans expiration", - "hasPassword": "Oui", - "noPassword": "Non", - "editShare": "Modifier le partage", - "notifyShare": "Notifier quelqu'un", - "copyLink": "Copier le lien", - "removeShare": "Supprimer le partage", - "linkCopied": "Lien copié dans le presse-papiers !", - "linkCopyFailed": "Erreur lors de la copie du lien", - "itemUpdated": "Paramètres de partage mis à jour", - "itemRemoved": "Partage supprimé avec succès", - "invalidEmail": "Veuillez entrer une adresse e-mail valide", - "notificationSent": "Notification envoyée avec succès", - "notificationFailed": "Erreur lors de l'envoi de la notification", - "shared_backToFiles": "Retour aux fichiers", - "shared_pageTitle": "Ressources partagées", - "shared_pageDescription": "Gérez vos fichiers et dossiers partagés", - "shared_filterType": "Type :", - "shared_filterAll": "Tous", - "shared_filterFiles": "Fichiers", - "shared_filterFolders": "Dossiers", - "shared_sortBy": "Trier par :", - "shared_sortByName": "Nom", - "shared_sortByDate": "Date de partage", - "shared_sortByExpiration": "Expiration", - "shared_search": "Rechercher", - "shared_colName": "Nom", - "shared_colType": "Type", - "shared_colDateShared": "Date de partage", - "shared_colExpiration": "Expiration", - "shared_colPermissions": "Permissions", - "shared_colPassword": "Mot de passe", - "shared_colActions": "Actions", - "shared_emptyStateTitle": "Aucune ressource partagée", - "shared_emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", - "shared_goToFiles": "Aller aux fichiers", - "shared_typeFile": "Fichier", - "shared_typeFolder": "Dossier", - "shared_noExpiration": "Sans expiration", - "shared_hasPassword": "Oui", - "shared_noPassword": "Non", - "shared_editShare": "Modifier le partage", - "shared_notifyShare": "Notifier quelqu'un", - "shared_copyLink": "Copier le lien", - "shared_removeShare": "Supprimer le partage", - "shared_linkCopied": "Lien copié dans le presse-papiers !", - "shared_linkCopyFailed": "Erreur lors de la copie du lien", - "shared_itemUpdated": "Paramètres de partage mis à jour", - "shared_itemRemoved": "Partage supprimé avec succès", - "shared_invalidEmail": "Veuillez entrer une adresse e-mail valide", - "shared_notificationSent": "Notification envoyée avec succès", - "shared_notificationFailed": "Erreur lors de l'envoi de la notification" - }, - "files": { - "name": "Nom", - "type": "Type", - "size": "Taille", - "modified": "Modifié", - "no_files": "Aucun fichier dans ce dossier", - "empty_hint": "Téléversez des fichiers ou créez des dossiers pour commencer", - "loading": "Chargement des fichiers…", - "view_grid": "Vue en grille", - "view_list": "Vue en liste", - "file_types": { - "document": "Document", - "image": "Image", - "video": "Vidéo", - "audio": "Audio", - "pdf": "PDF", - "text": "Texte", - "folder": "Dossier", - "spreadsheet": "Tableur", - "presentation": "Présentation", - "archive": "Archive", - "installer": "Installateur", - "code": "Code" - }, - "owner": "Propriétaire" - }, - "dialogs": { - "rename_folder": "Renommer le dossier", - "rename_file": "Renommer le fichier", - "new_name": "Nouveau nom", - "new_folder_title": "Nouveau dossier", - "folder_name": "Nom du dossier", - "folder_placeholder": "Mon dossier", - "rename_title": "Renommer", - "move_file": "Déplacer le fichier", - "move_folder": "Déplacer le dossier", - "select_destination": "Sélectionnez le dossier de destination :", - "root": "Racine", - "delete_confirmation": "Êtes-vous sûr de vouloir supprimer", - "and_contents": "et tout son contenu", - "no_undo": "Cette action est irréversible", - "confirm_title": "Confirmer l'action", - "confirm_delete": "Déplacer vers la corbeille", - "confirm_delete_file": "Êtes-vous sûr de vouloir déplacer le fichier « {{name}} » vers la corbeille ?", - "confirm_delete_folder": "Êtes-vous sûr de vouloir déplacer le dossier « {{name}} » et tout son contenu vers la corbeille ?", - "confirm_permanent_delete": "Supprimer définitivement", - "confirm_permanent_delete_msg": "Êtes-vous sûr de vouloir supprimer définitivement cet élément ? Cette action est irréversible.", - "confirm_empty_trash": "Vider la corbeille", - "confirm_delete_share": "Supprimer le lien de partage", - "confirm_delete_share_msg": "Êtes-vous sûr de vouloir supprimer ce lien de partage ?", - "share_file": "Partager le fichier", - "share_folder": "Partager le dossier", - "existing_shares": "Partages existants", - "share_options": "Options de partage", - "password": "Mot de passe", - "expiration": "Expiration", - "permissions": "Permissions", - "generated_link": "Lien généré", - "notify": "Envoyer une notification", - "recipient": "Destinataire", - "message": "Message", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Déplacer vers le dossier personnel" - }, - "dropzone": { - "drag_files": "Glissez des fichiers ici ou cliquez pour sélectionner", - "drop_files": "Déposez les fichiers pour téléverser" - }, - "permissions": { - "read": "Lecture", - "write": "Écriture", - "reshare": "Repartager" - }, - "errors": { - "file_not_found": "Fichier introuvable", - "folder_not_found": "Dossier introuvable", - "delete_error": "Erreur lors de la suppression", - "upload_error": "Erreur lors du téléversement", - "rename_error": "Erreur lors du renommage", - "move_error": "Erreur lors du déplacement", - "empty_name": "Le nom ne peut pas être vide", - "name_exists": "Un fichier ou dossier portant ce nom existe déjà", - "generic_error": "Une erreur est survenue", - "group_name_invalid": "Le nom du groupe doit respecter le format préfixe d'email (lettres, chiffres, point, tiret, souligné ; 1–64 caractères).", - "group_cycle": "Ce membre créerait une référence circulaire entre groupes.", - "group_depth_exceeded": "Cette profondeur d'imbrication dépasse le maximum autorisé (8).", - "group_virtual_immutable": "Le groupe « Internal » est géré par le système et ne peut pas être modifié.", - "group_not_found": "Groupe introuvable.", - "group_name_taken": "Un groupe portant ce nom existe déjà." - }, - "breadcrumb": { - "home": "Accueil" - }, - "trash": { - "empty_trash": "Vider la corbeille", - "empty_state": "La corbeille est vide", - "original_location": "Emplacement d'origine", - "deleted_date": "Date de suppression", - "remaining": "Restant", - "actions": "Actions", - "restore": "Restaurer", - "delete_permanently": "Supprimer définitivement", - "empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés.", - "groupby": { - "remaining_days": "Jours restants", - "trashed_time": "Date de suppression" - } - }, - "daysRemaining": { - "expired": "Expiré", - "today": "Aujourd'hui", - "tomorrow": "Demain", - "inDays": "{{count}} jours" - }, - "expiryChip": { - "never": "N'expire jamais", - "expired": "Expiré", - "today": "Expire aujourd'hui", - "tomorrow": "Expire demain", - "inDays": "Expire dans {{count}} jours", - "onDate": "Expire le {{date}}" - }, - "auth": { - "login_title": "Se connecter", - "username": "Nom d'utilisateur", - "username_placeholder": "Entrez votre nom d'utilisateur", - "login_identifier": "Nom d'utilisateur ou e-mail", - "login_identifier_placeholder": "Saisissez votre nom d'utilisateur ou e-mail", - "password": "Mot de passe", - "password_placeholder": "Entrez votre mot de passe", - "login_button": "Se connecter", - "no_account": "Vous n'avez pas de compte ?", - "register": "S'inscrire", - "admin_setup": "Première fois ?", - "setup": "Configurer l'administrateur", - "register_title": "Créer un compte", - "email": "E-mail", - "email_placeholder": "Entrez votre e-mail", - "confirm_password": "Confirmer le mot de passe", - "confirm_password_placeholder": "Confirmez votre mot de passe", - "register_button": "Créer un compte", - "have_account": "Vous avez déjà un compte ?", - "login": "Se connecter", - "setup_title": "Configuration initiale", - "setup_step1": "Admin", - "setup_step2": "Système", - "setup_step3": "Terminé", - "admin_username": "Nom d'utilisateur administrateur", - "admin_email": "E-mail administrateur", - "admin_password": "Mot de passe administrateur", - "create_admin": "Créer l'administrateur", - "back_to_login": "Déjà configuré ?", - "admin_success": "Compte administrateur créé avec succès ! Vous pouvez maintenant vous connecter.", - "account_success": "Compte créé avec succès ! Vous pouvez maintenant vous connecter.", - "passwords_mismatch": "Les mots de passe ne correspondent pas", - "admin_create_error": "Erreur lors de la création du compte administrateur", - "or": "ou", - "sso_login": "Se connecter avec SSO", - "sso_login_provider": "Se connecter avec {{provider}}", - "magicLinkHint": "Pas de mot de passe ? Saisissez votre adresse e-mail et nous vous enverrons un lien de connexion à usage unique.", - "magicLinkEmailLabel": "Adresse e-mail", - "magicLinkEmailPlaceholder": "vous@exemple.com", - "magicLinkSubmit": "Envoyer le lien de connexion", - "magicLinkSent": "Si un compte existe pour cette adresse, un lien de connexion vient d'être envoyé. Consultez votre boîte de réception.", - "magicLinkUnavailable": "La connexion par e-mail n'est pas disponible sur ce serveur.", - "magicLinkNetworkError": "Impossible de joindre le serveur : {{message}}", - "magicLinkToggle": "Pas de mot de passe ? Recevez un lien par e-mail", - "passwordsMatch": "Les mots de passe correspondent", - "capsLock": "Verr. Maj activé" - }, - "storage": { - "title": "Stockage", - "calculating": "Calcul en cours...", - "used": "{{percentage}}% utilisé ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Ce type de fichier ne peut pas être prévisualisé.", - "download_file": "Télécharger le fichier", - "zoom_in": "Zoom avant", - "zoom_out": "Zoom arrière", - "zoom_reset": "Réinitialiser le zoom" - }, - "language_selector": { - "title": "Bienvenue !", - "subtitle": "Sélectionnez votre langue pour continuer", - "continue": "Continuer", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Aucun favori pour le moment", - "empty_hint": "Marquez des fichiers ou dossiers avec une étoile pour les ajouter à vos favoris", - "add": "Ajouter aux favoris", - "remove": "Retirer des favoris", - "added_title": "Ajouté aux favoris", - "added_msg": "ajouté aux favoris", - "removed_title": "Retiré des favoris", - "removed_msg": "retiré des favoris" - }, - "recent": { - "title": "Récents", - "clear": "Effacer les récents", - "accessed": "Consulté", - "empty_state": "Aucun fichier récent", - "empty_hint": "Les fichiers que vous ouvrez apparaîtront ici", - "loadMore": "Charger plus" - }, - "notifications": { - "file_renamed": "Fichier renommé", - "file_renamed_to": "Fichier renommé en « {{name}} »", - "folder_renamed": "Dossier renommé", - "folder_renamed_to": "Dossier renommé en « {{name}} »", - "file_uploaded": "Fichier téléversé", - "file_deleted": "Fichier déplacé vers la corbeille", - "folder_deleted": "Dossier déplacé vers la corbeille", - "item_deleted_permanently": "Élément supprimé définitivement", - "trash_emptied": "Corbeille vidée avec succès", - "empty": "No notifications", - "title": "Notifications", - "link_created": "Lien créé", - "share_success": "Lien de partage créé avec succès", - "upload_files_section_title": "Dépôt non disponible ici", - "upload_files_section_body": "Accédez à la section Fichiers pour déposer des fichiers" - }, - "batch": { - "one_selected": "1 élément sélectionné", - "n_selected": "{{count}} éléments sélectionnés", - "confirm_delete": "Voulez-vous vraiment déplacer {{count}} éléments vers la corbeille ?", - "move_title": "Déplacer {{count}} élément(s)", - "add_favorites": "Ajouter aux favoris", - "move_copy": "Déplacer ou copier" - }, - "admin": { - "page_title": "Panneau d'administration", - "back_to_app": "Retour à OxiCloud", - "loading": "Chargement…", - "access_denied": "Accès refusé", - "access_denied_desc": "Privilèges d'administrateur requis.", - "sign_in": "Se connecter", - "tab_dashboard": "Tableau de bord", - "tab_users": "Utilisateurs", - "tab_oidc": "SSO / OIDC", - "total_users": "Utilisateurs totaux", - "active_users": "Utilisateurs actifs", - "admins": "Admins", - "version": "Version", - "storage_overview": "Aperçu du stockage", - "used": "Utilisé", - "total_quota": "Quota total", - "usage_pct": "Utilisation %", - "users_over_80": "Utilisateurs >80% quota", - "users_over_quota": "Utilisateurs dépassant le quota", - "system": "Système", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quotas", - "enabled": "Activé", - "disabled": "Désactivé", - "active": "Actif", - "off": "Inactif", - "allow_registration": "Autoriser l'inscription publique", - "registration_warning": "L'inscription publique est désactivée. Seuls les admins peuvent créer des utilisateurs.", - "user_management": "Gestion des utilisateurs", - "create_user": "Créer un utilisateur", - "col_user": "Utilisateur", - "col_role": "Rôle", - "col_auth": "Auth", - "col_status": "Statut", - "col_storage": "Stockage", - "col_last_login": "Dernière connexion", - "col_actions": "Actions", - "loading_users": "Chargement des utilisateurs…", - "failed_load_users": "Échec du chargement", - "no_users_found": "Aucun utilisateur trouvé", - "showing_users": "Affichage {{from}}-{{to}} sur {{total}}", - "prev": "Précédent", - "next": "Suivant", - "inactive": "Inactif", - "you_badge": "(vous)", - "local": "Local", - "never": "Jamais", - "just_now": "À l'instant", - "minutes_ago": "il y a {{n}}min", - "hours_ago": "il y a {{n}}h", - "days_ago": "il y a {{n}}j", - "edit_quota_title": "Modifier le quota", - "reset_password_title": "Réinitialiser le mot de passe", - "toggle_role_title": "Changer de rôle", - "deactivate_title": "Désactiver", - "activate_title": "Activer", - "delete_title": "Supprimer", - "sso_title": "Authentification unique (OIDC / SSO)", - "enable_sso": "Activer l'authentification SSO", - "provider_name": "Nom du fournisseur", - "issuer_url": "URL de l'émetteur", - "issuer_url_hint": "URL de l'émetteur OpenID Connect", - "auto_discover": "Auto-découverte", - "discovering": "Découverte…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Laisser vide pour conserver la valeur", - "secret_configured": "Un client secret est déjà configuré", - "callback_url": "URL de rappel", - "callback_url_hint": "(enregistrer dans votre IdP)", - "advanced_settings": "Paramètres avancés", - "scopes": "Scopes", - "auto_provision": "Provisionner automatiquement les utilisateurs", - "admin_groups": "Groupes admin", - "admin_groups_hint": "Noms de groupes OIDC séparés par des virgules", - "disable_password": "Désactiver la connexion par mot de passe (OIDC uniquement)", - "password_warning": "Cela empêchera TOUTES les connexions par mot de passe !", - "test_btn": "Tester", - "save_btn": "Enregistrer", - "saving": "Enregistrement…", - "settings_saved": "Paramètres enregistrés — OIDC est maintenant {{status}}", - "quota_modal_title": "Mettre à jour le quota", - "quota_user_label": "Utilisateur :", - "new_quota": "Nouveau quota", - "quota_unlimited_hint": "0 pour illimité", - "cancel": "Annuler", - "create_user_title": "Créer un nouvel utilisateur", - "username_label": "Nom d'utilisateur", - "username_placeholder": "jeandupont", - "username_hint": "3–32 caractères", - "password_label": "Mot de passe", - "password_placeholder": "Min 8 caractères", - "email_label": "E-mail", - "email_optional": "(facultatif)", - "email_placeholder": "utilisateur@exemple.com (auto-généré si vide)", - "role_label": "Rôle", - "role_user": "Utilisateur", - "role_admin": "Admin", - "quota_label": "Quota", - "creating": "Création…", - "reset_pw_title": "Réinitialiser le mot de passe", - "new_password_label": "Nouveau mot de passe", - "resetting": "Réinitialisation…", - "reset_btn": "Réinitialiser", - "confirm_role_change": "Changer le rôle en {{role}} ?", - "confirm_deactivate": "Voulez-vous vraiment désactiver cet utilisateur ?", - "confirm_activate": "Voulez-vous vraiment activer cet utilisateur ?", - "confirm_delete_user": "SUPPRIMER l'utilisateur \"{{name}}\" ? Irréversible !", - "confirm_action": "Confirmer l'action", - "confirm_yes": "Confirmer", - "confirm_no": "Annuler", - "error_username_short": "Le nom d'utilisateur doit contenir au moins 3 caractères", - "error_password_short": "Le mot de passe doit contenir au moins 8 caractères", - "error_generic": "Échec", - "error_network": "Erreur réseau : {{message}}", - "error_create_user": "Impossible de créer l'utilisateur", - "tab_storage": "Stockage", - "storage_title": "Configuration du stockage", - "storage_current_backend": "Backend actuel", - "storage_total_blobs": "Total des blobs", - "storage_total_size": "Taille totale", - "storage_dedup_ratio": "Taux de déduplication", - "storage_backend": "Backend", - "storage_local": "Local", - "storage_s3": "Compatible S3", - "storage_provider_preset": "Préréglage du fournisseur", - "storage_preset_custom": "Personnalisé", - "storage_endpoint_url": "URL du point de terminaison", - "storage_endpoint_hint": "Laisser vide pour AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Région", - "storage_access_key": "Clé d'accès", - "storage_secret_key": "Clé secrète", - "storage_secret_configured": "Clé configurée", - "storage_key_placeholder": "Saisir une nouvelle clé", - "storage_path_style": "Forcer le style de chemin", - "storage_path_style_hint": "Requis pour MinIO et certains services compatibles S3", - "storage_test_connection": "Tester la connexion", - "storage_test_success": "Connexion réussie", - "storage_test_failure": "Échec de la connexion", - "storage_save": "Enregistrer la configuration", - "storage_saved": "Configuration enregistrée", - "storage_migration": "Migration des données", - "storage_migration_coming_soon": "Outils de migration bientôt disponibles", - "migration_status_label": "État de la migration", - "migration_start": "Démarrer la migration", - "migration_pause": "Pause", - "migration_resume": "Reprendre", - "migration_verify": "Vérifier", - "migration_complete": "Terminer", - "migration_started": "Migration démarrée", - "migration_paused_msg": "Migration en pause", - "migration_resumed_msg": "Migration reprise", - "migration_completed_msg": "Migration terminée avec succès", - "migration_verifying": "Vérification en cours...", - "migration_verify_passed": "Vérification réussie", - "migration_verify_failed": "Échec de la vérification", - "migration_failed_blobs": "Blobs échoués", - "testing": "Test en cours...", - "tab_smtp": "SMTP", - "smtp_title": "E-mail sortant (SMTP)", - "smtp_intro": "Le SMTP est configuré exclusivement via les variables d'environnement (OXICLOUD_SMTP_*). Les valeurs ci-dessous proviennent du serveur en cours d'exécution — pour les modifier, éditez l'environnement et redémarrez OxiCloud.", - "smtp_enabled_label": "État", - "smtp_enabled": "Activé", - "smtp_disabled": "Désactivé (hôte non défini)", - "smtp_test_title": "Envoyer un e-mail de test", - "smtp_test_intro": "Envoie un message de diagnostic au destinataire ci-dessous et affiche la réponse du serveur SMTP afin que vous puissiez la corréler avec les journaux de votre relais.", - "smtp_test_to": "Adresse du destinataire", - "smtp_send_test": "Envoyer l'e-mail de test", - "smtp_sending": "Envoi…", - "smtp_sent": "E-mail de test envoyé.", - "smtp_send_failed": "Échec de l'envoi.", - "smtp_server_code": "Le serveur a répondu", - "smtp_test_missing_to": "Veuillez saisir une adresse de destinataire.", - "smtp_not_configured": "Le SMTP n'est pas configuré sur ce serveur." - }, - "profile": { - "page_title": "Profil", - "back_to_app": "Retour à OxiCloud", - "loading": "Chargement…", - "not_authenticated": "Non authentifié", - "not_authenticated_desc": "Connectez-vous pour voir votre profil.", - "sign_in": "Se connecter", - "role_admin": "Administrateur", - "role_user": "Utilisateur", - "account_details": "Détails du compte", - "username": "Nom d'utilisateur", - "email": "E-mail", - "role": "Rôle", - "last_login": "Dernière connexion", - "storage": "Stockage", - "used": "Utilisé", - "quota": "Quota", - "usage": "Utilisation", - "unlimited": "Illimité", - "app_passwords": "Mots de passe d'application", - "app_pw_desc": "Générez des mots de passe pour les clients WebDAV, CalDAV et CardDAV. Chaque mot de passe n'est affiché qu'une seule fois.", - "app_pw_label_placeholder": "Libellé (ex. Thunderbird, macOS)", - "generate": "Générer", - "generating": "Génération…", - "new_password_for": "Nouveau mot de passe pour", - "copy_warning": "Copiez ce mot de passe maintenant. Vous ne pourrez plus le revoir.", - "copy_to_clipboard": "Copier dans le presse-papiers", - "col_label": "Libellé", - "col_created": "Créé", - "col_last_used": "Dernière utilisation", - "col_status": "Statut", - "active": "Actif", - "revoked": "Révoqué", - "revoke_title": "Révoquer", - "no_app_passwords": "Aucun mot de passe d'application.", - "client_sessions": "Sessions client", - "client_sessions_desc": "Générées automatiquement lors de la connexion d'un client compatible Nextcloud.", - "col_client": "Client", - "never": "Jamais", - "just_now": "À l'instant", - "minutes_ago": "il y a {{n}} min", - "hours_ago": "il y a {{n}}h", - "days_ago": "il y a {{n}} jours", - "edit_profile": "Modifier le profil", - "edit_oidc_managed": "Pour modifier vos informations (nom, prénom, photo de profil, …), veuillez les mettre à jour chez votre fournisseur d'identité. Vos changements apparaîtront à votre prochaine connexion.", - "username_claim_hint": "2 à 64 caractères, lettres / chiffres / point / tiret / souligné. Une fois choisi, le nom d'utilisateur ne peut plus être modifié (les clients DAV/NextCloud en dépendent).", - "username_already_claimed": "Nom d'utilisateur fixé et non modifiable (les clients DAV/NextCloud en dépendent).", - "given_name": "Prénom", - "family_name": "Nom", - "notify_on_share": "M'avertir par e-mail quand quelqu'un partage avec moi", - "notify_on_share_hint": "Lorsque décoché, les partages apparaissent toujours dans votre compte — vous ne recevrez simplement pas d'e-mail à leur sujet.", - "save_profile": "Enregistrer", - "profile_saved": "Profil mis à jour", - "profile_no_changes": "Aucun changement à enregistrer.", - "profile_save_failed": "Échec de l'enregistrement", - "username_taken_error": "Ce nom d'utilisateur est déjà pris.", - "username_immutable_error": "Votre nom d'utilisateur est déjà défini et ne peut plus être modifié ici. Contactez un administrateur si vous souhaitez le renommer.", - "change_password": "Changer le mot de passe", - "current_password": "Mot de passe actuel", - "new_password": "Nouveau mot de passe", - "min_8_chars": "Au moins 8 caractères", - "confirm_password": "Confirmer le nouveau mot de passe", - "update_password": "Mettre à jour le mot de passe", - "updating": "Mise à jour…", - "password_updated": "Mot de passe mis à jour avec succès", - "passwords_no_match": "Les mots de passe ne correspondent pas", - "password_too_short": "Le mot de passe doit contenir au moins 8 caractères", - "password_change_failed": "Échec du changement de mot de passe", - "error_network": "Erreur réseau : {{message}}", - "error_label_required": "Veuillez entrer un libellé", - "error_create_pw": "Impossible de créer le mot de passe", - "confirm_revoke": "Révoquer le mot de passe \"{{label}}\" ? Les clients l'utilisant ne fonctionneront plus.", - "error_revoke": "Échec de la révocation", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Téléchargement en cours...", - "files": "fichiers", - "complete": "{{count}} / {{total}} téléchargés" - }, - "storage_quota_exceeded": "Quota de stockage dépassé", - "sharedwithme": { - "pageTitle": "Partagé avec moi", - "pageDescription": "Fichiers et dossiers que d'autres utilisateurs ont partagés avec vous", - "emptyStateTitle": "Rien n'a encore été partagé avec vous", - "emptyStateDesc": "Les éléments partagés avec vous par d'autres utilisateurs apparaîtront ici", - "loadMore": "Charger plus", - "sharedBy": "Partagé par", - "colName": "Nom", - "colType": "Type", - "colSharedBy": "Partagé par", - "colDate": "Date de partage", - "colPermissions": "Permissions" - }, - "groupby": { - "none": "Aucun", - "title": "Grouper par", - "type": "Type", - "type.folders": "Dossiers", - "owner": "Propriétaire", - "shareDate": "Date de partage", - "favoriteDate": "Date d'ajout aux favoris", - "accessedAt": "Date d'accès", - "modifiedAt": "Date de modification", - "createdAt": "Date de création", - "size": "Taille", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nouveau" - }, - "dateBucket": { - "today": "Aujourd'hui", - "last7days": "7 derniers jours", - "last30days": "30 derniers jours" - }, - "groups": { - "title": "Gérer les groupes", - "create_button": "Créer un groupe", - "create_dialog_title": "Nouveau groupe", - "edit_dialog_title": "Renommer le groupe", - "name_label": "Nom", - "name_placeholder": "ingenierie", - "description_label": "Description (facultatif)", - "members_section": "Membres", - "add_member_placeholder": "Ajouter un utilisateur ou un groupe…", - "no_members": "Aucun membre pour le moment.", - "remove_member": "Retirer", - "delete_group": "Supprimer le groupe", - "delete_confirm": "Supprimer le groupe « {name} » ? Les autorisations associées à ce groupe seront révoquées.", - "empty_state": "Aucun groupe pour le moment.", - "load_more": "Charger plus", - "back_to_list": "Retour", - "loading": "Chargement…", - "virtual_badge": "Système", - "member_count_zero": "Aucun membre", - "member_count_one": "1 membre", - "member_count_other": "{count} membres", - "delete_confirm_label": "Tapez le nom du groupe pour confirmer :", - "delete_confirm_mismatch": "Tapez le nom du groupe exactement pour confirmer.", - "virtual_internal_name": "Interne", - "members_loading": "Chargement des membres…", - "members_empty": "Aucun membre", - "virtual_internal_explanation": "Tous les utilisateurs internes de ce serveur" - }, - "myshares": { - "copyLink": "Copier le lien", - "deleteLink": "Supprimer le lien", - "notifyByEmail": "Notifier par e-mail", - "notifyFailed": "Impossible d'envoyer la notification.", - "notifyGroupMembers": "Notifier les membres du groupe", - "notifyRateLimited": "Trop de notifications pour ce destinataire — réessayez plus tard.", - "removeAccess": "Retirer l'accès", - "resendInvitation": "Renvoyer l'e-mail d'invitation" - }, - "sort": { - "asc": "croissant", - "desc": "décroissant" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", + "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez OxiCloud pour voir votre nouveau partage :\n{{login_link}}\n\nVous avez peut-être d'autres nouveaux partages de {{inviter}} — connectez-vous pour voir tous vos éléments partagés.\n\n— OxiCloud\n\nVous recevez ce message parce que vous avez un compte OxiCloud et que la préférence de notification de partage est activée. Vous pouvez la désactiver dans votre profil (M'avertir par e-mail quand quelqu'un partage avec moi)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Système de stockage cloud minimaliste" + }, + "nav": { + "files": "Fichiers", + "shared": "Partages", + "recent": "Récents", + "favorites": "Favoris", + "photos": "Photos", + "music": "Musique", + "trash": "Corbeille", + "sharedwithme": "Partages avec moi", + "profile": "Profil", + "shared_with_me": "Partages avec moi" + }, + "photos": { + "empty_state": "Pas encore de photos", + "empty_hint": "Téléchargez des images ou des vidéos pour les voir ici", + "items_selected": "sélectionnés", + "view_daily": "Jour", + "view_monthly": "Mois", + "view_yearly": "Année", + "group_by": "Grouper par" + }, + "music": { + "create_playlist": "Créer une Playlist", + "playlists": "Playlists", + "no_playlists": "Aucune playlist", + "select_playlist": "Sélectionnez une playlist", + "select_hint": "Choisissez une playlist dans la barre latérale ou créez-en une nouvelle", + "add_tracks": "Ajouter des Pistes", + "no_tracks": "Aucune piste dans cette playlist", + "unknown_artist": "Artiste Inconnu", + "unknown_title": "Inconnu", + "confirm_delete": "Supprimer cette playlist ?", + "playlist_name": "Nom de la playlist", + "create": "Créer", + "delete": "Supprimer", + "share": "Partager", + "edit": "Modifier", + "play_all": "Tout Lire", + "shuffle": "Aléatoire", + "repeat": "Répéter", + "repeat_one": "Répéter Une", + "queue": "File d'attente", + "queue_empty": "File d'attente vide", + "not_playing": "Pas en lecture", + "play": "Lecture", + "pause": "Pause", + "previous": "Précédent", + "next": "Suivant", + "volume": "Volume", + "mute": "Muet", + "unmute": "Activer le son", + "title": "Titre", + "artist": "Artiste", + "album": "Album", + "tracks": "pistes", + "add": "Ajouter", + "added": "Ajouté !", + "added_to_playlist": "ajouté à la playlist", + "add_to_playlist": "Ajouter à la playlist", + "load_error": "Erreur de chargement des playlists", + "add_error": "Impossible d'ajouter les pistes", + "no_playlists_yet": "Pas encore de playlists. Créez-en une d'abord !", + "selected_files": "Sélectionnés :", + "error": "Erreur", + "search_audio": "Rechercher des fichiers audio…", + "no_audio_files": "Aucun fichier audio trouvé", + "selected": "sélectionnés", + "loading": "Chargement…", + "search_error": "Impossible de charger les fichiers audio", + "adding": "Ajout en cours…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "Précédent" + }, + "actions": { + "search": "Rechercher des fichiers...", + "new_folder": "Nouveau dossier", + "upload": "Téléverser", + "upload_files": "Téléverser des fichiers", + "upload_folder": "Téléverser un dossier", + "upload.uploading": "Envoi en cours...", + "upload.complete": "{count} / {total} envoyés", + "upload.files": "fichiers", + "rename": "Renommer", + "move": "Déplacer vers...", + "move_to": "Déplacer vers", + "delete": "Supprimer", + "download": "Télécharger", + "view": "Afficher", + "cancel": "Annuler", + "confirm": "Confirmer", + "share": "Partager", + "favorite": "Ajouter aux favoris", + "unfavorite": "Retirer des favoris", + "copy": "Copier", + "notify": "Notifier", + "send": "Envoyer", + "clear_recent": "Effacer les récents", + "logout": "Se déconnecter", + "create": "Créer", + "search_btn": "Rechercher", + "close": "Fermer", + "delete_permanently": "Supprimer définitivement", + "empty_trash": "Vider la corbeille", + "open_parent_folder": "Aller au dossier parent", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Apparence", + "about": "À propos d'OxiCloud", + "about_description": "Plateforme de stockage cloud construite avec Rust et Architecture Propre. Rapide, sécurisée et privée.", + "admin_panel": "Panneau d'administration", + "profile": "Mon profil", + "role_user": "Utilisateur", + "theme": { + "light": "Clair", + "dark": "Sombre", + "auto": "Comme le système" + }, + "manage_groups": "Gérer les groupes", + "admin": "Admin" + }, + "share": { + "dialogTitle": "Lien de partage", + "linkLabel": "Lien partagé :", + "copyLink": "Copier", + "permissions": "Permissions :", + "permissionRead": "Lecture", + "permissionWrite": "Écriture", + "permissionReshare": "Repartager", + "password": "Protection par mot de passe :", + "generatePassword": "Générer", + "expiration": "Date d'expiration :", + "update": "Mettre à jour le partage", + "remove": "Supprimer le partage", + "notifyTitle": "Envoyer une notification", + "notifyEmailLabel": "Adresse e-mail :", + "notifyMessageLabel": "Message (facultatif) :", + "notifySend": "Envoyer la notification", + "shareWithOthers": "Partager avec d'autres", + "sharePublicly": "Partager publiquement", + "shareSettings": "Paramètres de partage", + "shareCopied": "Lien copié dans le presse-papiers", + "shareCreated": "Lien de partage créé avec succès", + "shareUpdated": "Paramètres de partage mis à jour", + "shareRemoved": "Partage supprimé avec succès", + "inviteByEmail": "Inviter par e-mail — une invitation sera envoyée", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Copier", + "copy_failed": "Could not copy link", + "download": "Télécharger", + "files": "Fichiers", + "folders": "Dossiers", + "link_name": "Link name (optional)", + "notifyByEmail": "Notifier par e-mail", + "revoke": "Remove", + "role_label": "Rôle" + }, + "share_dialogTitle": "Lien de partage", + "share_linkLabel": "Lien partagé :", + "share_copyLink": "Copier", + "share_permissions": "Permissions :", + "share_permissionRead": "Lecture", + "share_permissionWrite": "Écriture", + "share_permissionReshare": "Repartager", + "share_password": "Protection par mot de passe :", + "share_generatePassword": "Générer", + "share_expiration": "Date d'expiration :", + "share_update": "Mettre à jour le partage", + "share_remove": "Supprimer le partage", + "share_notifyTitle": "Envoyer une notification", + "share_notifyEmailLabel": "Adresse e-mail :", + "share_notifyMessageLabel": "Message (facultatif) :", + "share_notifySend": "Envoyer la notification", + "shared": { + "backToFiles": "Retour aux fichiers", + "pageTitle": "Ressources partagées", + "pageDescription": "Gérez vos fichiers et dossiers partagés", + "filterType": "Type :", + "filterAll": "Tous", + "filterFiles": "Fichiers", + "filterFolders": "Dossiers", + "sortBy": "Trier par :", + "sortByName": "Nom", + "sortByDate": "Date de partage", + "sortByExpiration": "Expiration", + "search": "Rechercher", + "colName": "Nom", + "colType": "Type", + "colDateShared": "Date de partage", + "colExpiration": "Expiration", + "colPermissions": "Permissions", + "colPassword": "Mot de passe", + "colActions": "Actions", + "emptyStateTitle": "Aucune ressource partagée", + "emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", + "goToFiles": "Aller aux fichiers", + "typeFile": "Fichier", + "typeFolder": "Dossier", + "noExpiration": "Sans expiration", + "hasPassword": "Oui", + "noPassword": "Non", + "editShare": "Modifier le partage", + "notifyShare": "Notifier quelqu'un", + "copyLink": "Copier le lien", + "removeShare": "Supprimer le partage", + "linkCopied": "Lien copié dans le presse-papiers !", + "linkCopyFailed": "Erreur lors de la copie du lien", + "itemUpdated": "Paramètres de partage mis à jour", + "itemRemoved": "Partage supprimé avec succès", + "invalidEmail": "Veuillez entrer une adresse e-mail valide", + "notificationSent": "Notification envoyée avec succès", + "notificationFailed": "Erreur lors de l'envoi de la notification", + "shared_backToFiles": "Retour aux fichiers", + "shared_pageTitle": "Ressources partagées", + "shared_pageDescription": "Gérez vos fichiers et dossiers partagés", + "shared_filterType": "Type :", + "shared_filterAll": "Tous", + "shared_filterFiles": "Fichiers", + "shared_filterFolders": "Dossiers", + "shared_sortBy": "Trier par :", + "shared_sortByName": "Nom", + "shared_sortByDate": "Date de partage", + "shared_sortByExpiration": "Expiration", + "shared_search": "Rechercher", + "shared_colName": "Nom", + "shared_colType": "Type", + "shared_colDateShared": "Date de partage", + "shared_colExpiration": "Expiration", + "shared_colPermissions": "Permissions", + "shared_colPassword": "Mot de passe", + "shared_colActions": "Actions", + "shared_emptyStateTitle": "Aucune ressource partagée", + "shared_emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", + "shared_goToFiles": "Aller aux fichiers", + "shared_typeFile": "Fichier", + "shared_typeFolder": "Dossier", + "shared_noExpiration": "Sans expiration", + "shared_hasPassword": "Oui", + "shared_noPassword": "Non", + "shared_editShare": "Modifier le partage", + "shared_notifyShare": "Notifier quelqu'un", + "shared_copyLink": "Copier le lien", + "shared_removeShare": "Supprimer le partage", + "shared_linkCopied": "Lien copié dans le presse-papiers !", + "shared_linkCopyFailed": "Erreur lors de la copie du lien", + "shared_itemUpdated": "Paramètres de partage mis à jour", + "shared_itemRemoved": "Partage supprimé avec succès", + "shared_invalidEmail": "Veuillez entrer une adresse e-mail valide", + "shared_notificationSent": "Notification envoyée avec succès", + "shared_notificationFailed": "Erreur lors de l'envoi de la notification" + }, + "files": { + "name": "Nom", + "type": "Type", + "size": "Taille", + "modified": "Modifié", + "no_files": "Aucun fichier dans ce dossier", + "empty_hint": "Téléversez des fichiers ou créez des dossiers pour commencer", + "loading": "Chargement des fichiers…", + "view_grid": "Vue en grille", + "view_list": "Vue en liste", + "file_types": { + "document": "Document", + "image": "Image", + "video": "Vidéo", + "audio": "Audio", + "pdf": "PDF", + "text": "Texte", + "folder": "Dossier", + "spreadsheet": "Tableur", + "presentation": "Présentation", + "archive": "Archive", + "installer": "Installateur", + "code": "Code" + }, + "owner": "Propriétaire", + "add_favorites": "Ajouter aux favoris", + "added_favorites": "Ajouté aux favoris", + "col_name": "Nom", + "col_owner": "Propriétaire", + "col_size": "Taille", + "col_type": "Type", + "copy": "Copier", + "edit": "Modifier", + "file": "Fichier", + "folder": "Dossier", + "new_folder": "Nouveau dossier", + "share": "Partager", + "view": "Afficher" + }, + "dialogs": { + "rename_folder": "Renommer le dossier", + "rename_file": "Renommer le fichier", + "new_name": "Nouveau nom", + "new_folder_title": "Nouveau dossier", + "folder_name": "Nom du dossier", + "folder_placeholder": "Mon dossier", + "rename_title": "Renommer", + "move_file": "Déplacer le fichier", + "move_folder": "Déplacer le dossier", + "select_destination": "Sélectionnez le dossier de destination :", + "root": "Racine", + "delete_confirmation": "Êtes-vous sûr de vouloir supprimer", + "and_contents": "et tout son contenu", + "no_undo": "Cette action est irréversible", + "confirm_title": "Confirmer l'action", + "confirm_delete": "Déplacer vers la corbeille", + "confirm_delete_file": "Êtes-vous sûr de vouloir déplacer le fichier « {{name}} » vers la corbeille ?", + "confirm_delete_folder": "Êtes-vous sûr de vouloir déplacer le dossier « {{name}} » et tout son contenu vers la corbeille ?", + "confirm_permanent_delete": "Supprimer définitivement", + "confirm_permanent_delete_msg": "Êtes-vous sûr de vouloir supprimer définitivement cet élément ? Cette action est irréversible.", + "confirm_empty_trash": "Vider la corbeille", + "confirm_delete_share": "Supprimer le lien de partage", + "confirm_delete_share_msg": "Êtes-vous sûr de vouloir supprimer ce lien de partage ?", + "share_file": "Partager le fichier", + "share_folder": "Partager le dossier", + "existing_shares": "Partages existants", + "share_options": "Options de partage", + "password": "Mot de passe", + "expiration": "Expiration", + "permissions": "Permissions", + "generated_link": "Lien généré", + "notify": "Envoyer une notification", + "recipient": "Destinataire", + "message": "Message", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Déplacer vers le dossier personnel" + }, + "dropzone": { + "drag_files": "Glissez des fichiers ici ou cliquez pour sélectionner", + "drop_files": "Déposez les fichiers pour téléverser" + }, + "permissions": { + "read": "Lecture", + "write": "Écriture", + "reshare": "Repartager" + }, + "errors": { + "file_not_found": "Fichier introuvable", + "folder_not_found": "Dossier introuvable", + "delete_error": "Erreur lors de la suppression", + "upload_error": "Erreur lors du téléversement", + "rename_error": "Erreur lors du renommage", + "move_error": "Erreur lors du déplacement", + "empty_name": "Le nom ne peut pas être vide", + "name_exists": "Un fichier ou dossier portant ce nom existe déjà", + "generic_error": "Une erreur est survenue", + "group_name_invalid": "Le nom du groupe doit respecter le format préfixe d'email (lettres, chiffres, point, tiret, souligné ; 1–64 caractères).", + "group_cycle": "Ce membre créerait une référence circulaire entre groupes.", + "group_depth_exceeded": "Cette profondeur d'imbrication dépasse le maximum autorisé (8).", + "group_virtual_immutable": "Le groupe « Internal » est géré par le système et ne peut pas être modifié.", + "group_not_found": "Groupe introuvable.", + "group_name_taken": "Un groupe portant ce nom existe déjà." + }, + "breadcrumb": { + "home": "Accueil" + }, + "trash": { + "empty_trash": "Vider la corbeille", + "empty_state": "La corbeille est vide", + "original_location": "Emplacement d'origine", + "deleted_date": "Date de suppression", + "remaining": "Restant", + "actions": "Actions", + "restore": "Restaurer", + "delete_permanently": "Supprimer définitivement", + "empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés.", + "groupby": { + "remaining_days": "Jours restants", + "trashed_time": "Date de suppression" + }, + "delete": "Supprimer définitivement", + "empty_action": "Vider la corbeille" + }, + "daysRemaining": { + "expired": "Expiré", + "today": "Aujourd'hui", + "tomorrow": "Demain", + "inDays": "{{count}} jours" + }, + "expiryChip": { + "never": "N'expire jamais", + "expired": "Expiré", + "today": "Expire aujourd'hui", + "tomorrow": "Expire demain", + "inDays": "Expire dans {{count}} jours", + "onDate": "Expire le {{date}}" + }, + "auth": { + "login_title": "Se connecter", + "username": "Nom d'utilisateur", + "username_placeholder": "Entrez votre nom d'utilisateur", + "login_identifier": "Nom d'utilisateur ou e-mail", + "login_identifier_placeholder": "Saisissez votre nom d'utilisateur ou e-mail", + "password": "Mot de passe", + "password_placeholder": "Entrez votre mot de passe", + "login_button": "Se connecter", + "no_account": "Vous n'avez pas de compte ?", + "register": "S'inscrire", + "admin_setup": "Première fois ?", + "setup": "Configurer l'administrateur", + "register_title": "Créer un compte", + "email": "E-mail", + "email_placeholder": "Entrez votre e-mail", + "confirm_password": "Confirmer le mot de passe", + "confirm_password_placeholder": "Confirmez votre mot de passe", + "register_button": "Créer un compte", + "have_account": "Vous avez déjà un compte ?", + "login": "Se connecter", + "setup_title": "Configuration initiale", + "setup_step1": "Admin", + "setup_step2": "Système", + "setup_step3": "Terminé", + "admin_username": "Nom d'utilisateur administrateur", + "admin_email": "E-mail administrateur", + "admin_password": "Mot de passe administrateur", + "create_admin": "Créer l'administrateur", + "back_to_login": "Déjà configuré ?", + "admin_success": "Compte administrateur créé avec succès ! Vous pouvez maintenant vous connecter.", + "account_success": "Compte créé avec succès ! Vous pouvez maintenant vous connecter.", + "passwords_mismatch": "Les mots de passe ne correspondent pas", + "admin_create_error": "Erreur lors de la création du compte administrateur", + "or": "ou", + "sso_login": "Se connecter avec SSO", + "sso_login_provider": "Se connecter avec {{provider}}", + "magicLinkHint": "Pas de mot de passe ? Saisissez votre adresse e-mail et nous vous enverrons un lien de connexion à usage unique.", + "magicLinkEmailLabel": "Adresse e-mail", + "magicLinkEmailPlaceholder": "vous@exemple.com", + "magicLinkSubmit": "Envoyer le lien de connexion", + "magicLinkSent": "Si un compte existe pour cette adresse, un lien de connexion vient d'être envoyé. Consultez votre boîte de réception.", + "magicLinkUnavailable": "La connexion par e-mail n'est pas disponible sur ce serveur.", + "magicLinkNetworkError": "Impossible de joindre le serveur : {{message}}", + "magicLinkToggle": "Pas de mot de passe ? Recevez un lien par e-mail", + "passwordsMatch": "Les mots de passe correspondent", + "capsLock": "Verr. Maj activé", + "caps_lock": "Verr. Maj activé", + "magic_email_label": "Adresse e-mail", + "magic_hint": "Pas de mot de passe ? Saisissez votre adresse e-mail et nous vous enverrons un lien de connexion à usage unique.", + "magic_unavailable": "La connexion par e-mail n'est pas disponible sur ce serveur.", + "passwords_match": "Les mots de passe correspondent", + "sign_in": "Se connecter" + }, + "storage": { + "title": "Stockage", + "calculating": "Calcul en cours...", + "used": "{{percentage}}% utilisé ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Ce type de fichier ne peut pas être prévisualisé.", + "download_file": "Télécharger le fichier", + "zoom_in": "Zoom avant", + "zoom_out": "Zoom arrière", + "zoom_reset": "Réinitialiser le zoom" + }, + "language_selector": { + "title": "Bienvenue !", + "subtitle": "Sélectionnez votre langue pour continuer", + "continue": "Continuer", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Aucun favori pour le moment", + "empty_hint": "Marquez des fichiers ou dossiers avec une étoile pour les ajouter à vos favoris", + "add": "Ajouter aux favoris", + "remove": "Retirer des favoris", + "added_title": "Ajouté aux favoris", + "added_msg": "ajouté aux favoris", + "removed_title": "Retiré des favoris", + "removed_msg": "retiré des favoris" + }, + "recent": { + "title": "Récents", + "clear": "Effacer les récents", + "accessed": "Consulté", + "empty_state": "Aucun fichier récent", + "empty_hint": "Les fichiers que vous ouvrez apparaîtront ici", + "loadMore": "Charger plus" + }, + "notifications": { + "file_renamed": "Fichier renommé", + "file_renamed_to": "Fichier renommé en « {{name}} »", + "folder_renamed": "Dossier renommé", + "folder_renamed_to": "Dossier renommé en « {{name}} »", + "file_uploaded": "Fichier téléversé", + "file_deleted": "Fichier déplacé vers la corbeille", + "folder_deleted": "Dossier déplacé vers la corbeille", + "item_deleted_permanently": "Élément supprimé définitivement", + "trash_emptied": "Corbeille vidée avec succès", + "empty": "No notifications", + "title": "Notifications", + "link_created": "Lien créé", + "share_success": "Lien de partage créé avec succès", + "upload_files_section_title": "Dépôt non disponible ici", + "upload_files_section_body": "Accédez à la section Fichiers pour déposer des fichiers" + }, + "batch": { + "one_selected": "1 élément sélectionné", + "n_selected": "{{count}} éléments sélectionnés", + "confirm_delete": "Voulez-vous vraiment déplacer {{count}} éléments vers la corbeille ?", + "move_title": "Déplacer {{count}} élément(s)", + "add_favorites": "Ajouter aux favoris", + "move_copy": "Déplacer ou copier" + }, + "admin": { + "page_title": "Panneau d'administration", + "back_to_app": "Retour à OxiCloud", + "loading": "Chargement…", + "access_denied": "Accès refusé", + "access_denied_desc": "Privilèges d'administrateur requis.", + "sign_in": "Se connecter", + "tab_dashboard": "Tableau de bord", + "tab_users": "Utilisateurs", + "tab_oidc": "SSO / OIDC", + "total_users": "Utilisateurs totaux", + "active_users": "Utilisateurs actifs", + "admins": "Admins", + "version": "Version", + "storage_overview": "Aperçu du stockage", + "used": "Utilisé", + "total_quota": "Quota total", + "usage_pct": "Utilisation %", + "users_over_80": "Utilisateurs >80% quota", + "users_over_quota": "Utilisateurs dépassant le quota", + "system": "Système", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quotas", + "enabled": "Activé", + "disabled": "Désactivé", + "active": "Actif", + "off": "Inactif", + "allow_registration": "Autoriser l'inscription publique", + "registration_warning": "L'inscription publique est désactivée. Seuls les admins peuvent créer des utilisateurs.", + "user_management": "Gestion des utilisateurs", + "create_user": "Créer un utilisateur", + "col_user": "Utilisateur", + "col_role": "Rôle", + "col_auth": "Auth", + "col_status": "Statut", + "col_storage": "Stockage", + "col_last_login": "Dernière connexion", + "col_actions": "Actions", + "loading_users": "Chargement des utilisateurs…", + "failed_load_users": "Échec du chargement", + "no_users_found": "Aucun utilisateur trouvé", + "showing_users": "Affichage {{from}}-{{to}} sur {{total}}", + "prev": "Précédent", + "next": "Suivant", + "inactive": "Inactif", + "you_badge": "(vous)", + "local": "Local", + "never": "Jamais", + "just_now": "À l'instant", + "minutes_ago": "il y a {{n}}min", + "hours_ago": "il y a {{n}}h", + "days_ago": "il y a {{n}}j", + "edit_quota_title": "Modifier le quota", + "reset_password_title": "Réinitialiser le mot de passe", + "toggle_role_title": "Changer de rôle", + "deactivate_title": "Désactiver", + "activate_title": "Activer", + "delete_title": "Supprimer", + "sso_title": "Authentification unique (OIDC / SSO)", + "enable_sso": "Activer l'authentification SSO", + "provider_name": "Nom du fournisseur", + "issuer_url": "URL de l'émetteur", + "issuer_url_hint": "URL de l'émetteur OpenID Connect", + "auto_discover": "Auto-découverte", + "discovering": "Découverte…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Laisser vide pour conserver la valeur", + "secret_configured": "Un client secret est déjà configuré", + "callback_url": "URL de rappel", + "callback_url_hint": "(enregistrer dans votre IdP)", + "advanced_settings": "Paramètres avancés", + "scopes": "Scopes", + "auto_provision": "Provisionner automatiquement les utilisateurs", + "admin_groups": "Groupes admin", + "admin_groups_hint": "Noms de groupes OIDC séparés par des virgules", + "disable_password": "Désactiver la connexion par mot de passe (OIDC uniquement)", + "password_warning": "Cela empêchera TOUTES les connexions par mot de passe !", + "test_btn": "Tester", + "save_btn": "Enregistrer", + "saving": "Enregistrement…", + "settings_saved": "Paramètres enregistrés — OIDC est maintenant {{status}}", + "quota_modal_title": "Mettre à jour le quota", + "quota_user_label": "Utilisateur :", + "new_quota": "Nouveau quota", + "quota_unlimited_hint": "0 pour illimité", + "cancel": "Annuler", + "create_user_title": "Créer un nouvel utilisateur", + "username_label": "Nom d'utilisateur", + "username_placeholder": "jeandupont", + "username_hint": "3–32 caractères", + "password_label": "Mot de passe", + "password_placeholder": "Min 8 caractères", + "email_label": "E-mail", + "email_optional": "(facultatif)", + "email_placeholder": "utilisateur@exemple.com (auto-généré si vide)", + "role_label": "Rôle", + "role_user": "Utilisateur", + "role_admin": "Admin", + "quota_label": "Quota", + "creating": "Création…", + "reset_pw_title": "Réinitialiser le mot de passe", + "new_password_label": "Nouveau mot de passe", + "resetting": "Réinitialisation…", + "reset_btn": "Réinitialiser", + "confirm_role_change": "Changer le rôle en {{role}} ?", + "confirm_deactivate": "Voulez-vous vraiment désactiver cet utilisateur ?", + "confirm_activate": "Voulez-vous vraiment activer cet utilisateur ?", + "confirm_delete_user": "SUPPRIMER l'utilisateur \"{{name}}\" ? Irréversible !", + "confirm_action": "Confirmer l'action", + "confirm_yes": "Confirmer", + "confirm_no": "Annuler", + "error_username_short": "Le nom d'utilisateur doit contenir au moins 3 caractères", + "error_password_short": "Le mot de passe doit contenir au moins 8 caractères", + "error_generic": "Échec", + "error_network": "Erreur réseau : {{message}}", + "error_create_user": "Impossible de créer l'utilisateur", + "tab_storage": "Stockage", + "storage_title": "Configuration du stockage", + "storage_current_backend": "Backend actuel", + "storage_total_blobs": "Total des blobs", + "storage_total_size": "Taille totale", + "storage_dedup_ratio": "Taux de déduplication", + "storage_backend": "Backend", + "storage_local": "Local", + "storage_s3": "Compatible S3", + "storage_provider_preset": "Préréglage du fournisseur", + "storage_preset_custom": "Personnalisé", + "storage_endpoint_url": "URL du point de terminaison", + "storage_endpoint_hint": "Laisser vide pour AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Région", + "storage_access_key": "Clé d'accès", + "storage_secret_key": "Clé secrète", + "storage_secret_configured": "Clé configurée", + "storage_key_placeholder": "Saisir une nouvelle clé", + "storage_path_style": "Forcer le style de chemin", + "storage_path_style_hint": "Requis pour MinIO et certains services compatibles S3", + "storage_test_connection": "Tester la connexion", + "storage_test_success": "Connexion réussie", + "storage_test_failure": "Échec de la connexion", + "storage_save": "Enregistrer la configuration", + "storage_saved": "Configuration enregistrée", + "storage_migration": "Migration des données", + "storage_migration_coming_soon": "Outils de migration bientôt disponibles", + "migration_status_label": "État de la migration", + "migration_start": "Démarrer la migration", + "migration_pause": "Pause", + "migration_resume": "Reprendre", + "migration_verify": "Vérifier", + "migration_complete": "Terminer", + "migration_started": "Migration démarrée", + "migration_paused_msg": "Migration en pause", + "migration_resumed_msg": "Migration reprise", + "migration_completed_msg": "Migration terminée avec succès", + "migration_verifying": "Vérification en cours...", + "migration_verify_passed": "Vérification réussie", + "migration_verify_failed": "Échec de la vérification", + "migration_failed_blobs": "Blobs échoués", + "testing": "Test en cours...", + "tab_smtp": "SMTP", + "smtp_title": "E-mail sortant (SMTP)", + "smtp_intro": "Le SMTP est configuré exclusivement via les variables d'environnement (OXICLOUD_SMTP_*). Les valeurs ci-dessous proviennent du serveur en cours d'exécution — pour les modifier, éditez l'environnement et redémarrez OxiCloud.", + "smtp_enabled_label": "État", + "smtp_enabled": "Activé", + "smtp_disabled": "Désactivé (hôte non défini)", + "smtp_test_title": "Envoyer un e-mail de test", + "smtp_test_intro": "Envoie un message de diagnostic au destinataire ci-dessous et affiche la réponse du serveur SMTP afin que vous puissiez la corréler avec les journaux de votre relais.", + "smtp_test_to": "Adresse du destinataire", + "smtp_send_test": "Envoyer l'e-mail de test", + "smtp_sending": "Envoi…", + "smtp_sent": "E-mail de test envoyé.", + "smtp_send_failed": "Échec de l'envoi.", + "smtp_server_code": "Le serveur a répondu", + "smtp_test_missing_to": "Veuillez saisir une adresse de destinataire.", + "smtp_not_configured": "Le SMTP n'est pas configuré sur ce serveur.", + "admin_users": "Admins", + "confirm_role": "Changer le rôle en {{role}} ?", + "dashboard": "Tableau de bord", + "email": "E-mail", + "mig_complete": "Terminer", + "mig_pause": "Pause", + "mig_resume": "Reprendre", + "mig_verify_failed": "Échec de la vérification", + "mig_verify_passed": "Vérification réussie", + "mig_verifying": "Vérification en cours...", + "oidc_auto_provision": "Provisionner automatiquement les utilisateurs", + "oidc_callback": "URL de rappel", + "oidc_client_id": "Client ID", + "oidc_disable_pw": "Désactiver la connexion par mot de passe (OIDC uniquement)", + "oidc_issuer": "URL de l'émetteur", + "oidc_scopes": "Scopes", + "password": "Mot de passe", + "quotas": "Quotas", + "reset_pw_for": "Nouveau mot de passe pour", + "role": "Rôle", + "smtp_fail": "Échec de l'envoi.", + "smtp_send": "Envoyer", + "smtp_test": "Envoyer l'e-mail de test", + "smtp_user_state": "Auth", + "status": "Statut", + "storage": "Stockage", + "storage_endpoint": "URL du point de terminaison", + "storage_tab": "Stockage", + "time_min_ago": "il y a {{n}} min", + "title": "Admin", + "user": "Utilisateur", + "username": "Nom d'utilisateur", + "users": "Utilisateurs" + }, + "profile": { + "page_title": "Profil", + "back_to_app": "Retour à OxiCloud", + "loading": "Chargement…", + "not_authenticated": "Non authentifié", + "not_authenticated_desc": "Connectez-vous pour voir votre profil.", + "sign_in": "Se connecter", + "role_admin": "Administrateur", + "role_user": "Utilisateur", + "account_details": "Détails du compte", + "username": "Nom d'utilisateur", + "email": "E-mail", + "role": "Rôle", + "last_login": "Dernière connexion", + "storage": "Stockage", + "used": "Utilisé", + "quota": "Quota", + "usage": "Utilisation", + "unlimited": "Illimité", + "app_passwords": "Mots de passe d'application", + "app_pw_desc": "Générez des mots de passe pour les clients WebDAV, CalDAV et CardDAV. Chaque mot de passe n'est affiché qu'une seule fois.", + "app_pw_label_placeholder": "Libellé (ex. Thunderbird, macOS)", + "generate": "Générer", + "generating": "Génération…", + "new_password_for": "Nouveau mot de passe pour", + "copy_warning": "Copiez ce mot de passe maintenant. Vous ne pourrez plus le revoir.", + "copy_to_clipboard": "Copier dans le presse-papiers", + "col_label": "Libellé", + "col_created": "Créé", + "col_last_used": "Dernière utilisation", + "col_status": "Statut", + "active": "Actif", + "revoked": "Révoqué", + "revoke_title": "Révoquer", + "no_app_passwords": "Aucun mot de passe d'application.", + "client_sessions": "Sessions client", + "client_sessions_desc": "Générées automatiquement lors de la connexion d'un client compatible Nextcloud.", + "col_client": "Client", + "never": "Jamais", + "just_now": "À l'instant", + "minutes_ago": "il y a {{n}} min", + "hours_ago": "il y a {{n}}h", + "days_ago": "il y a {{n}} jours", + "edit_profile": "Modifier le profil", + "edit_oidc_managed": "Pour modifier vos informations (nom, prénom, photo de profil, …), veuillez les mettre à jour chez votre fournisseur d'identité. Vos changements apparaîtront à votre prochaine connexion.", + "username_claim_hint": "2 à 64 caractères, lettres / chiffres / point / tiret / souligné. Une fois choisi, le nom d'utilisateur ne peut plus être modifié (les clients DAV/NextCloud en dépendent).", + "username_already_claimed": "Nom d'utilisateur fixé et non modifiable (les clients DAV/NextCloud en dépendent).", + "given_name": "Prénom", + "family_name": "Nom", + "notify_on_share": "M'avertir par e-mail quand quelqu'un partage avec moi", + "notify_on_share_hint": "Lorsque décoché, les partages apparaissent toujours dans votre compte — vous ne recevrez simplement pas d'e-mail à leur sujet.", + "save_profile": "Enregistrer", + "profile_saved": "Profil mis à jour", + "profile_no_changes": "Aucun changement à enregistrer.", + "profile_save_failed": "Échec de l'enregistrement", + "username_taken_error": "Ce nom d'utilisateur est déjà pris.", + "username_immutable_error": "Votre nom d'utilisateur est déjà défini et ne peut plus être modifié ici. Contactez un administrateur si vous souhaitez le renommer.", + "change_password": "Changer le mot de passe", + "current_password": "Mot de passe actuel", + "new_password": "Nouveau mot de passe", + "min_8_chars": "Au moins 8 caractères", + "confirm_password": "Confirmer le nouveau mot de passe", + "update_password": "Mettre à jour le mot de passe", + "updating": "Mise à jour…", + "password_updated": "Mot de passe mis à jour avec succès", + "passwords_no_match": "Les mots de passe ne correspondent pas", + "password_too_short": "Le mot de passe doit contenir au moins 8 caractères", + "password_change_failed": "Échec du changement de mot de passe", + "error_network": "Erreur réseau : {{message}}", + "error_label_required": "Veuillez entrer un libellé", + "error_create_pw": "Impossible de créer le mot de passe", + "confirm_revoke": "Révoquer le mot de passe \"{{label}}\" ? Les clients l'utilisant ne fonctionneront plus.", + "error_revoke": "Échec de la révocation", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Les mots de passe ne correspondent pas" + }, + "upload": { + "uploading": "Téléchargement en cours...", + "files": "fichiers", + "complete": "{{count}} / {{total}} téléchargés" + }, + "storage_quota_exceeded": "Quota de stockage dépassé", + "sharedwithme": { + "pageTitle": "Partagé avec moi", + "pageDescription": "Fichiers et dossiers que d'autres utilisateurs ont partagés avec vous", + "emptyStateTitle": "Rien n'a encore été partagé avec vous", + "emptyStateDesc": "Les éléments partagés avec vous par d'autres utilisateurs apparaîtront ici", + "loadMore": "Charger plus", + "sharedBy": "Partagé par", + "colName": "Nom", + "colType": "Type", + "colSharedBy": "Partagé par", + "colDate": "Date de partage", + "colPermissions": "Permissions" + }, + "groupby": { + "none": "Aucun", + "title": "Grouper par", + "type": "Type", + "type.folders": "Dossiers", + "owner": "Propriétaire", + "shareDate": "Date de partage", + "favoriteDate": "Date d'ajout aux favoris", + "accessedAt": "Date d'accès", + "modifiedAt": "Date de modification", + "createdAt": "Date de création", + "size": "Taille", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nouveau", + "folders": "Dossiers" + }, + "dateBucket": { + "today": "Aujourd'hui", + "last7days": "7 derniers jours", + "last30days": "30 derniers jours", + "unknown": "Inconnu" + }, + "groups": { + "title": "Gérer les groupes", + "create_button": "Créer un groupe", + "create_dialog_title": "Nouveau groupe", + "edit_dialog_title": "Renommer le groupe", + "name_label": "Nom", + "name_placeholder": "ingenierie", + "description_label": "Description (facultatif)", + "members_section": "Membres", + "add_member_placeholder": "Ajouter un utilisateur ou un groupe…", + "no_members": "Aucun membre pour le moment.", + "remove_member": "Retirer", + "delete_group": "Supprimer le groupe", + "delete_confirm": "Supprimer le groupe « {name} » ? Les autorisations associées à ce groupe seront révoquées.", + "empty_state": "Aucun groupe pour le moment.", + "load_more": "Charger plus", + "back_to_list": "Retour", + "loading": "Chargement…", + "virtual_badge": "Système", + "member_count_zero": "Aucun membre", + "member_count_one": "1 membre", + "member_count_other": "{count} membres", + "delete_confirm_label": "Tapez le nom du groupe pour confirmer :", + "delete_confirm_mismatch": "Tapez le nom du groupe exactement pour confirmer.", + "virtual_internal_name": "Interne", + "members_loading": "Chargement des membres…", + "members_empty": "Aucun membre", + "virtual_internal_explanation": "Tous les utilisateurs internes de ce serveur", + "create": "Créer un groupe", + "empty": "Aucun groupe pour le moment.", + "members": "Membres" + }, + "myshares": { + "copyLink": "Copier le lien", + "deleteLink": "Supprimer le lien", + "notifyByEmail": "Notifier par e-mail", + "notifyFailed": "Impossible d'envoyer la notification.", + "notifyGroupMembers": "Notifier les membres du groupe", + "notifyRateLimited": "Trop de notifications pour ce destinataire — réessayez plus tard.", + "removeAccess": "Retirer l'accès", + "resendInvitation": "Renvoyer l'e-mail d'invitation", + "publicLinks": "Public links" + }, + "sort": { + "asc": "croissant", + "desc": "décroissant" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Audio", + "code": "Code", + "text": "Texte" + }, + "common": { + "add": "Ajouter", + "cancel": "Annuler", + "clear": "Clear", + "close": "Fermer", + "confirm": "Confirmer", + "copy": "Copier", + "create": "Créer", + "delete": "Supprimer", + "download": "Télécharger", + "load_more": "Charger plus", + "loading": "Chargement…", + "next": "Suivant", + "no": "Non", + "previous": "Précédent", + "remove": "Remove", + "rename": "Renommer", + "save": "Enregistrer", + "search": "Rechercher", + "yes": "Oui" + }, + "device": { + "continue": "Continuer", + "unknown": "Inconnu" + }, + "expiryBucket": { + "expired": "Expiré", + "noExpiry": "Sans expiration", + "today": "Aujourd'hui", + "tomorrow": "Demain" + }, + "nextcloud": { + "error_title": "Erreur", + "sign_in_with": "Se connecter avec {{provider}}" + }, + "search": { + "size_label": "Taille", + "title": "Rechercher", + "type": { + "audio": "Audio" + }, + "type_label": "Type" + }, + "sizeBucket": { + "folders": "Dossiers" + }, + "view": { + "grid": "Vue en grille", + "list": "Vue en liste" + } } diff --git a/frontend/static/locales/hi.json b/frontend/static/locales/hi.json index 87725c07..3ba82a72 100644 --- a/frontend/static/locales/hi.json +++ b/frontend/static/locales/hi.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "यह साइन-इन लिंक अब वैध नहीं है", - "expired_body": "लिंक समाप्त हो गया हो सकता है या पहले से उपयोग किया जा चुका हो सकता है। हम आपको एक नया भेज सकते हैं — यह कुछ ही सेकंड में आपके इनबॉक्स में पहुँच जाएगा।", - "resend_to": "{{email}} को नया लिंक भेजें", - "generic_unavailable": "यह साइन-इन लिंक अब वैध नहीं है। यह पहले से उपयोग किया जा चुका हो सकता है या समाप्त हो गया हो सकता है। लॉगिन पृष्ठ से नया लिंक माँगें।", - "service_unavailable": "इस सर्वर पर मैजिक-लिंक साइन-इन सक्षम नहीं है।", - "internal_error": "साइन इन करते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", - "resend_failure": "लिंक भेजते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", - "cross_browser_title": "इस डिवाइस पर साइन-इन जारी रखें?", - "cross_browser_body": "आपने यह साइन-इन लिंक उससे भिन्न ब्राउज़र या डिवाइस में खोला है जहाँ से आपने इसका अनुरोध किया था।", - "cross_browser_warning": "यदि आपने यह लिंक माँगा है, तो आगे बढ़ना सुरक्षित है। यदि नहीं, तो इस पृष्ठ को बंद कर दें — जारी रखें पर क्लिक करने से कोई और आपके खाते में साइन-इन हो जाएगा।", - "cross_browser_continue": "जारी रखें और साइन इन करें", - "resend_confirmation_title": "अपना इनबॉक्स देखें", - "resend_confirmation_body": "यदि साइन-इन लिंक किसी सक्रिय खाते का था, तो अभी एक नया लिंक भेजा गया है। कृपया अपना इनबॉक्स देखें।", - "return_link": "OxiCloud पर वापस जाएँ" - }, - "email": { - "invitation": { - "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", - "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nइसे नीचे दिए गए लिंक पर क्लिक करके खोलें:\n{{link}}\n\nलिंक केवल एक बार काम करता है और {{ttl_hours}} घंटों में समाप्त हो जाता है।\nयदि आप इस आमंत्रण की अपेक्षा नहीं कर रहे थे, तो आप इस संदेश को अनदेखा कर सकते हैं।\n\n— OxiCloud" - }, - "login": { - "subject": "OxiCloud में साइन इन करें", - "body": "नमस्ते,\n\nOxiCloud में साइन इन करने के लिए नीचे दिए गए लिंक का उपयोग करें। लिंक केवल एक बार काम करता है और {{ttl_minutes}} मिनट में समाप्त हो जाता है। इसे उसी डिवाइस पर खोलें जहाँ से आपने अनुरोध किया था।\n\n{{link}}\n\nयदि आपने यह साइन-इन लिंक नहीं माँगा था, तो आप इस संदेश को अनदेखा कर सकते हैं — किसी और कार्रवाई की आवश्यकता नहीं है।\n\n— OxiCloud" - }, - "kind_file": "फ़ाइल", - "kind_folder": "फ़ोल्डर", - "english_fallback_divider": "--- अंग्रेज़ी संस्करण नीचे ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "यह साइन-इन लिंक अब वैध नहीं है", + "expired_body": "लिंक समाप्त हो गया हो सकता है या पहले से उपयोग किया जा चुका हो सकता है। हम आपको एक नया भेज सकते हैं — यह कुछ ही सेकंड में आपके इनबॉक्स में पहुँच जाएगा।", + "resend_to": "{{email}} को नया लिंक भेजें", + "generic_unavailable": "यह साइन-इन लिंक अब वैध नहीं है। यह पहले से उपयोग किया जा चुका हो सकता है या समाप्त हो गया हो सकता है। लॉगिन पृष्ठ से नया लिंक माँगें।", + "service_unavailable": "इस सर्वर पर मैजिक-लिंक साइन-इन सक्षम नहीं है।", + "internal_error": "साइन इन करते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", + "resend_failure": "लिंक भेजते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", + "cross_browser_title": "इस डिवाइस पर साइन-इन जारी रखें?", + "cross_browser_body": "आपने यह साइन-इन लिंक उससे भिन्न ब्राउज़र या डिवाइस में खोला है जहाँ से आपने इसका अनुरोध किया था।", + "cross_browser_warning": "यदि आपने यह लिंक माँगा है, तो आगे बढ़ना सुरक्षित है। यदि नहीं, तो इस पृष्ठ को बंद कर दें — जारी रखें पर क्लिक करने से कोई और आपके खाते में साइन-इन हो जाएगा।", + "cross_browser_continue": "जारी रखें और साइन इन करें", + "resend_confirmation_title": "अपना इनबॉक्स देखें", + "resend_confirmation_body": "यदि साइन-इन लिंक किसी सक्रिय खाते का था, तो अभी एक नया लिंक भेजा गया है। कृपया अपना इनबॉक्स देखें।", + "return_link": "OxiCloud पर वापस जाएँ" + }, + "email": { + "invitation": { + "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", + "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nइसे नीचे दिए गए लिंक पर क्लिक करके खोलें:\n{{link}}\n\nलिंक केवल एक बार काम करता है और {{ttl_hours}} घंटों में समाप्त हो जाता है।\nयदि आप इस आमंत्रण की अपेक्षा नहीं कर रहे थे, तो आप इस संदेश को अनदेखा कर सकते हैं।\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", - "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nअपना नया साझाकरण देखने के लिए OxiCloud खोलें:\n{{login_link}}\n\nहो सकता है आपके पास {{inviter}} से और भी नए साझाकरण हों — साइन इन करें और अपने सभी साझा किए गए आइटम देखें।\n\n— OxiCloud\n\nआपको यह संदेश इसलिए मिल रहा है क्योंकि आपका OxiCloud खाता है और साझाकरण-सूचना प्राथमिकता चालू है। आप इसे अपनी प्रोफ़ाइल में बंद कर सकते हैं (जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें)।" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "न्यूनतम क्लाउड स्टोरेज सिस्टम" - }, - "nav": { - "files": "फ़ाइलें", - "shared": "साझा", - "recent": "हाल ही में", - "favorites": "पसंदीदा", - "photos": "फ़ोटो", - "music": "संगीत", - "trash": "रद्दी", - "sharedwithme": "मेरे साथ साझा किए गए" - }, - "photos": { - "empty_state": "अभी कोई फ़ोटो नहीं", - "empty_hint": "यहाँ देखने के लिए चित्र या वीडियो अपलोड करें", - "items_selected": "चयनित", - "view_daily": "दिन", - "view_monthly": "महीना", - "view_yearly": "वर्ष" - }, - "music": { - "create_playlist": "प्लेलिस्ट बनाएँ", - "playlists": "प्लेलिस्ट", - "no_playlists": "अभी कोई प्लेलिस्ट नहीं", - "select_playlist": "प्लेलिस्ट चुनें", - "select_hint": "साइडबार से प्लेलिस्ट चुनें या नई बनाएँ", - "add_tracks": "ट्रैक जोड़ें", - "no_tracks": "इस प्लेलिस्ट में कोई ट्रैक नहीं", - "unknown_artist": "अज्ञात कलाकार", - "unknown_title": "अज्ञात", - "confirm_delete": "इस प्लेलिस्ट को हटाएँ?", - "playlist_name": "प्लेलिस्ट का नाम", - "create": "बनाएँ", - "delete": "हटाएँ", - "share": "साझा करें", - "edit": "संपादित करें", - "play_all": "सभी चलाएँ", - "shuffle": "शफल", - "repeat": "दोहराएँ", - "repeat_one": "एक दोहराएँ", - "queue": "कतार", - "queue_empty": "कतार खाली है", - "not_playing": "नहीं चल रहा", - "play": "चलाएँ", - "pause": "रोकें", - "previous": "पिछला", - "next": "अगला", - "volume": "आवाज़", - "mute": "म्यूट", - "unmute": "अनम्यूट", - "title": "शीर्षक", - "artist": "कलाकार", - "album": "एल्बम", - "tracks": "ट्रैक", - "add": "जोड़ें", - "added": "जोड़ा गया!", - "added_to_playlist": "प्लेलिस्ट में जोड़ा गया", - "add_to_playlist": "प्लेलिस्ट में जोड़ें", - "load_error": "प्लेलिस्ट लोड करने में त्रुटि", - "add_error": "प्लेलिस्ट में ट्रैक नहीं जोड़े जा सके", - "no_playlists_yet": "अभी तक कोई प्लेलिस्ट नहीं। पहले एक बनाएं!", - "selected_files": "चयनित:", - "error": "त्रुटि", - "search_audio": "ऑडियो फ़ाइलें खोजें…", - "no_audio_files": "कोई ऑडियो फ़ाइल नहीं मिली", - "selected": "चयनित", - "loading": "लोड हो रहा है…", - "search_error": "ऑडियो फ़ाइलें लोड नहीं हो सकीं", - "adding": "जोड़ा जा रहा है…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "फ़ाइलें खोजें...", - "new_folder": "नया फ़ोल्डर", - "upload": "अपलोड", - "upload_files": "फ़ाइलें अपलोड करें", - "upload_folder": "फ़ोल्डर अपलोड करें", - "upload.uploading": "अपलोड हो रहा है...", - "upload.complete": "{count} / {total} अपलोड हुईं", - "upload.files": "फ़ाइलें", - "rename": "नाम बदलें", - "move": "यहाँ ले जाएँ...", - "move_to": "यहाँ ले जाएँ", - "delete": "हटाएँ", - "download": "डाउनलोड", - "view": "देखें", - "cancel": "रद्द करें", - "confirm": "पुष्टि करें", - "share": "साझा करें", - "favorite": "पसंदीदा में जोड़ें", - "unfavorite": "पसंदीदा से हटाएँ", - "copy": "कॉपी करें", - "notify": "सूचित करें", - "send": "भेजें", - "clear_recent": "हाल ही का साफ़ करें", - "logout": "लॉग आउट", - "create": "बनाएँ", - "search_btn": "खोजें", - "close": "बंद करें", - "delete_permanently": "स्थायी रूप से हटाएँ", - "empty_trash": "रद्दी खाली करें", - "open_parent_folder": "मूल फ़ोल्डर पर जाएं", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "दिखावट", - "about": "OxiCloud के बारे में", - "about_description": "Rust और Clean Architecture से बना क्लाउड स्टोरेज प्लेटफ़ॉर्म। तेज़, सुरक्षित और निजी।", - "admin_panel": "एडमिन पैनल", - "profile": "मेरी प्रोफ़ाइल", - "role_user": "उपयोगकर्ता", - "theme": { - "light": "हल्का", - "dark": "गहरा", - "auto": "सिस्टम जैसा" + "login": { + "subject": "OxiCloud में साइन इन करें", + "body": "नमस्ते,\n\nOxiCloud में साइन इन करने के लिए नीचे दिए गए लिंक का उपयोग करें। लिंक केवल एक बार काम करता है और {{ttl_minutes}} मिनट में समाप्त हो जाता है। इसे उसी डिवाइस पर खोलें जहाँ से आपने अनुरोध किया था।\n\n{{link}}\n\nयदि आपने यह साइन-इन लिंक नहीं माँगा था, तो आप इस संदेश को अनदेखा कर सकते हैं — किसी और कार्रवाई की आवश्यकता नहीं है।\n\n— OxiCloud" }, - "manage_groups": "समूह प्रबंधित करें" + "kind_file": "फ़ाइल", + "kind_folder": "फ़ोल्डर", + "english_fallback_divider": "--- अंग्रेज़ी संस्करण नीचे ---" + } }, - "share": { - "dialogTitle": "शेयर लिंक", - "linkLabel": "शेयर लिंक:", - "copyLink": "कॉपी", - "permissions": "अनुमतियाँ:", - "permissionRead": "पढ़ें", - "permissionWrite": "लिखें", - "permissionReshare": "पुनः साझा करें", - "password": "पासवर्ड सुरक्षा:", - "generatePassword": "जनरेट करें", - "expiration": "समाप्ति तिथि:", - "update": "शेयर अपडेट करें", - "remove": "शेयर हटाएँ", - "notifyTitle": "सूचना भेजें", - "notifyEmailLabel": "ईमेल पता:", - "notifyMessageLabel": "संदेश (वैकल्पिक):", - "notifySend": "सूचना भेजें", - "shareWithOthers": "दूसरों के साथ साझा करें", - "sharePublicly": "सार्वजनिक रूप से साझा करें", - "shareSettings": "साझा सेटिंग्स", - "shareCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ", - "shareCreated": "शेयर लिंक सफलतापूर्वक बनाया गया", - "shareUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", - "shareRemoved": "शेयर सफलतापूर्वक हटाया गया", - "inviteByEmail": "ईमेल द्वारा आमंत्रित करें — आमंत्रण भेजा जाएगा", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "शेयर लिंक", - "share_linkLabel": "शेयर लिंक:", - "share_copyLink": "कॉपी", - "share_permissions": "अनुमतियाँ:", - "share_permissionRead": "पढ़ें", - "share_permissionWrite": "लिखें", - "share_permissionReshare": "पुनः साझा करें", - "share_password": "पासवर्ड सुरक्षा:", - "share_generatePassword": "जनरेट करें", - "share_expiration": "समाप्ति तिथि:", - "share_update": "शेयर अपडेट करें", - "share_remove": "शेयर हटाएँ", - "share_notifyTitle": "सूचना भेजें", - "share_notifyEmailLabel": "ईमेल पता:", - "share_notifyMessageLabel": "संदेश (वैकल्पिक):", - "share_notifySend": "सूचना भेजें", - "shared": { - "backToFiles": "फ़ाइलों पर वापस", - "pageTitle": "साझा संसाधन", - "pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", - "filterType": "प्रकार:", - "filterAll": "सभी", - "filterFiles": "फ़ाइलें", - "filterFolders": "फ़ोल्डर", - "sortBy": "क्रमबद्ध:", - "sortByName": "नाम", - "sortByDate": "साझा तिथि", - "sortByExpiration": "समाप्ति", - "search": "खोजें", - "colName": "नाम", - "colType": "प्रकार", - "colDateShared": "साझा तिथि", - "colExpiration": "समाप्ति", - "colPermissions": "अनुमतियाँ", - "colPassword": "पासवर्ड", - "colActions": "कार्य", - "emptyStateTitle": "अभी कोई साझा संसाधन नहीं", - "emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", - "goToFiles": "फ़ाइलों पर जाएँ", - "typeFile": "फ़ाइल", - "typeFolder": "फ़ोल्डर", - "noExpiration": "कोई समाप्ति नहीं", - "hasPassword": "हाँ", - "noPassword": "नहीं", - "editShare": "शेयर संपादित करें", - "notifyShare": "किसी को सूचित करें", - "copyLink": "लिंक कॉपी करें", - "removeShare": "शेयर हटाएँ", - "linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", - "linkCopyFailed": "लिंक कॉपी करने में विफल", - "itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", - "itemRemoved": "शेयर सफलतापूर्वक हटाया गया", - "invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", - "notificationSent": "सूचना सफलतापूर्वक भेजी गई", - "notificationFailed": "सूचना भेजने में विफल", - "shared_backToFiles": "फ़ाइलों पर वापस", - "shared_pageTitle": "साझा संसाधन", - "shared_pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", - "shared_filterType": "प्रकार:", - "shared_filterAll": "सभी", - "shared_filterFiles": "फ़ाइलें", - "shared_filterFolders": "फ़ोल्डर", - "shared_sortBy": "क्रमबद्ध:", - "shared_sortByName": "नाम", - "shared_sortByDate": "साझा तिथि", - "shared_sortByExpiration": "समाप्ति", - "shared_search": "खोजें", - "shared_colName": "नाम", - "shared_colType": "प्रकार", - "shared_colDateShared": "साझा तिथि", - "shared_colExpiration": "समाप्ति", - "shared_colPermissions": "अनुमतियाँ", - "shared_colPassword": "पासवर्ड", - "shared_colActions": "कार्य", - "shared_emptyStateTitle": "अभी कोई साझा संसाधन नहीं", - "shared_emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", - "shared_goToFiles": "फ़ाइलों पर जाएँ", - "shared_typeFile": "फ़ाइल", - "shared_typeFolder": "फ़ोल्डर", - "shared_noExpiration": "कोई समाप्ति नहीं", - "shared_hasPassword": "हाँ", - "shared_noPassword": "नहीं", - "shared_editShare": "शेयर संपादित करें", - "shared_notifyShare": "किसी को सूचित करें", - "shared_copyLink": "लिंक कॉपी करें", - "shared_removeShare": "शेयर हटाएँ", - "shared_linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", - "shared_linkCopyFailed": "लिंक कॉपी करने में विफल", - "shared_itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", - "shared_itemRemoved": "शेयर सफलतापूर्वक हटाया गया", - "shared_invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", - "shared_notificationSent": "सूचना सफलतापूर्वक भेजी गई", - "shared_notificationFailed": "सूचना भेजने में विफल" - }, - "files": { - "name": "नाम", - "type": "प्रकार", - "size": "आकार", - "modified": "संशोधित", - "no_files": "इस फ़ोल्डर में कोई फ़ाइल नहीं", - "empty_hint": "शुरू करने के लिए ह़ैलें अपलोड करें या होल्डर बनाएँ", - "loading": "फ़ाइलें लोड हो रही हैं…", - "view_grid": "ग्रिड दृश्य", - "view_list": "सूची दृश्य", - "file_types": { - "document": "दस्तावेज़", - "image": "चित्र", - "video": "वीडियो", - "audio": "ऑडियो", - "pdf": "PDF", - "text": "टेक्स्ट", - "folder": "फ़ोल्डर", - "spreadsheet": "स्प्रेडशीट", - "presentation": "प्रेज़ेंटेशन", - "archive": "संग्रह", - "installer": "इंस्टॉलर", - "code": "कोड" - }, - "owner": "स्वामी" - }, - "dialogs": { - "rename_folder": "फ़ोल्डर का नाम बदलें", - "rename_file": "फ़ाइल का नाम बदलें", - "new_name": "नया नाम", - "new_folder_title": "नया फ़ोल्डर", - "folder_name": "फ़ोल्डर का नाम", - "folder_placeholder": "मेरा फ़ोल्डर", - "rename_title": "नाम बदलें", - "move_file": "फ़ाइल ले जाएँ", - "move_folder": "फ़ोल्डर ले जाएँ", - "select_destination": "गंतव्य फ़ोल्डर चुनें:", - "select_this_folder": "यह फ़ोल्डर चुनें", - "go_to_parent": ".. (पैरेंट फ़ोल्डर)", - "no_subfolders": "कोई सब-फ़ोल्डर नहीं", - "root": "रूट", - "delete_confirmation": "क्या आप वाकई हटाना चाहते हैं", - "and_contents": "और इसकी सभी सामग्री", - "no_undo": "यह कार्य पूर्ववत नहीं किया जा सकता", - "confirm_title": "कार्य की पुष्टि करें", - "confirm_delete": "रद्दी में भेजें", - "confirm_delete_file": "क्या आप वाकई फ़ाइल \"{{name}}\" को रद्दी में भेजना चाहते हैं?", - "confirm_delete_folder": "क्या आप वाकई फ़ोल्डर \"{{name}}\" और उसकी सभी सामग्री को रद्दी में भेजना चाहते हैं?", - "confirm_permanent_delete": "स्थायी रूप से हटाएँ", - "confirm_permanent_delete_msg": "क्या आप वाकई इस आइटम को स्थायी रूप से हटाना चाहते हैं? यह कार्य पूर्ववत नहीं किया जा सकता।", - "confirm_empty_trash": "रद्दी खाली करें", - "confirm_delete_share": "शेयर लिंक हटाएँ", - "confirm_delete_share_msg": "क्या आप वाकई इस शेयर लिंक को हटाना चाहते हैं?", - "share_file": "फ़ाइल साझा करें", - "share_folder": "फ़ोल्डर साझा करें", - "existing_shares": "मौजूदा शेयर", - "share_options": "शेयर विकल्प", - "password": "पासवर्ड", - "expiration": "समाप्ति", - "permissions": "अनुमतियाँ", - "generated_link": "जनरेट किया गया लिंक", - "notify": "सूचना भेजें", - "recipient": "प्राप्तकर्ता", - "message": "संदेश", - "move_to_home": "होम फ़ोल्डर में ले जाएं" - }, - "dropzone": { - "drag_files": "फ़ाइलें यहाँ खींचें या चुनने के लिए क्लिक करें", - "drop_files": "अपलोड करने के लिए फ़ाइलें छोड़ें" - }, - "permissions": { - "read": "पढ़ें", - "write": "लिखें", - "reshare": "पुनः साझा करें" - }, - "errors": { - "file_not_found": "फ़ाइल नहीं मिली", - "folder_not_found": "फ़ोल्डर नहीं मिला", - "delete_error": "हटाने में त्रुटि", - "upload_error": "फ़ाइल अपलोड करने में त्रुटि", - "rename_error": "नाम बदलने में त्रुटि", - "move_error": "ले जाने में त्रुटि", - "empty_name": "नाम खाली नहीं हो सकता", - "name_exists": "इस नाम की फ़ाइल या फ़ोल्डर पहले से मौजूद है", - "generic_error": "एक त्रुटि हुई है", - "group_name_invalid": "समूह का नाम ईमेल उपसर्ग प्रारूप के अनुरूप होना चाहिए (अक्षर, अंक, बिंदु, डैश, अंडरस्कोर; 1–64 वर्ण).", - "group_cycle": "यह सदस्य समूहों के बीच चक्रीय संदर्भ बनाएगा।", - "group_depth_exceeded": "यह नेस्टिंग गहराई अनुमत अधिकतम (8) से अधिक है।", - "group_virtual_immutable": "«Internal» समूह सिस्टम द्वारा प्रबंधित है और इसे संशोधित नहीं किया जा सकता।", - "group_not_found": "समूह नहीं मिला।", - "group_name_taken": "इस नाम का एक समूह पहले से मौजूद है।" - }, - "breadcrumb": { - "home": "होम" - }, - "trash": { - "empty_trash": "रद्दी खाली करें", - "empty_state": "रद्दी खाली है", - "original_location": "मूल स्थान", - "deleted_date": "हटाने की तिथि", - "remaining": "शेष", - "actions": "कार्य", - "restore": "पुनर्स्थापित करें", - "delete_permanently": "स्थायी रूप से हटाएँ", - "empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।", - "groupby": { - "remaining_days": "शेष दिन", - "trashed_time": "हटाने का समय" - } - }, - "daysRemaining": { - "expired": "समाप्त", - "today": "आज", - "tomorrow": "कल", - "inDays": "{{count}} दिन" - }, - "expiryChip": { - "never": "कभी समाप्त नहीं होता", - "expired": "समाप्त", - "today": "आज समाप्त होता है", - "tomorrow": "कल समाप्त होता है", - "inDays": "{{count}} दिनों में समाप्त होता है", - "onDate": "{{date}} को समाप्त होता है" - }, - "auth": { - "login_title": "साइन इन", - "username": "उपयोगकर्ता नाम", - "username_placeholder": "अपना उपयोगकर्ता नाम दर्ज करें", - "login_identifier": "उपयोगकर्ता नाम या ईमेल", - "login_identifier_placeholder": "अपना उपयोगकर्ता नाम या ईमेल दर्ज करें", - "password": "पासवर्ड", - "password_placeholder": "अपना पासवर्ड दर्ज करें", - "login_button": "साइन इन", - "no_account": "खाता नहीं है?", - "register": "साइन अप करें", - "admin_setup": "पहली बार?", - "setup": "एडमिन सेटअप करें", - "register_title": "खाता बनाएँ", - "email": "ईमेल", - "email_placeholder": "अपना ईमेल दर्ज करें", - "confirm_password": "पासवर्ड की पुष्टि करें", - "confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें", - "register_button": "खाता बनाएँ", - "have_account": "पहले से खाता है?", - "login": "साइन इन", - "setup_title": "प्रारंभिक सेटअप", - "setup_step1": "एडमिन", - "setup_step2": "सिस्टम", - "setup_step3": "पूर्ण", - "admin_username": "एडमिन उपयोगकर्ता नाम", - "admin_email": "एडमिन ईमेल", - "admin_password": "एडमिन पासवर्ड", - "create_admin": "एडमिन बनाएँ", - "back_to_login": "पहले से सेटअप है?", - "admin_success": "एडमिन खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", - "account_success": "खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", - "passwords_mismatch": "पासवर्ड मेल नहीं खाते", - "admin_create_error": "एडमिन खाता बनाने में त्रुटि", - "or": "या", - "sso_login": "SSO से साइन इन करें", - "sso_login_provider": "{{provider}} से साइन इन करें", - "magicLinkHint": "पासवर्ड नहीं है? अपना ईमेल दर्ज करें और हम आपको एक बार उपयोग होने वाला साइन-इन लिंक भेज देंगे।", - "magicLinkEmailLabel": "ईमेल पता", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "साइन-इन लिंक भेजें", - "magicLinkSent": "यदि उस ईमेल के लिए कोई खाता मौजूद है, तो एक साइन-इन लिंक भेज दिया गया है। अपना इनबॉक्स देखें।", - "magicLinkUnavailable": "इस सर्वर पर ईमेल द्वारा साइन-इन उपलब्ध नहीं है।", - "magicLinkNetworkError": "सर्वर से कनेक्ट नहीं हो सका: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "स्टोरेज", - "calculating": "गणना हो रही है...", - "used": "{{percentage}}% उपयोग ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "इस फ़ाइल प्रकार का पूर्वावलोकन नहीं किया जा सकता।", - "download_file": "फ़ाइल डाउनलोड करें", - "zoom_in": "ज़ूम इन", - "zoom_out": "ज़ूम आउट", - "zoom_reset": "ज़ूम रीसेट" - }, - "language_selector": { - "title": "स्वागत है!", - "subtitle": "जारी रखने के लिए अपनी भाषा चुनें", - "continue": "आगे बढ़ें", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "hi": "हिन्दी", - "ar": "العربية", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "अभी कोई पसंदीदा नहीं", - "empty_hint": "पसंदीदा में जोड़ने के लिए फ़ाइलों या फ़ोल्डर को स्टार करें", - "add": "पसंदीदा में जोड़ें", - "remove": "पसंदीदा से हटाएँ", - "added_title": "पसंदीदा में जोड़ा गया", - "added_msg": "पसंदीदा में जोड़ा गया", - "removed_title": "पसंदीदा से हटाया गया", - "removed_msg": "पसंदीदा से हटाया गया" - }, - "recent": { - "title": "हाल ही में", - "clear": "हाल ही का साफ़ करें", - "accessed": "एक्सेस किया", - "empty_state": "कोई हाल की फ़ाइलें नहीं", - "empty_hint": "जो फ़ाइलें आप खोलेंगे वे यहाँ दिखेंगी", - "loadMore": "और लोड करें" - }, - "notifications": { - "file_renamed": "फ़ाइल का नाम बदला गया", - "file_renamed_to": "फ़ाइल का नाम \"{{name}}\" रखा गया", - "folder_renamed": "फ़ोल्डर का नाम बदला गया", - "folder_renamed_to": "फ़ोल्डर का नाम \"{{name}}\" रखा गया", - "file_uploaded": "फ़ाइल अपलोड हुई", - "file_deleted": "फ़ाइल रद्दी में भेजी गई", - "folder_deleted": "फ़ोल्डर रद्दी में भेजा गया", - "item_deleted_permanently": "आइटम स्थायी रूप से हटाया गया", - "trash_emptied": "रद्दी सफलतापूर्वक खाली की गई", - "title": "सूचनाएँ", - "empty": "कोई सूचना नहीं", - "link_created": "लिंक बनाया गया", - "share_success": "शेयर लिंक सफलतापूर्वक बनाया गया", - "upload_files_section_title": "यहाँ अपलोड उपलब्ध नहीं है", - "upload_files_section_body": "फ़ाइलें अपलोड करने के लिए फ़ाइलें अनुभाग पर जाएँ" - }, - "batch": { - "one_selected": "1 आइटम चयनित", - "n_selected": "{{count}} आइटम चयनित", - "confirm_delete": "क्या आप वाकई {{count}} आइटम रद्दी में भेजना चाहते हैं?", - "move_title": "{{count}} आइटम ले जाएँ", - "add_favorites": "पसंदीदा में जोड़ें", - "move_copy": "ले जाएँ या कॉपी करें" - }, - "admin": { - "page_title": "एडमिन पैनल", - "back_to_app": "OxiCloud पर वापस", - "loading": "लोड हो रहा है…", - "access_denied": "पहुंच अस्वीकृत", - "access_denied_desc": "व्यवस्थापक विशेषाधिकार आवश्यक।", - "sign_in": "साइन इन", - "tab_dashboard": "डैशबोर्ड", - "tab_users": "उपयोगकर्ता", - "tab_oidc": "SSO / OIDC", - "total_users": "कुल उपयोगकर्ता", - "active_users": "सक्रिय उपयोगकर्ता", - "admins": "व्यवस्थापक", - "version": "संस्करण", - "storage_overview": "स्टोरेज अवलोकन", - "used": "उपयोग किया", - "total_quota": "कुल कोटा", - "usage_pct": "उपयोग %", - "users_over_80": ">80% कोटा वाले", - "users_over_quota": "कोटा से अधिक", - "system": "सिस्टम", - "auth_label": "प्रमाणीकरण", - "oidc_label": "OIDC", - "quotas_label": "कोटा", - "enabled": "सक्षम", - "disabled": "अक्षम", - "active": "सक्रिय", - "off": "बंद", - "allow_registration": "सार्वजनिक पंजीकरण की अनुमति", - "registration_warning": "सार्वजनिक पंजीकरण अक्षम है। केवल व्यवस्थापक उपयोगकर्ता बना सकते हैं।", - "user_management": "उपयोगकर्ता प्रबंधन", - "create_user": "उपयोगकर्ता बनाएं", - "col_user": "उपयोगकर्ता", - "col_role": "भूमिका", - "col_auth": "प्रमाणीकरण", - "col_status": "स्थिति", - "col_storage": "स्टोरेज", - "col_last_login": "अंतिम लॉगिन", - "col_actions": "कार्रवाई", - "loading_users": "उपयोगकर्ता लोड हो रहे हैं…", - "failed_load_users": "लोड करने में विफल", - "no_users_found": "कोई उपयोगकर्ता नहीं मिला", - "showing_users": "{{from}}-{{to}} / {{total}} दिखा रहे हैं", - "prev": "पिछला", - "next": "अगला", - "inactive": "निष्क्रिय", - "you_badge": "(आप)", - "local": "स्थानीय", - "never": "कभी नहीं", - "just_now": "अभी", - "minutes_ago": "{{n}} मिनट पहले", - "hours_ago": "{{n}} घंटे पहले", - "days_ago": "{{n}} दिन पहले", - "edit_quota_title": "कोटा संपादित करें", - "reset_password_title": "पासवर्ड रीसेट", - "toggle_role_title": "भूमिका बदलें", - "deactivate_title": "निष्क्रिय करें", - "activate_title": "सक्रिय करें", - "delete_title": "हटाएं", - "sso_title": "सिंगल साइन-ऑन (OIDC / SSO)", - "enable_sso": "SSO सक्षम करें", - "provider_name": "प्रदाता का नाम", - "issuer_url": "जारीकर्ता URL", - "issuer_url_hint": "OpenID Connect जारीकर्ता URL", - "auto_discover": "स्वतः खोज", - "discovering": "खोज रहे हैं…", - "client_id": "क्लाइंट ID", - "client_secret": "क्लाइंट सीक्रेट", - "client_secret_placeholder": "वर्तमान मान बनाए रखने के लिए खाली छोड़ें", - "secret_configured": "क्लाइंट सीक्रेट पहले से कॉन्फ़िगर है", - "callback_url": "कॉलबैक URL", - "callback_url_hint": "(अपने IdP में पंजीकृत करें)", - "advanced_settings": "उन्नत सेटिंग्स", - "scopes": "स्कोप", - "auto_provision": "पहले लॉगिन पर स्वतः प्रावधान", - "admin_groups": "व्यवस्थापक समूह", - "admin_groups_hint": "अल्पविराम-पृथक OIDC समूह नाम", - "disable_password": "पासवर्ड लॉगिन अक्षम (केवल OIDC)", - "password_warning": "सभी पासवर्ड लॉगिन रुक जाएंगे!", - "test_btn": "परीक्षण", - "save_btn": "सहेजें", - "saving": "सहेज रहे हैं…", - "settings_saved": "सेटिंग्स सहेजी गईं — OIDC अब {{status}}", - "quota_modal_title": "स्टोरेज कोटा अपडेट", - "quota_user_label": "उपयोगकर्ता:", - "new_quota": "नया कोटा", - "quota_unlimited_hint": "असीमित के लिए 0", - "cancel": "रद्द करें", - "create_user_title": "नया उपयोगकर्ता बनाएं", - "username_label": "उपयोगकर्ता नाम", - "username_placeholder": "username", - "username_hint": "3–32 अक्षर", - "password_label": "पासवर्ड", - "password_placeholder": "न्यूनतम 8 अक्षर", - "email_label": "ईमेल", - "email_optional": "(वैकल्पिक)", - "email_placeholder": "user@example.com (खाली होने पर स्वतः)", - "role_label": "भूमिका", - "role_user": "उपयोगकर्ता", - "role_admin": "व्यवस्थापक", - "quota_label": "कोटा", - "creating": "बना रहे हैं…", - "reset_pw_title": "पासवर्ड रीसेट", - "new_password_label": "नया पासवर्ड", - "resetting": "रीसेट हो रहा है…", - "reset_btn": "रीसेट", - "confirm_role_change": "भूमिका {{role}} में बदलें?", - "confirm_deactivate": "इस उपयोगकर्ता को निष्क्रिय करें?", - "confirm_activate": "इस उपयोगकर्ता को सक्रिय करें?", - "confirm_delete_user": "उपयोगकर्ता \"{{name}}\" हटाएं? पूर्ववत नहीं होगा!", - "confirm_action": "कार्रवाई की पुष्टि", - "confirm_yes": "पुष्टि", - "confirm_no": "रद्द", - "error_username_short": "नाम कम से कम 3 अक्षर", - "error_password_short": "पासवर्ड कम से कम 8 अक्षर", - "error_generic": "विफल", - "error_network": "नेटवर्क त्रुटि: {{message}}", - "error_create_user": "उपयोगकर्ता बनाने में विफल", - "tab_storage": "स्टोरेज", - "storage_title": "स्टोरेज कॉन्फ़िगरेशन", - "storage_current_backend": "वर्तमान बैकएंड", - "storage_total_blobs": "कुल ब्लॉब्स", - "storage_total_size": "कुल आकार", - "storage_dedup_ratio": "डीडुप्लिकेशन अनुपात", - "storage_backend": "बैकएंड", - "storage_local": "स्थानीय", - "storage_s3": "S3 संगत", - "storage_provider_preset": "प्रदाता प्रीसेट", - "storage_preset_custom": "कस्टम", - "storage_endpoint_url": "एंडपॉइंट URL", - "storage_endpoint_hint": "AWS S3 के लिए खाली छोड़ें", - "storage_bucket": "बकेट", - "storage_region": "क्षेत्र", - "storage_access_key": "एक्सेस की", - "storage_secret_key": "सीक्रेट की", - "storage_secret_configured": "की कॉन्फ़िगर की गई", - "storage_key_placeholder": "नई की दर्ज करें", - "storage_path_style": "पाथ स्टाइल फ़ोर्स करें", - "storage_path_style_hint": "MinIO और कुछ S3-संगत सेवाओं के लिए आवश्यक", - "storage_test_connection": "कनेक्शन परीक्षण", - "storage_test_success": "कनेक्शन सफल", - "storage_test_failure": "कनेक्शन विफल", - "storage_save": "कॉन्फ़िगरेशन सहेजें", - "storage_saved": "कॉन्फ़िगरेशन सहेजी गई", - "storage_migration": "डेटा माइग्रेशन", - "storage_migration_coming_soon": "माइग्रेशन टूल्स जल्द आ रहे हैं", - "migration_status_label": "माइग्रेशन स्थिति", - "migration_start": "माइग्रेशन शुरू करें", - "migration_pause": "रोकें", - "migration_resume": "फिर से शुरू करें", - "migration_verify": "सत्यापित करें", - "migration_complete": "पूर्ण करें", - "migration_started": "माइग्रेशन शुरू हुआ", - "migration_paused_msg": "माइग्रेशन रोका गया", - "migration_resumed_msg": "माइग्रेशन फिर से शुरू हुआ", - "migration_completed_msg": "माइग्रेशन सफलतापूर्वक पूर्ण हुआ", - "migration_verifying": "सत्यापन हो रहा है...", - "migration_verify_passed": "सत्यापन पास", - "migration_verify_failed": "सत्यापन विफल", - "migration_failed_blobs": "विफल ब्लॉब्स", - "testing": "परीक्षण हो रहा है...", - "smtp_disabled": "अक्षम (होस्ट सेट नहीं)", - "smtp_enabled": "सक्षम", - "smtp_enabled_label": "स्थिति", - "smtp_intro": "SMTP केवल पर्यावरण चर (OXICLOUD_SMTP_*) के माध्यम से कॉन्फ़िगर किया जाता है। नीचे दिए गए मान चल रहे सर्वर से पढ़े जाते हैं — उन्हें बदलने के लिए, पर्यावरण संपादित करें और OxiCloud को पुनः आरंभ करें।", - "smtp_not_configured": "इस सर्वर पर SMTP कॉन्फ़िगर नहीं है।", - "smtp_send_failed": "भेजना विफल।", - "smtp_send_test": "परीक्षण ईमेल भेजें", - "smtp_sending": "भेजा जा रहा है…", - "smtp_sent": "परीक्षण ईमेल भेजा गया।", - "smtp_server_code": "सर्वर का उत्तर", - "smtp_test_intro": "नीचे दिए गए प्राप्तकर्ता को एक पूर्व-निर्धारित निदान संदेश भेजता है और SMTP सर्वर का उत्तर रिपोर्ट करता है ताकि आप इसे अपने रिले लॉग्स से मिला सकें।", - "smtp_test_missing_to": "प्राप्तकर्ता पता दर्ज करें।", - "smtp_test_title": "परीक्षण ईमेल भेजें", - "smtp_test_to": "प्राप्तकर्ता का पता", - "smtp_title": "जावक ईमेल (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "प्रोफ़ाइल", - "back_to_app": "OxiCloud पर वापस", - "loading": "लोड हो रहा है…", - "not_authenticated": "प्रमाणित नहीं", - "not_authenticated_desc": "अपना प्रोफ़ाइल देखने के लिए साइन इन करें।", - "sign_in": "साइन इन", - "role_admin": "व्यवस्थापक", - "role_user": "उपयोगकर्ता", - "account_details": "खाता विवरण", - "username": "उपयोगकर्ता नाम", - "email": "ईमेल", - "role": "भूमिका", - "last_login": "अंतिम लॉगिन", - "storage": "स्टोरेज", - "used": "उपयोग किया", - "quota": "कोटा", - "usage": "उपयोग", - "unlimited": "असीमित", - "app_passwords": "ऐप पासवर्ड", - "app_pw_desc": "WebDAV, CalDAV और CardDAV क्लाइंट के लिए पासवर्ड जनरेट करें। प्रत्येक पासवर्ड केवल एक बार दिखाया जाता है।", - "app_pw_label_placeholder": "लेबल (जैसे Thunderbird, macOS)", - "generate": "जनरेट करें", - "generating": "जनरेट हो रहा है…", - "new_password_for": "नया पासवर्ड", - "copy_warning": "इस पासवर्ड को अभी कॉपी करें। आप इसे दोबारा नहीं देख पाएंगे।", - "copy_to_clipboard": "क्लिपबोर्ड पर कॉपी करें", - "col_label": "लेबल", - "col_created": "बनाया गया", - "col_last_used": "अंतिम उपयोग", - "col_status": "स्थिति", - "active": "सक्रिय", - "revoked": "रद्द", - "revoke_title": "रद्द करें", - "no_app_passwords": "अभी तक कोई ऐप पासवर्ड नहीं।", - "client_sessions": "क्लाइंट सत्र", - "client_sessions_desc": "Nextcloud-संगत क्लाइंट कनेक्ट करने पर स्वतः जनरेट।", - "col_client": "क्लाइंट", - "never": "कभी नहीं", - "just_now": "अभी", - "minutes_ago": "{{n}} मिनट पहले", - "hours_ago": "{{n}} घंटे पहले", - "days_ago": "{{n}} दिन पहले", - "edit_profile": "प्रोफ़ाइल संपादित करें", - "edit_oidc_managed": "अपनी जानकारी (नाम, प्रथम नाम, प्रोफ़ाइल चित्र, …) बदलने के लिए, कृपया अपने पहचान प्रदाता पर इसे अद्यतन करें। आपके परिवर्तन अगले साइन-इन पर दिखाई देंगे।", - "username_claim_hint": "2–64 अक्षर, अक्षर / अंक / डॉट / डैश / अंडरस्कोर। एक बार चुनने के बाद, उपयोगकर्ता नाम नहीं बदला जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", - "username_already_claimed": "उपयोगकर्ता नाम सेट है और बदला नहीं जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", - "given_name": "प्रथम नाम", - "family_name": "अंतिम नाम", - "notify_on_share": "जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें", - "notify_on_share_hint": "जब अनचेक किया जाए, तो साझाकरण आपके खाते में दिखाई देते रहेंगे — आपको बस उनके बारे में ईमेल नहीं मिलेगा।", - "save_profile": "परिवर्तन सहेजें", - "profile_saved": "प्रोफ़ाइल अद्यतन की गई", - "profile_no_changes": "सहेजने के लिए कोई परिवर्तन नहीं।", - "profile_save_failed": "सहेजना विफल", - "username_taken_error": "यह उपयोगकर्ता नाम पहले से उपयोग में है।", - "username_immutable_error": "आपका उपयोगकर्ता नाम पहले से सेट है और यहाँ नहीं बदला जा सकता। यदि आपको नाम बदलने की आवश्यकता है तो किसी व्यवस्थापक से संपर्क करें।", - "change_password": "पासवर्ड बदलें", - "current_password": "वर्तमान पासवर्ड", - "new_password": "नया पासवर्ड", - "min_8_chars": "कम से कम 8 अक्षर", - "confirm_password": "नया पासवर्ड पुष्टि करें", - "update_password": "पासवर्ड अपडेट करें", - "updating": "अपडेट हो रहा है…", - "password_updated": "पासवर्ड सफलतापूर्वक अपडेट हुआ", - "passwords_no_match": "पासवर्ड मेल नहीं खाते", - "password_too_short": "पासवर्ड कम से कम 8 अक्षर का होना चाहिए", - "password_change_failed": "पासवर्ड बदलने में विफल", - "error_network": "नेटवर्क त्रुटि: {{message}}", - "error_label_required": "कृपया एक लेबल दर्ज करें", - "error_create_pw": "ऐप पासवर्ड बनाने में विफल", - "confirm_revoke": "ऐप पासवर्ड \"{{label}}\" रद्द करें? इसका उपयोग करने वाले क्लाइंट काम करना बंद कर देंगे।", - "error_revoke": "रद्द करने में विफल", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "अपलोड हो रहा है...", - "files": "फ़ाइलें", - "complete": "{{count}} / {{total}} अपलोड हुए" - }, - "storage_quota_exceeded": "स्टोरेज कोटा पार हो गया", - "sharedwithme": { - "pageTitle": "मेरे साथ साझा किया", - "pageDescription": "फ़ाइलें और फ़ोल्डर जो अन्य उपयोगकर्ताओं ने आपके साथ साझा किए हैं", - "emptyStateTitle": "अभी तक आपके साथ कुछ भी साझा नहीं किया गया", - "emptyStateDesc": "अन्य उपयोगकर्ताओं द्वारा आपके साथ साझा किए गए आइटम यहाँ दिखाई देंगे", - "loadMore": "और लोड करें", - "sharedBy": "द्वारा साझा किया", - "colName": "नाम", - "colType": "प्रकार", - "colSharedBy": "द्वारा साझा किया", - "colDate": "साझाकरण तिथि", - "colPermissions": "अनुमतियाँ" - }, - "groupby": { - "none": "कोई नहीं", - "title": "इसके अनुसार समूहीकृत करें", - "owner": "स्वामी", - "shareDate": "साझा तिथि", - "type": "प्रकार", - "type.folders": "फ़ोल्डर", - "accessedAt": "पहुँच की तारीख", - "modifiedAt": "संशोधन की तारीख", - "createdAt": "बनाने की तारीख", - "size": "आकार", - "favoriteDate": "पसंदीदा की तारीख", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "नया" - }, - "dateBucket": { - "today": "आज", - "last7days": "पिछले 7 दिन", - "last30days": "पिछले 30 दिन" - }, - "groups": { - "title": "समूह प्रबंधित करें", - "create_button": "समूह बनाएँ", - "create_dialog_title": "नया समूह", - "edit_dialog_title": "समूह का नाम बदलें", - "name_label": "नाम", - "name_placeholder": "engineering", - "description_label": "विवरण (वैकल्पिक)", - "members_section": "सदस्य", - "add_member_placeholder": "उपयोगकर्ता या समूह जोड़ें…", - "no_members": "अभी तक कोई सदस्य नहीं।", - "remove_member": "हटाएँ", - "delete_group": "समूह हटाएँ", - "delete_confirm": "समूह \"{name}\" को हटाएँ? इस समूह से जुड़ी अनुमतियाँ रद्द कर दी जाएँगी।", - "empty_state": "अभी तक कोई समूह नहीं।", - "load_more": "और लोड करें", - "back_to_list": "वापस", - "loading": "लोड हो रहा है…", - "virtual_badge": "सिस्टम", - "member_count_zero": "कोई सदस्य नहीं", - "member_count_one": "1 सदस्य", - "member_count_other": "{count} सदस्य", - "delete_confirm_label": "पुष्टि के लिए समूह का नाम लिखें:", - "delete_confirm_mismatch": "पुष्टि के लिए समूह का नाम बिल्कुल वैसा ही लिखें।", - "virtual_internal_name": "आंतरिक", - "members_loading": "सदस्य लोड हो रहे हैं…", - "members_empty": "कोई सदस्य नहीं", - "virtual_internal_explanation": "इस सर्वर पर हर आंतरिक उपयोगकर्ता" - }, - "myshares": { - "copyLink": "लिंक कॉपी करें", - "deleteLink": "लिंक हटाएँ", - "notifyByEmail": "ईमेल से सूचित करें", - "notifyFailed": "सूचना नहीं भेजी जा सकी।", - "notifyGroupMembers": "समूह के सदस्यों को सूचित करें", - "notifyRateLimited": "इस प्राप्तकर्ता के लिए बहुत अधिक सूचनाएँ — बाद में पुनः प्रयास करें।", - "removeAccess": "पहुँच हटाएँ", - "resendInvitation": "आमंत्रण ईमेल पुनः भेजें" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", + "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nअपना नया साझाकरण देखने के लिए OxiCloud खोलें:\n{{login_link}}\n\nहो सकता है आपके पास {{inviter}} से और भी नए साझाकरण हों — साइन इन करें और अपने सभी साझा किए गए आइटम देखें।\n\n— OxiCloud\n\nआपको यह संदेश इसलिए मिल रहा है क्योंकि आपका OxiCloud खाता है और साझाकरण-सूचना प्राथमिकता चालू है। आप इसे अपनी प्रोफ़ाइल में बंद कर सकते हैं (जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें)।" + } } + }, + "app": { + "title": "OxiCloud", + "description": "न्यूनतम क्लाउड स्टोरेज सिस्टम" + }, + "nav": { + "files": "फ़ाइलें", + "shared": "साझा", + "recent": "हाल ही में", + "favorites": "पसंदीदा", + "photos": "फ़ोटो", + "music": "संगीत", + "trash": "रद्दी", + "sharedwithme": "मेरे साथ साझा किए गए", + "profile": "प्रोफ़ाइल", + "shared_with_me": "मेरे साथ साझा किए गए" + }, + "photos": { + "empty_state": "अभी कोई फ़ोटो नहीं", + "empty_hint": "यहाँ देखने के लिए चित्र या वीडियो अपलोड करें", + "items_selected": "चयनित", + "view_daily": "दिन", + "view_monthly": "महीना", + "view_yearly": "वर्ष", + "group_by": "इसके अनुसार समूहीकृत करें" + }, + "music": { + "create_playlist": "प्लेलिस्ट बनाएँ", + "playlists": "प्लेलिस्ट", + "no_playlists": "अभी कोई प्लेलिस्ट नहीं", + "select_playlist": "प्लेलिस्ट चुनें", + "select_hint": "साइडबार से प्लेलिस्ट चुनें या नई बनाएँ", + "add_tracks": "ट्रैक जोड़ें", + "no_tracks": "इस प्लेलिस्ट में कोई ट्रैक नहीं", + "unknown_artist": "अज्ञात कलाकार", + "unknown_title": "अज्ञात", + "confirm_delete": "इस प्लेलिस्ट को हटाएँ?", + "playlist_name": "प्लेलिस्ट का नाम", + "create": "बनाएँ", + "delete": "हटाएँ", + "share": "साझा करें", + "edit": "संपादित करें", + "play_all": "सभी चलाएँ", + "shuffle": "शफल", + "repeat": "दोहराएँ", + "repeat_one": "एक दोहराएँ", + "queue": "कतार", + "queue_empty": "कतार खाली है", + "not_playing": "नहीं चल रहा", + "play": "चलाएँ", + "pause": "रोकें", + "previous": "पिछला", + "next": "अगला", + "volume": "आवाज़", + "mute": "म्यूट", + "unmute": "अनम्यूट", + "title": "शीर्षक", + "artist": "कलाकार", + "album": "एल्बम", + "tracks": "ट्रैक", + "add": "जोड़ें", + "added": "जोड़ा गया!", + "added_to_playlist": "प्लेलिस्ट में जोड़ा गया", + "add_to_playlist": "प्लेलिस्ट में जोड़ें", + "load_error": "प्लेलिस्ट लोड करने में त्रुटि", + "add_error": "प्लेलिस्ट में ट्रैक नहीं जोड़े जा सके", + "no_playlists_yet": "अभी तक कोई प्लेलिस्ट नहीं। पहले एक बनाएं!", + "selected_files": "चयनित:", + "error": "त्रुटि", + "search_audio": "ऑडियो फ़ाइलें खोजें…", + "no_audio_files": "कोई ऑडियो फ़ाइल नहीं मिली", + "selected": "चयनित", + "loading": "लोड हो रहा है…", + "search_error": "ऑडियो फ़ाइलें लोड नहीं हो सकीं", + "adding": "जोड़ा जा रहा है…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "पिछला" + }, + "actions": { + "search": "फ़ाइलें खोजें...", + "new_folder": "नया फ़ोल्डर", + "upload": "अपलोड", + "upload_files": "फ़ाइलें अपलोड करें", + "upload_folder": "फ़ोल्डर अपलोड करें", + "upload.uploading": "अपलोड हो रहा है...", + "upload.complete": "{count} / {total} अपलोड हुईं", + "upload.files": "फ़ाइलें", + "rename": "नाम बदलें", + "move": "यहाँ ले जाएँ...", + "move_to": "यहाँ ले जाएँ", + "delete": "हटाएँ", + "download": "डाउनलोड", + "view": "देखें", + "cancel": "रद्द करें", + "confirm": "पुष्टि करें", + "share": "साझा करें", + "favorite": "पसंदीदा में जोड़ें", + "unfavorite": "पसंदीदा से हटाएँ", + "copy": "कॉपी करें", + "notify": "सूचित करें", + "send": "भेजें", + "clear_recent": "हाल ही का साफ़ करें", + "logout": "लॉग आउट", + "create": "बनाएँ", + "search_btn": "खोजें", + "close": "बंद करें", + "delete_permanently": "स्थायी रूप से हटाएँ", + "empty_trash": "रद्दी खाली करें", + "open_parent_folder": "मूल फ़ोल्डर पर जाएं", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "दिखावट", + "about": "OxiCloud के बारे में", + "about_description": "Rust और Clean Architecture से बना क्लाउड स्टोरेज प्लेटफ़ॉर्म। तेज़, सुरक्षित और निजी।", + "admin_panel": "एडमिन पैनल", + "profile": "मेरी प्रोफ़ाइल", + "role_user": "उपयोगकर्ता", + "theme": { + "light": "हल्का", + "dark": "गहरा", + "auto": "सिस्टम जैसा" + }, + "manage_groups": "समूह प्रबंधित करें", + "admin": "एडमिन" + }, + "share": { + "dialogTitle": "शेयर लिंक", + "linkLabel": "शेयर लिंक:", + "copyLink": "कॉपी", + "permissions": "अनुमतियाँ:", + "permissionRead": "पढ़ें", + "permissionWrite": "लिखें", + "permissionReshare": "पुनः साझा करें", + "password": "पासवर्ड सुरक्षा:", + "generatePassword": "जनरेट करें", + "expiration": "समाप्ति तिथि:", + "update": "शेयर अपडेट करें", + "remove": "शेयर हटाएँ", + "notifyTitle": "सूचना भेजें", + "notifyEmailLabel": "ईमेल पता:", + "notifyMessageLabel": "संदेश (वैकल्पिक):", + "notifySend": "सूचना भेजें", + "shareWithOthers": "दूसरों के साथ साझा करें", + "sharePublicly": "सार्वजनिक रूप से साझा करें", + "shareSettings": "साझा सेटिंग्स", + "shareCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ", + "shareCreated": "शेयर लिंक सफलतापूर्वक बनाया गया", + "shareUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", + "shareRemoved": "शेयर सफलतापूर्वक हटाया गया", + "inviteByEmail": "ईमेल द्वारा आमंत्रित करें — आमंत्रण भेजा जाएगा", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "कॉपी", + "copy_failed": "Could not copy link", + "download": "डाउनलोड", + "files": "फ़ाइलें", + "folders": "फ़ोल्डर", + "link_name": "Link name (optional)", + "notifyByEmail": "ईमेल से सूचित करें", + "revoke": "Remove", + "role_label": "भूमिका" + }, + "share_dialogTitle": "शेयर लिंक", + "share_linkLabel": "शेयर लिंक:", + "share_copyLink": "कॉपी", + "share_permissions": "अनुमतियाँ:", + "share_permissionRead": "पढ़ें", + "share_permissionWrite": "लिखें", + "share_permissionReshare": "पुनः साझा करें", + "share_password": "पासवर्ड सुरक्षा:", + "share_generatePassword": "जनरेट करें", + "share_expiration": "समाप्ति तिथि:", + "share_update": "शेयर अपडेट करें", + "share_remove": "शेयर हटाएँ", + "share_notifyTitle": "सूचना भेजें", + "share_notifyEmailLabel": "ईमेल पता:", + "share_notifyMessageLabel": "संदेश (वैकल्पिक):", + "share_notifySend": "सूचना भेजें", + "shared": { + "backToFiles": "फ़ाइलों पर वापस", + "pageTitle": "साझा संसाधन", + "pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", + "filterType": "प्रकार:", + "filterAll": "सभी", + "filterFiles": "फ़ाइलें", + "filterFolders": "फ़ोल्डर", + "sortBy": "क्रमबद्ध:", + "sortByName": "नाम", + "sortByDate": "साझा तिथि", + "sortByExpiration": "समाप्ति", + "search": "खोजें", + "colName": "नाम", + "colType": "प्रकार", + "colDateShared": "साझा तिथि", + "colExpiration": "समाप्ति", + "colPermissions": "अनुमतियाँ", + "colPassword": "पासवर्ड", + "colActions": "कार्य", + "emptyStateTitle": "अभी कोई साझा संसाधन नहीं", + "emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", + "goToFiles": "फ़ाइलों पर जाएँ", + "typeFile": "फ़ाइल", + "typeFolder": "फ़ोल्डर", + "noExpiration": "कोई समाप्ति नहीं", + "hasPassword": "हाँ", + "noPassword": "नहीं", + "editShare": "शेयर संपादित करें", + "notifyShare": "किसी को सूचित करें", + "copyLink": "लिंक कॉपी करें", + "removeShare": "शेयर हटाएँ", + "linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", + "linkCopyFailed": "लिंक कॉपी करने में विफल", + "itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", + "itemRemoved": "शेयर सफलतापूर्वक हटाया गया", + "invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", + "notificationSent": "सूचना सफलतापूर्वक भेजी गई", + "notificationFailed": "सूचना भेजने में विफल", + "shared_backToFiles": "फ़ाइलों पर वापस", + "shared_pageTitle": "साझा संसाधन", + "shared_pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", + "shared_filterType": "प्रकार:", + "shared_filterAll": "सभी", + "shared_filterFiles": "फ़ाइलें", + "shared_filterFolders": "फ़ोल्डर", + "shared_sortBy": "क्रमबद्ध:", + "shared_sortByName": "नाम", + "shared_sortByDate": "साझा तिथि", + "shared_sortByExpiration": "समाप्ति", + "shared_search": "खोजें", + "shared_colName": "नाम", + "shared_colType": "प्रकार", + "shared_colDateShared": "साझा तिथि", + "shared_colExpiration": "समाप्ति", + "shared_colPermissions": "अनुमतियाँ", + "shared_colPassword": "पासवर्ड", + "shared_colActions": "कार्य", + "shared_emptyStateTitle": "अभी कोई साझा संसाधन नहीं", + "shared_emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", + "shared_goToFiles": "फ़ाइलों पर जाएँ", + "shared_typeFile": "फ़ाइल", + "shared_typeFolder": "फ़ोल्डर", + "shared_noExpiration": "कोई समाप्ति नहीं", + "shared_hasPassword": "हाँ", + "shared_noPassword": "नहीं", + "shared_editShare": "शेयर संपादित करें", + "shared_notifyShare": "किसी को सूचित करें", + "shared_copyLink": "लिंक कॉपी करें", + "shared_removeShare": "शेयर हटाएँ", + "shared_linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", + "shared_linkCopyFailed": "लिंक कॉपी करने में विफल", + "shared_itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", + "shared_itemRemoved": "शेयर सफलतापूर्वक हटाया गया", + "shared_invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", + "shared_notificationSent": "सूचना सफलतापूर्वक भेजी गई", + "shared_notificationFailed": "सूचना भेजने में विफल" + }, + "files": { + "name": "नाम", + "type": "प्रकार", + "size": "आकार", + "modified": "संशोधित", + "no_files": "इस फ़ोल्डर में कोई फ़ाइल नहीं", + "empty_hint": "शुरू करने के लिए ह़ैलें अपलोड करें या होल्डर बनाएँ", + "loading": "फ़ाइलें लोड हो रही हैं…", + "view_grid": "ग्रिड दृश्य", + "view_list": "सूची दृश्य", + "file_types": { + "document": "दस्तावेज़", + "image": "चित्र", + "video": "वीडियो", + "audio": "ऑडियो", + "pdf": "PDF", + "text": "टेक्स्ट", + "folder": "फ़ोल्डर", + "spreadsheet": "स्प्रेडशीट", + "presentation": "प्रेज़ेंटेशन", + "archive": "संग्रह", + "installer": "इंस्टॉलर", + "code": "कोड" + }, + "owner": "स्वामी", + "add_favorites": "पसंदीदा में जोड़ें", + "added_favorites": "पसंदीदा में जोड़ा गया", + "col_name": "नाम", + "col_owner": "स्वामी", + "col_size": "आकार", + "col_type": "प्रकार", + "copy": "कॉपी करें", + "edit": "संपादित करें", + "file": "फ़ाइल", + "folder": "फ़ोल्डर", + "new_folder": "नया फ़ोल्डर", + "share": "साझा करें", + "view": "देखें" + }, + "dialogs": { + "rename_folder": "फ़ोल्डर का नाम बदलें", + "rename_file": "फ़ाइल का नाम बदलें", + "new_name": "नया नाम", + "new_folder_title": "नया फ़ोल्डर", + "folder_name": "फ़ोल्डर का नाम", + "folder_placeholder": "मेरा फ़ोल्डर", + "rename_title": "नाम बदलें", + "move_file": "फ़ाइल ले जाएँ", + "move_folder": "फ़ोल्डर ले जाएँ", + "select_destination": "गंतव्य फ़ोल्डर चुनें:", + "select_this_folder": "यह फ़ोल्डर चुनें", + "go_to_parent": ".. (पैरेंट फ़ोल्डर)", + "no_subfolders": "कोई सब-फ़ोल्डर नहीं", + "root": "रूट", + "delete_confirmation": "क्या आप वाकई हटाना चाहते हैं", + "and_contents": "और इसकी सभी सामग्री", + "no_undo": "यह कार्य पूर्ववत नहीं किया जा सकता", + "confirm_title": "कार्य की पुष्टि करें", + "confirm_delete": "रद्दी में भेजें", + "confirm_delete_file": "क्या आप वाकई फ़ाइल \"{{name}}\" को रद्दी में भेजना चाहते हैं?", + "confirm_delete_folder": "क्या आप वाकई फ़ोल्डर \"{{name}}\" और उसकी सभी सामग्री को रद्दी में भेजना चाहते हैं?", + "confirm_permanent_delete": "स्थायी रूप से हटाएँ", + "confirm_permanent_delete_msg": "क्या आप वाकई इस आइटम को स्थायी रूप से हटाना चाहते हैं? यह कार्य पूर्ववत नहीं किया जा सकता।", + "confirm_empty_trash": "रद्दी खाली करें", + "confirm_delete_share": "शेयर लिंक हटाएँ", + "confirm_delete_share_msg": "क्या आप वाकई इस शेयर लिंक को हटाना चाहते हैं?", + "share_file": "फ़ाइल साझा करें", + "share_folder": "फ़ोल्डर साझा करें", + "existing_shares": "मौजूदा शेयर", + "share_options": "शेयर विकल्प", + "password": "पासवर्ड", + "expiration": "समाप्ति", + "permissions": "अनुमतियाँ", + "generated_link": "जनरेट किया गया लिंक", + "notify": "सूचना भेजें", + "recipient": "प्राप्तकर्ता", + "message": "संदेश", + "move_to_home": "होम फ़ोल्डर में ले जाएं" + }, + "dropzone": { + "drag_files": "फ़ाइलें यहाँ खींचें या चुनने के लिए क्लिक करें", + "drop_files": "अपलोड करने के लिए फ़ाइलें छोड़ें" + }, + "permissions": { + "read": "पढ़ें", + "write": "लिखें", + "reshare": "पुनः साझा करें" + }, + "errors": { + "file_not_found": "फ़ाइल नहीं मिली", + "folder_not_found": "फ़ोल्डर नहीं मिला", + "delete_error": "हटाने में त्रुटि", + "upload_error": "फ़ाइल अपलोड करने में त्रुटि", + "rename_error": "नाम बदलने में त्रुटि", + "move_error": "ले जाने में त्रुटि", + "empty_name": "नाम खाली नहीं हो सकता", + "name_exists": "इस नाम की फ़ाइल या फ़ोल्डर पहले से मौजूद है", + "generic_error": "एक त्रुटि हुई है", + "group_name_invalid": "समूह का नाम ईमेल उपसर्ग प्रारूप के अनुरूप होना चाहिए (अक्षर, अंक, बिंदु, डैश, अंडरस्कोर; 1–64 वर्ण).", + "group_cycle": "यह सदस्य समूहों के बीच चक्रीय संदर्भ बनाएगा।", + "group_depth_exceeded": "यह नेस्टिंग गहराई अनुमत अधिकतम (8) से अधिक है।", + "group_virtual_immutable": "«Internal» समूह सिस्टम द्वारा प्रबंधित है और इसे संशोधित नहीं किया जा सकता।", + "group_not_found": "समूह नहीं मिला।", + "group_name_taken": "इस नाम का एक समूह पहले से मौजूद है।" + }, + "breadcrumb": { + "home": "होम" + }, + "trash": { + "empty_trash": "रद्दी खाली करें", + "empty_state": "रद्दी खाली है", + "original_location": "मूल स्थान", + "deleted_date": "हटाने की तिथि", + "remaining": "शेष", + "actions": "कार्य", + "restore": "पुनर्स्थापित करें", + "delete_permanently": "स्थायी रूप से हटाएँ", + "empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।", + "groupby": { + "remaining_days": "शेष दिन", + "trashed_time": "हटाने का समय" + }, + "delete": "स्थायी रूप से हटाएँ", + "empty_action": "रद्दी खाली करें" + }, + "daysRemaining": { + "expired": "समाप्त", + "today": "आज", + "tomorrow": "कल", + "inDays": "{{count}} दिन" + }, + "expiryChip": { + "never": "कभी समाप्त नहीं होता", + "expired": "समाप्त", + "today": "आज समाप्त होता है", + "tomorrow": "कल समाप्त होता है", + "inDays": "{{count}} दिनों में समाप्त होता है", + "onDate": "{{date}} को समाप्त होता है" + }, + "auth": { + "login_title": "साइन इन", + "username": "उपयोगकर्ता नाम", + "username_placeholder": "अपना उपयोगकर्ता नाम दर्ज करें", + "login_identifier": "उपयोगकर्ता नाम या ईमेल", + "login_identifier_placeholder": "अपना उपयोगकर्ता नाम या ईमेल दर्ज करें", + "password": "पासवर्ड", + "password_placeholder": "अपना पासवर्ड दर्ज करें", + "login_button": "साइन इन", + "no_account": "खाता नहीं है?", + "register": "साइन अप करें", + "admin_setup": "पहली बार?", + "setup": "एडमिन सेटअप करें", + "register_title": "खाता बनाएँ", + "email": "ईमेल", + "email_placeholder": "अपना ईमेल दर्ज करें", + "confirm_password": "पासवर्ड की पुष्टि करें", + "confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें", + "register_button": "खाता बनाएँ", + "have_account": "पहले से खाता है?", + "login": "साइन इन", + "setup_title": "प्रारंभिक सेटअप", + "setup_step1": "एडमिन", + "setup_step2": "सिस्टम", + "setup_step3": "पूर्ण", + "admin_username": "एडमिन उपयोगकर्ता नाम", + "admin_email": "एडमिन ईमेल", + "admin_password": "एडमिन पासवर्ड", + "create_admin": "एडमिन बनाएँ", + "back_to_login": "पहले से सेटअप है?", + "admin_success": "एडमिन खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", + "account_success": "खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", + "passwords_mismatch": "पासवर्ड मेल नहीं खाते", + "admin_create_error": "एडमिन खाता बनाने में त्रुटि", + "or": "या", + "sso_login": "SSO से साइन इन करें", + "sso_login_provider": "{{provider}} से साइन इन करें", + "magicLinkHint": "पासवर्ड नहीं है? अपना ईमेल दर्ज करें और हम आपको एक बार उपयोग होने वाला साइन-इन लिंक भेज देंगे।", + "magicLinkEmailLabel": "ईमेल पता", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "साइन-इन लिंक भेजें", + "magicLinkSent": "यदि उस ईमेल के लिए कोई खाता मौजूद है, तो एक साइन-इन लिंक भेज दिया गया है। अपना इनबॉक्स देखें।", + "magicLinkUnavailable": "इस सर्वर पर ईमेल द्वारा साइन-इन उपलब्ध नहीं है।", + "magicLinkNetworkError": "सर्वर से कनेक्ट नहीं हो सका: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "ईमेल पता", + "magic_hint": "पासवर्ड नहीं है? अपना ईमेल दर्ज करें और हम आपको एक बार उपयोग होने वाला साइन-इन लिंक भेज देंगे।", + "magic_unavailable": "इस सर्वर पर ईमेल द्वारा साइन-इन उपलब्ध नहीं है।", + "passwords_match": "Passwords match", + "sign_in": "साइन इन" + }, + "storage": { + "title": "स्टोरेज", + "calculating": "गणना हो रही है...", + "used": "{{percentage}}% उपयोग ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "इस फ़ाइल प्रकार का पूर्वावलोकन नहीं किया जा सकता।", + "download_file": "फ़ाइल डाउनलोड करें", + "zoom_in": "ज़ूम इन", + "zoom_out": "ज़ूम आउट", + "zoom_reset": "ज़ूम रीसेट" + }, + "language_selector": { + "title": "स्वागत है!", + "subtitle": "जारी रखने के लिए अपनी भाषा चुनें", + "continue": "आगे बढ़ें", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "hi": "हिन्दी", + "ar": "العربية", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "अभी कोई पसंदीदा नहीं", + "empty_hint": "पसंदीदा में जोड़ने के लिए फ़ाइलों या फ़ोल्डर को स्टार करें", + "add": "पसंदीदा में जोड़ें", + "remove": "पसंदीदा से हटाएँ", + "added_title": "पसंदीदा में जोड़ा गया", + "added_msg": "पसंदीदा में जोड़ा गया", + "removed_title": "पसंदीदा से हटाया गया", + "removed_msg": "पसंदीदा से हटाया गया" + }, + "recent": { + "title": "हाल ही में", + "clear": "हाल ही का साफ़ करें", + "accessed": "एक्सेस किया", + "empty_state": "कोई हाल की फ़ाइलें नहीं", + "empty_hint": "जो फ़ाइलें आप खोलेंगे वे यहाँ दिखेंगी", + "loadMore": "और लोड करें" + }, + "notifications": { + "file_renamed": "फ़ाइल का नाम बदला गया", + "file_renamed_to": "फ़ाइल का नाम \"{{name}}\" रखा गया", + "folder_renamed": "फ़ोल्डर का नाम बदला गया", + "folder_renamed_to": "फ़ोल्डर का नाम \"{{name}}\" रखा गया", + "file_uploaded": "फ़ाइल अपलोड हुई", + "file_deleted": "फ़ाइल रद्दी में भेजी गई", + "folder_deleted": "फ़ोल्डर रद्दी में भेजा गया", + "item_deleted_permanently": "आइटम स्थायी रूप से हटाया गया", + "trash_emptied": "रद्दी सफलतापूर्वक खाली की गई", + "title": "सूचनाएँ", + "empty": "कोई सूचना नहीं", + "link_created": "लिंक बनाया गया", + "share_success": "शेयर लिंक सफलतापूर्वक बनाया गया", + "upload_files_section_title": "यहाँ अपलोड उपलब्ध नहीं है", + "upload_files_section_body": "फ़ाइलें अपलोड करने के लिए फ़ाइलें अनुभाग पर जाएँ" + }, + "batch": { + "one_selected": "1 आइटम चयनित", + "n_selected": "{{count}} आइटम चयनित", + "confirm_delete": "क्या आप वाकई {{count}} आइटम रद्दी में भेजना चाहते हैं?", + "move_title": "{{count}} आइटम ले जाएँ", + "add_favorites": "पसंदीदा में जोड़ें", + "move_copy": "ले जाएँ या कॉपी करें" + }, + "admin": { + "page_title": "एडमिन पैनल", + "back_to_app": "OxiCloud पर वापस", + "loading": "लोड हो रहा है…", + "access_denied": "पहुंच अस्वीकृत", + "access_denied_desc": "व्यवस्थापक विशेषाधिकार आवश्यक।", + "sign_in": "साइन इन", + "tab_dashboard": "डैशबोर्ड", + "tab_users": "उपयोगकर्ता", + "tab_oidc": "SSO / OIDC", + "total_users": "कुल उपयोगकर्ता", + "active_users": "सक्रिय उपयोगकर्ता", + "admins": "व्यवस्थापक", + "version": "संस्करण", + "storage_overview": "स्टोरेज अवलोकन", + "used": "उपयोग किया", + "total_quota": "कुल कोटा", + "usage_pct": "उपयोग %", + "users_over_80": ">80% कोटा वाले", + "users_over_quota": "कोटा से अधिक", + "system": "सिस्टम", + "auth_label": "प्रमाणीकरण", + "oidc_label": "OIDC", + "quotas_label": "कोटा", + "enabled": "सक्षम", + "disabled": "अक्षम", + "active": "सक्रिय", + "off": "बंद", + "allow_registration": "सार्वजनिक पंजीकरण की अनुमति", + "registration_warning": "सार्वजनिक पंजीकरण अक्षम है। केवल व्यवस्थापक उपयोगकर्ता बना सकते हैं।", + "user_management": "उपयोगकर्ता प्रबंधन", + "create_user": "उपयोगकर्ता बनाएं", + "col_user": "उपयोगकर्ता", + "col_role": "भूमिका", + "col_auth": "प्रमाणीकरण", + "col_status": "स्थिति", + "col_storage": "स्टोरेज", + "col_last_login": "अंतिम लॉगिन", + "col_actions": "कार्रवाई", + "loading_users": "उपयोगकर्ता लोड हो रहे हैं…", + "failed_load_users": "लोड करने में विफल", + "no_users_found": "कोई उपयोगकर्ता नहीं मिला", + "showing_users": "{{from}}-{{to}} / {{total}} दिखा रहे हैं", + "prev": "पिछला", + "next": "अगला", + "inactive": "निष्क्रिय", + "you_badge": "(आप)", + "local": "स्थानीय", + "never": "कभी नहीं", + "just_now": "अभी", + "minutes_ago": "{{n}} मिनट पहले", + "hours_ago": "{{n}} घंटे पहले", + "days_ago": "{{n}} दिन पहले", + "edit_quota_title": "कोटा संपादित करें", + "reset_password_title": "पासवर्ड रीसेट", + "toggle_role_title": "भूमिका बदलें", + "deactivate_title": "निष्क्रिय करें", + "activate_title": "सक्रिय करें", + "delete_title": "हटाएं", + "sso_title": "सिंगल साइन-ऑन (OIDC / SSO)", + "enable_sso": "SSO सक्षम करें", + "provider_name": "प्रदाता का नाम", + "issuer_url": "जारीकर्ता URL", + "issuer_url_hint": "OpenID Connect जारीकर्ता URL", + "auto_discover": "स्वतः खोज", + "discovering": "खोज रहे हैं…", + "client_id": "क्लाइंट ID", + "client_secret": "क्लाइंट सीक्रेट", + "client_secret_placeholder": "वर्तमान मान बनाए रखने के लिए खाली छोड़ें", + "secret_configured": "क्लाइंट सीक्रेट पहले से कॉन्फ़िगर है", + "callback_url": "कॉलबैक URL", + "callback_url_hint": "(अपने IdP में पंजीकृत करें)", + "advanced_settings": "उन्नत सेटिंग्स", + "scopes": "स्कोप", + "auto_provision": "पहले लॉगिन पर स्वतः प्रावधान", + "admin_groups": "व्यवस्थापक समूह", + "admin_groups_hint": "अल्पविराम-पृथक OIDC समूह नाम", + "disable_password": "पासवर्ड लॉगिन अक्षम (केवल OIDC)", + "password_warning": "सभी पासवर्ड लॉगिन रुक जाएंगे!", + "test_btn": "परीक्षण", + "save_btn": "सहेजें", + "saving": "सहेज रहे हैं…", + "settings_saved": "सेटिंग्स सहेजी गईं — OIDC अब {{status}}", + "quota_modal_title": "स्टोरेज कोटा अपडेट", + "quota_user_label": "उपयोगकर्ता:", + "new_quota": "नया कोटा", + "quota_unlimited_hint": "असीमित के लिए 0", + "cancel": "रद्द करें", + "create_user_title": "नया उपयोगकर्ता बनाएं", + "username_label": "उपयोगकर्ता नाम", + "username_placeholder": "username", + "username_hint": "3–32 अक्षर", + "password_label": "पासवर्ड", + "password_placeholder": "न्यूनतम 8 अक्षर", + "email_label": "ईमेल", + "email_optional": "(वैकल्पिक)", + "email_placeholder": "user@example.com (खाली होने पर स्वतः)", + "role_label": "भूमिका", + "role_user": "उपयोगकर्ता", + "role_admin": "व्यवस्थापक", + "quota_label": "कोटा", + "creating": "बना रहे हैं…", + "reset_pw_title": "पासवर्ड रीसेट", + "new_password_label": "नया पासवर्ड", + "resetting": "रीसेट हो रहा है…", + "reset_btn": "रीसेट", + "confirm_role_change": "भूमिका {{role}} में बदलें?", + "confirm_deactivate": "इस उपयोगकर्ता को निष्क्रिय करें?", + "confirm_activate": "इस उपयोगकर्ता को सक्रिय करें?", + "confirm_delete_user": "उपयोगकर्ता \"{{name}}\" हटाएं? पूर्ववत नहीं होगा!", + "confirm_action": "कार्रवाई की पुष्टि", + "confirm_yes": "पुष्टि", + "confirm_no": "रद्द", + "error_username_short": "नाम कम से कम 3 अक्षर", + "error_password_short": "पासवर्ड कम से कम 8 अक्षर", + "error_generic": "विफल", + "error_network": "नेटवर्क त्रुटि: {{message}}", + "error_create_user": "उपयोगकर्ता बनाने में विफल", + "tab_storage": "स्टोरेज", + "storage_title": "स्टोरेज कॉन्फ़िगरेशन", + "storage_current_backend": "वर्तमान बैकएंड", + "storage_total_blobs": "कुल ब्लॉब्स", + "storage_total_size": "कुल आकार", + "storage_dedup_ratio": "डीडुप्लिकेशन अनुपात", + "storage_backend": "बैकएंड", + "storage_local": "स्थानीय", + "storage_s3": "S3 संगत", + "storage_provider_preset": "प्रदाता प्रीसेट", + "storage_preset_custom": "कस्टम", + "storage_endpoint_url": "एंडपॉइंट URL", + "storage_endpoint_hint": "AWS S3 के लिए खाली छोड़ें", + "storage_bucket": "बकेट", + "storage_region": "क्षेत्र", + "storage_access_key": "एक्सेस की", + "storage_secret_key": "सीक्रेट की", + "storage_secret_configured": "की कॉन्फ़िगर की गई", + "storage_key_placeholder": "नई की दर्ज करें", + "storage_path_style": "पाथ स्टाइल फ़ोर्स करें", + "storage_path_style_hint": "MinIO और कुछ S3-संगत सेवाओं के लिए आवश्यक", + "storage_test_connection": "कनेक्शन परीक्षण", + "storage_test_success": "कनेक्शन सफल", + "storage_test_failure": "कनेक्शन विफल", + "storage_save": "कॉन्फ़िगरेशन सहेजें", + "storage_saved": "कॉन्फ़िगरेशन सहेजी गई", + "storage_migration": "डेटा माइग्रेशन", + "storage_migration_coming_soon": "माइग्रेशन टूल्स जल्द आ रहे हैं", + "migration_status_label": "माइग्रेशन स्थिति", + "migration_start": "माइग्रेशन शुरू करें", + "migration_pause": "रोकें", + "migration_resume": "फिर से शुरू करें", + "migration_verify": "सत्यापित करें", + "migration_complete": "पूर्ण करें", + "migration_started": "माइग्रेशन शुरू हुआ", + "migration_paused_msg": "माइग्रेशन रोका गया", + "migration_resumed_msg": "माइग्रेशन फिर से शुरू हुआ", + "migration_completed_msg": "माइग्रेशन सफलतापूर्वक पूर्ण हुआ", + "migration_verifying": "सत्यापन हो रहा है...", + "migration_verify_passed": "सत्यापन पास", + "migration_verify_failed": "सत्यापन विफल", + "migration_failed_blobs": "विफल ब्लॉब्स", + "testing": "परीक्षण हो रहा है...", + "smtp_disabled": "अक्षम (होस्ट सेट नहीं)", + "smtp_enabled": "सक्षम", + "smtp_enabled_label": "स्थिति", + "smtp_intro": "SMTP केवल पर्यावरण चर (OXICLOUD_SMTP_*) के माध्यम से कॉन्फ़िगर किया जाता है। नीचे दिए गए मान चल रहे सर्वर से पढ़े जाते हैं — उन्हें बदलने के लिए, पर्यावरण संपादित करें और OxiCloud को पुनः आरंभ करें।", + "smtp_not_configured": "इस सर्वर पर SMTP कॉन्फ़िगर नहीं है।", + "smtp_send_failed": "भेजना विफल।", + "smtp_send_test": "परीक्षण ईमेल भेजें", + "smtp_sending": "भेजा जा रहा है…", + "smtp_sent": "परीक्षण ईमेल भेजा गया।", + "smtp_server_code": "सर्वर का उत्तर", + "smtp_test_intro": "नीचे दिए गए प्राप्तकर्ता को एक पूर्व-निर्धारित निदान संदेश भेजता है और SMTP सर्वर का उत्तर रिपोर्ट करता है ताकि आप इसे अपने रिले लॉग्स से मिला सकें।", + "smtp_test_missing_to": "प्राप्तकर्ता पता दर्ज करें।", + "smtp_test_title": "परीक्षण ईमेल भेजें", + "smtp_test_to": "प्राप्तकर्ता का पता", + "smtp_title": "जावक ईमेल (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "व्यवस्थापक", + "confirm_role": "भूमिका {{role}} में बदलें?", + "dashboard": "डैशबोर्ड", + "email": "ईमेल", + "mig_complete": "पूर्ण करें", + "mig_pause": "रोकें", + "mig_resume": "फिर से शुरू करें", + "mig_verify_failed": "सत्यापन विफल", + "mig_verify_passed": "सत्यापन पास", + "mig_verifying": "सत्यापन हो रहा है...", + "oidc_auto_provision": "पहले लॉगिन पर स्वतः प्रावधान", + "oidc_callback": "कॉलबैक URL", + "oidc_client_id": "क्लाइंट ID", + "oidc_disable_pw": "पासवर्ड लॉगिन अक्षम (केवल OIDC)", + "oidc_issuer": "जारीकर्ता URL", + "oidc_scopes": "स्कोप", + "password": "पासवर्ड", + "quotas": "कोटा", + "reset_pw_for": "नया पासवर्ड", + "role": "भूमिका", + "smtp_fail": "भेजना विफल।", + "smtp_send": "भेजें", + "smtp_test": "परीक्षण ईमेल भेजें", + "smtp_user_state": "प्रमाणीकरण", + "status": "स्थिति", + "storage": "स्टोरेज", + "storage_endpoint": "एंडपॉइंट URL", + "storage_tab": "स्टोरेज", + "time_min_ago": "{{n}} मिनट पहले", + "title": "व्यवस्थापक", + "user": "उपयोगकर्ता", + "username": "उपयोगकर्ता नाम", + "users": "उपयोगकर्ता" + }, + "profile": { + "page_title": "प्रोफ़ाइल", + "back_to_app": "OxiCloud पर वापस", + "loading": "लोड हो रहा है…", + "not_authenticated": "प्रमाणित नहीं", + "not_authenticated_desc": "अपना प्रोफ़ाइल देखने के लिए साइन इन करें।", + "sign_in": "साइन इन", + "role_admin": "व्यवस्थापक", + "role_user": "उपयोगकर्ता", + "account_details": "खाता विवरण", + "username": "उपयोगकर्ता नाम", + "email": "ईमेल", + "role": "भूमिका", + "last_login": "अंतिम लॉगिन", + "storage": "स्टोरेज", + "used": "उपयोग किया", + "quota": "कोटा", + "usage": "उपयोग", + "unlimited": "असीमित", + "app_passwords": "ऐप पासवर्ड", + "app_pw_desc": "WebDAV, CalDAV और CardDAV क्लाइंट के लिए पासवर्ड जनरेट करें। प्रत्येक पासवर्ड केवल एक बार दिखाया जाता है।", + "app_pw_label_placeholder": "लेबल (जैसे Thunderbird, macOS)", + "generate": "जनरेट करें", + "generating": "जनरेट हो रहा है…", + "new_password_for": "नया पासवर्ड", + "copy_warning": "इस पासवर्ड को अभी कॉपी करें। आप इसे दोबारा नहीं देख पाएंगे।", + "copy_to_clipboard": "क्लिपबोर्ड पर कॉपी करें", + "col_label": "लेबल", + "col_created": "बनाया गया", + "col_last_used": "अंतिम उपयोग", + "col_status": "स्थिति", + "active": "सक्रिय", + "revoked": "रद्द", + "revoke_title": "रद्द करें", + "no_app_passwords": "अभी तक कोई ऐप पासवर्ड नहीं।", + "client_sessions": "क्लाइंट सत्र", + "client_sessions_desc": "Nextcloud-संगत क्लाइंट कनेक्ट करने पर स्वतः जनरेट।", + "col_client": "क्लाइंट", + "never": "कभी नहीं", + "just_now": "अभी", + "minutes_ago": "{{n}} मिनट पहले", + "hours_ago": "{{n}} घंटे पहले", + "days_ago": "{{n}} दिन पहले", + "edit_profile": "प्रोफ़ाइल संपादित करें", + "edit_oidc_managed": "अपनी जानकारी (नाम, प्रथम नाम, प्रोफ़ाइल चित्र, …) बदलने के लिए, कृपया अपने पहचान प्रदाता पर इसे अद्यतन करें। आपके परिवर्तन अगले साइन-इन पर दिखाई देंगे।", + "username_claim_hint": "2–64 अक्षर, अक्षर / अंक / डॉट / डैश / अंडरस्कोर। एक बार चुनने के बाद, उपयोगकर्ता नाम नहीं बदला जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", + "username_already_claimed": "उपयोगकर्ता नाम सेट है और बदला नहीं जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", + "given_name": "प्रथम नाम", + "family_name": "अंतिम नाम", + "notify_on_share": "जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें", + "notify_on_share_hint": "जब अनचेक किया जाए, तो साझाकरण आपके खाते में दिखाई देते रहेंगे — आपको बस उनके बारे में ईमेल नहीं मिलेगा।", + "save_profile": "परिवर्तन सहेजें", + "profile_saved": "प्रोफ़ाइल अद्यतन की गई", + "profile_no_changes": "सहेजने के लिए कोई परिवर्तन नहीं।", + "profile_save_failed": "सहेजना विफल", + "username_taken_error": "यह उपयोगकर्ता नाम पहले से उपयोग में है।", + "username_immutable_error": "आपका उपयोगकर्ता नाम पहले से सेट है और यहाँ नहीं बदला जा सकता। यदि आपको नाम बदलने की आवश्यकता है तो किसी व्यवस्थापक से संपर्क करें।", + "change_password": "पासवर्ड बदलें", + "current_password": "वर्तमान पासवर्ड", + "new_password": "नया पासवर्ड", + "min_8_chars": "कम से कम 8 अक्षर", + "confirm_password": "नया पासवर्ड पुष्टि करें", + "update_password": "पासवर्ड अपडेट करें", + "updating": "अपडेट हो रहा है…", + "password_updated": "पासवर्ड सफलतापूर्वक अपडेट हुआ", + "passwords_no_match": "पासवर्ड मेल नहीं खाते", + "password_too_short": "पासवर्ड कम से कम 8 अक्षर का होना चाहिए", + "password_change_failed": "पासवर्ड बदलने में विफल", + "error_network": "नेटवर्क त्रुटि: {{message}}", + "error_label_required": "कृपया एक लेबल दर्ज करें", + "error_create_pw": "ऐप पासवर्ड बनाने में विफल", + "confirm_revoke": "ऐप पासवर्ड \"{{label}}\" रद्द करें? इसका उपयोग करने वाले क्लाइंट काम करना बंद कर देंगे।", + "error_revoke": "रद्द करने में विफल", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "पासवर्ड मेल नहीं खाते" + }, + "upload": { + "uploading": "अपलोड हो रहा है...", + "files": "फ़ाइलें", + "complete": "{{count}} / {{total}} अपलोड हुए" + }, + "storage_quota_exceeded": "स्टोरेज कोटा पार हो गया", + "sharedwithme": { + "pageTitle": "मेरे साथ साझा किया", + "pageDescription": "फ़ाइलें और फ़ोल्डर जो अन्य उपयोगकर्ताओं ने आपके साथ साझा किए हैं", + "emptyStateTitle": "अभी तक आपके साथ कुछ भी साझा नहीं किया गया", + "emptyStateDesc": "अन्य उपयोगकर्ताओं द्वारा आपके साथ साझा किए गए आइटम यहाँ दिखाई देंगे", + "loadMore": "और लोड करें", + "sharedBy": "द्वारा साझा किया", + "colName": "नाम", + "colType": "प्रकार", + "colSharedBy": "द्वारा साझा किया", + "colDate": "साझाकरण तिथि", + "colPermissions": "अनुमतियाँ" + }, + "groupby": { + "none": "कोई नहीं", + "title": "इसके अनुसार समूहीकृत करें", + "owner": "स्वामी", + "shareDate": "साझा तिथि", + "type": "प्रकार", + "type.folders": "फ़ोल्डर", + "accessedAt": "पहुँच की तारीख", + "modifiedAt": "संशोधन की तारीख", + "createdAt": "बनाने की तारीख", + "size": "आकार", + "favoriteDate": "पसंदीदा की तारीख", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "नया", + "folders": "फ़ोल्डर" + }, + "dateBucket": { + "today": "आज", + "last7days": "पिछले 7 दिन", + "last30days": "पिछले 30 दिन", + "unknown": "अज्ञात" + }, + "groups": { + "title": "समूह प्रबंधित करें", + "create_button": "समूह बनाएँ", + "create_dialog_title": "नया समूह", + "edit_dialog_title": "समूह का नाम बदलें", + "name_label": "नाम", + "name_placeholder": "engineering", + "description_label": "विवरण (वैकल्पिक)", + "members_section": "सदस्य", + "add_member_placeholder": "उपयोगकर्ता या समूह जोड़ें…", + "no_members": "अभी तक कोई सदस्य नहीं।", + "remove_member": "हटाएँ", + "delete_group": "समूह हटाएँ", + "delete_confirm": "समूह \"{name}\" को हटाएँ? इस समूह से जुड़ी अनुमतियाँ रद्द कर दी जाएँगी।", + "empty_state": "अभी तक कोई समूह नहीं।", + "load_more": "और लोड करें", + "back_to_list": "वापस", + "loading": "लोड हो रहा है…", + "virtual_badge": "सिस्टम", + "member_count_zero": "कोई सदस्य नहीं", + "member_count_one": "1 सदस्य", + "member_count_other": "{count} सदस्य", + "delete_confirm_label": "पुष्टि के लिए समूह का नाम लिखें:", + "delete_confirm_mismatch": "पुष्टि के लिए समूह का नाम बिल्कुल वैसा ही लिखें।", + "virtual_internal_name": "आंतरिक", + "members_loading": "सदस्य लोड हो रहे हैं…", + "members_empty": "कोई सदस्य नहीं", + "virtual_internal_explanation": "इस सर्वर पर हर आंतरिक उपयोगकर्ता", + "create": "समूह बनाएँ", + "empty": "अभी तक कोई समूह नहीं।", + "members": "सदस्य" + }, + "myshares": { + "copyLink": "लिंक कॉपी करें", + "deleteLink": "लिंक हटाएँ", + "notifyByEmail": "ईमेल से सूचित करें", + "notifyFailed": "सूचना नहीं भेजी जा सकी।", + "notifyGroupMembers": "समूह के सदस्यों को सूचित करें", + "notifyRateLimited": "इस प्राप्तकर्ता के लिए बहुत अधिक सूचनाएँ — बाद में पुनः प्रयास करें।", + "removeAccess": "पहुँच हटाएँ", + "resendInvitation": "आमंत्रण ईमेल पुनः भेजें", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "ऑडियो", + "code": "कोड", + "text": "टेक्स्ट" + }, + "common": { + "add": "जोड़ें", + "cancel": "रद्द करें", + "clear": "Clear", + "close": "बंद करें", + "confirm": "पुष्टि करें", + "copy": "कॉपी करें", + "create": "बनाएँ", + "delete": "हटाएँ", + "download": "डाउनलोड", + "load_more": "और लोड करें", + "loading": "लोड हो रहा है…", + "next": "अगला", + "no": "नहीं", + "previous": "पिछला", + "remove": "Remove", + "rename": "नाम बदलें", + "save": "सहेजें", + "search": "खोजें", + "yes": "हाँ" + }, + "device": { + "continue": "आगे बढ़ें", + "unknown": "अज्ञात" + }, + "expiryBucket": { + "expired": "समाप्त", + "noExpiry": "कोई समाप्ति नहीं", + "today": "आज", + "tomorrow": "कल" + }, + "nextcloud": { + "error_title": "त्रुटि", + "sign_in_with": "{{provider}} से साइन इन करें" + }, + "search": { + "size_label": "आकार", + "title": "खोजें", + "type": { + "audio": "ऑडियो" + }, + "type_label": "प्रकार" + }, + "sizeBucket": { + "folders": "फ़ोल्डर" + }, + "view": { + "grid": "ग्रिड दृश्य", + "list": "सूची दृश्य" + } } diff --git a/frontend/static/locales/it.json b/frontend/static/locales/it.json index 31bb6052..747c7346 100644 --- a/frontend/static/locales/it.json +++ b/frontend/static/locales/it.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Questo link di accesso non è più valido", - "expired_body": "Il link potrebbe essere scaduto o già stato utilizzato. Possiamo inviartene uno nuovo — arriverà nella tua casella di posta in pochi secondi.", - "resend_to": "Invia un nuovo link a {{email}}", - "generic_unavailable": "Questo link di accesso non è più valido. Potrebbe essere già stato utilizzato o essere scaduto. Richiedi un nuovo link dalla pagina di accesso.", - "service_unavailable": "L'accesso tramite magic link non è abilitato su questo server.", - "internal_error": "Si è verificato un errore durante l'accesso. Riprova.", - "resend_failure": "Si è verificato un errore durante l'invio del link. Riprova.", - "cross_browser_title": "Continuare l'accesso su questo dispositivo?", - "cross_browser_body": "Hai aperto questo link di accesso in un browser o dispositivo diverso da quello in cui l'hai richiesto.", - "cross_browser_warning": "Se hai richiesto questo link, puoi continuare in sicurezza. In caso contrario, chiudi questa pagina — cliccare su Continua effettuerebbe l'accesso di qualcun altro al tuo account.", - "cross_browser_continue": "Continua e accedi", - "resend_confirmation_title": "Controlla la tua casella di posta", - "resend_confirmation_body": "Se il link di accesso apparteneva a un account attivo, è appena stato inviato un nuovo link. Controlla la tua casella di posta.", - "return_link": "Torna a OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", - "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nAprilo facendo clic sul link sottostante:\n{{link}}\n\nIl link è monouso e scade tra {{ttl_hours}} ore.\nSe non ti aspettavi questo invito, puoi ignorare questo messaggio.\n\n— OxiCloud" - }, - "login": { - "subject": "Accedi a OxiCloud", - "body": "Ciao,\n\nUsa il link sottostante per accedere a OxiCloud. Il link è monouso e scade tra {{ttl_minutes}} minuti. Aprilo sullo stesso dispositivo da cui l'hai richiesto.\n\n{{link}}\n\nSe non hai richiesto questo link di accesso, puoi ignorare questo messaggio — non è necessaria alcuna ulteriore azione.\n\n— OxiCloud" - }, - "kind_file": "file", - "kind_folder": "cartella", - "english_fallback_divider": "--- Versione inglese qui sotto ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Questo link di accesso non è più valido", + "expired_body": "Il link potrebbe essere scaduto o già stato utilizzato. Possiamo inviartene uno nuovo — arriverà nella tua casella di posta in pochi secondi.", + "resend_to": "Invia un nuovo link a {{email}}", + "generic_unavailable": "Questo link di accesso non è più valido. Potrebbe essere già stato utilizzato o essere scaduto. Richiedi un nuovo link dalla pagina di accesso.", + "service_unavailable": "L'accesso tramite magic link non è abilitato su questo server.", + "internal_error": "Si è verificato un errore durante l'accesso. Riprova.", + "resend_failure": "Si è verificato un errore durante l'invio del link. Riprova.", + "cross_browser_title": "Continuare l'accesso su questo dispositivo?", + "cross_browser_body": "Hai aperto questo link di accesso in un browser o dispositivo diverso da quello in cui l'hai richiesto.", + "cross_browser_warning": "Se hai richiesto questo link, puoi continuare in sicurezza. In caso contrario, chiudi questa pagina — cliccare su Continua effettuerebbe l'accesso di qualcun altro al tuo account.", + "cross_browser_continue": "Continua e accedi", + "resend_confirmation_title": "Controlla la tua casella di posta", + "resend_confirmation_body": "Se il link di accesso apparteneva a un account attivo, è appena stato inviato un nuovo link. Controlla la tua casella di posta.", + "return_link": "Torna a OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", + "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nAprilo facendo clic sul link sottostante:\n{{link}}\n\nIl link è monouso e scade tra {{ttl_hours}} ore.\nSe non ti aspettavi questo invito, puoi ignorare questo messaggio.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", - "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nApri OxiCloud per vedere la tua nuova condivisione:\n{{login_link}}\n\nPotresti avere altre nuove condivisioni da {{inviter}} — accedi per vedere tutti gli elementi condivisi con te.\n\n— OxiCloud\n\nRicevi questo messaggio perché hai un account OxiCloud e la preferenza di notifica delle condivisioni è attiva. Puoi disattivarla dal tuo profilo (Avvisami via email quando qualcuno condivide con me)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Sistema di archiviazione cloud minimalista" - }, - "nav": { - "files": "File", - "shared": "Condivisioni", - "recent": "Recenti", - "favorites": "Preferiti", - "photos": "Foto", - "music": "Musica", - "trash": "Cestino", - "sharedwithme": "Condivisi con me" - }, - "photos": { - "empty_state": "Nessuna foto ancora", - "empty_hint": "Carica immagini o video per vederli qui", - "items_selected": "selezionati", - "view_daily": "Giorno", - "view_monthly": "Mese", - "view_yearly": "Anno" - }, - "music": { - "create_playlist": "Crea Playlist", - "playlists": "Playlist", - "no_playlists": "Nessuna playlist", - "select_playlist": "Seleziona una playlist", - "select_hint": "Scegli una playlist dalla barra laterale o creane una nuova", - "add_tracks": "Aggiungi Tracce", - "no_tracks": "Nessuna traccia in questa playlist", - "unknown_artist": "Artista Sconosciuto", - "unknown_title": "Sconosciuto", - "confirm_delete": "Eliminare questa playlist?", - "playlist_name": "Nome playlist", - "create": "Crea", - "delete": "Elimina", - "share": "Condividi", - "edit": "Modifica", - "play_all": "Riproduci Tutto", - "shuffle": "Casuale", - "repeat": "Ripeti", - "repeat_one": "Ripeti Una", - "queue": "Coda", - "queue_empty": "Coda vuota", - "not_playing": "Non in riproduzione", - "play": "Riproduci", - "pause": "Pausa", - "previous": "Precedente", - "next": "Successivo", - "volume": "Volume", - "mute": "Muto", - "unmute": "Attiva audio", - "title": "Titolo", - "artist": "Artista", - "album": "Album", - "tracks": "tracce", - "add": "Aggiungi", - "added": "Aggiunto!", - "added_to_playlist": "aggiunto alla playlist", - "add_to_playlist": "Aggiungi alla playlist", - "load_error": "Errore nel caricamento delle playlist", - "add_error": "Impossibile aggiungere le tracce", - "no_playlists_yet": "Nessuna playlist ancora. Creane una prima!", - "selected_files": "Selezionati:", - "error": "Errore", - "search_audio": "Cerca file audio…", - "no_audio_files": "Nessun file audio trovato", - "selected": "selezionati", - "loading": "Caricamento…", - "search_error": "Impossibile caricare i file audio", - "adding": "Aggiunta in corso…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Cerca file...", - "new_folder": "Nuova cartella", - "upload": "Carica", - "upload_files": "Carica file", - "upload_folder": "Carica cartella", - "upload.uploading": "Caricamento...", - "upload.complete": "{count} / {total} caricati", - "upload.files": "file", - "rename": "Rinomina", - "move": "Sposta in...", - "move_to": "Sposta in", - "delete": "Elimina", - "download": "Scarica", - "view": "Visualizza", - "cancel": "Annulla", - "confirm": "Conferma", - "share": "Condividi", - "favorite": "Aggiungi ai preferiti", - "unfavorite": "Rimuovi dai preferiti", - "copy": "Copia", - "notify": "Notifica", - "send": "Invia", - "clear_recent": "Cancella recenti", - "logout": "Disconnetti", - "create": "Crea", - "search_btn": "Cerca", - "close": "Chiudi", - "delete_permanently": "Elimina definitivamente", - "empty_trash": "Svuota il cestino", - "open_parent_folder": "Vai alla cartella padre", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Aspetto", - "about": "Informazioni su OxiCloud", - "about_description": "Piattaforma di archiviazione cloud realizzata con Rust & Architettura Pulita. Veloce, sicura e privata.", - "admin_panel": "Pannello di amministrazione", - "profile": "Il mio profilo", - "role_user": "Utente", - "theme": { - "light": "Chiaro", - "dark": "Scuro", - "auto": "Come il sistema" + "login": { + "subject": "Accedi a OxiCloud", + "body": "Ciao,\n\nUsa il link sottostante per accedere a OxiCloud. Il link è monouso e scade tra {{ttl_minutes}} minuti. Aprilo sullo stesso dispositivo da cui l'hai richiesto.\n\n{{link}}\n\nSe non hai richiesto questo link di accesso, puoi ignorare questo messaggio — non è necessaria alcuna ulteriore azione.\n\n— OxiCloud" }, - "manage_groups": "Gestisci gruppi" + "kind_file": "file", + "kind_folder": "cartella", + "english_fallback_divider": "--- Versione inglese qui sotto ---" + } }, - "share": { - "dialogTitle": "Link di condivisione", - "linkLabel": "Link di condivisione:", - "copyLink": "Copia", - "permissions": "Permessi:", - "permissionRead": "Lettura", - "permissionWrite": "Scrittura", - "permissionReshare": "Ricondivisione", - "password": "Protezione password:", - "generatePassword": "Genera", - "expiration": "Data di scadenza:", - "update": "Aggiorna condivisione", - "remove": "Rimuovi condivisione", - "notifyTitle": "Invia notifica", - "notifyEmailLabel": "Indirizzo email:", - "notifyMessageLabel": "Messaggio (opzionale):", - "notifySend": "Invia notifica", - "shareWithOthers": "Condividi con altri", - "sharePublicly": "Condividi pubblicamente", - "shareSettings": "Impostazioni di condivisione", - "shareCopied": "Link copiato negli appunti", - "shareCreated": "Link di condivisione creato con successo", - "shareUpdated": "Impostazioni di condivisione aggiornate con successo", - "shareRemoved": "Condivisione rimossa con successo", - "inviteByEmail": "Invita via email — verrà inviato un invito", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link di condivisione", - "share_linkLabel": "Link di condivisione:", - "share_copyLink": "Copia", - "share_permissions": "Permessi:", - "share_permissionRead": "Lettura", - "share_permissionWrite": "Scrittura", - "share_permissionReshare": "Ricondivisione", - "share_password": "Protezione password:", - "share_generatePassword": "Genera", - "share_expiration": "Data di scadenza:", - "share_update": "Aggiorna condivisione", - "share_remove": "Rimuovi condivisione", - "share_notifyTitle": "Invia notifica", - "share_notifyEmailLabel": "Indirizzo email:", - "share_notifyMessageLabel": "Messaggio (opzionale):", - "share_notifySend": "Invia notifica", - "shared": { - "backToFiles": "Torna ai file", - "pageTitle": "Risorse condivise", - "pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", - "filterType": "Tipo:", - "filterAll": "Tutti", - "filterFiles": "File", - "filterFolders": "Cartelle", - "sortBy": "Ordina per:", - "sortByName": "Nome", - "sortByDate": "Data di condivisione", - "sortByExpiration": "Scadenza", - "search": "Cerca", - "colName": "Nome", - "colType": "Tipo", - "colDateShared": "Data di condivisione", - "colExpiration": "Scadenza", - "colPermissions": "Permessi", - "colPassword": "Password", - "colActions": "Azioni", - "emptyStateTitle": "Ancora nessuna risorsa condivisa", - "emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", - "goToFiles": "Vai ai file", - "typeFile": "File", - "typeFolder": "Cartella", - "noExpiration": "Nessuna scadenza", - "hasPassword": "Sì", - "noPassword": "No", - "editShare": "Modifica condivisione", - "notifyShare": "Notifica a qualcuno", - "copyLink": "Copia link", - "removeShare": "Rimuovi condivisione", - "linkCopied": "Link copiato negli appunti!", - "linkCopyFailed": "Impossibile copiare il link", - "itemUpdated": "Impostazioni di condivisione aggiornate con successo", - "itemRemoved": "Condivisione rimossa con successo", - "invalidEmail": "Inserisci un indirizzo email valido", - "notificationSent": "Notifica inviata con successo", - "notificationFailed": "Impossibile inviare la notifica", - "shared_backToFiles": "Torna ai file", - "shared_pageTitle": "Risorse condivise", - "shared_pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", - "shared_filterType": "Tipo:", - "shared_filterAll": "Tutti", - "shared_filterFiles": "File", - "shared_filterFolders": "Cartelle", - "shared_sortBy": "Ordina per:", - "shared_sortByName": "Nome", - "shared_sortByDate": "Data di condivisione", - "shared_sortByExpiration": "Scadenza", - "shared_search": "Cerca", - "shared_colName": "Nome", - "shared_colType": "Tipo", - "shared_colDateShared": "Data di condivisione", - "shared_colExpiration": "Scadenza", - "shared_colPermissions": "Permessi", - "shared_colPassword": "Password", - "shared_colActions": "Azioni", - "shared_emptyStateTitle": "Ancora nessuna risorsa condivisa", - "shared_emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", - "shared_goToFiles": "Vai ai file", - "shared_typeFile": "File", - "shared_typeFolder": "Cartella", - "shared_noExpiration": "Nessuna scadenza", - "shared_hasPassword": "Sì", - "shared_noPassword": "No", - "shared_editShare": "Modifica condivisione", - "shared_notifyShare": "Notifica a qualcuno", - "shared_copyLink": "Copia link", - "shared_removeShare": "Rimuovi condivisione", - "shared_linkCopied": "Link copiato negli appunti!", - "shared_linkCopyFailed": "Impossibile copiare il link", - "shared_itemUpdated": "Impostazioni di condivisione aggiornate con successo", - "shared_itemRemoved": "Condivisione rimossa con successo", - "shared_invalidEmail": "Inserisci un indirizzo email valido", - "shared_notificationSent": "Notifica inviata con successo", - "shared_notificationFailed": "Impossibile inviare la notifica" - }, - "files": { - "name": "Nome", - "type": "Tipo", - "size": "Dimensione", - "modified": "Modificato", - "no_files": "Nessun file in questa cartella", - "empty_hint": "Carica file o crea cartelle per iniziare", - "loading": "Caricamento file…", - "view_grid": "Visualizzazione griglia", - "view_list": "Visualizzazione elenco", - "file_types": { - "document": "Documento", - "image": "Immagine", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Testo", - "folder": "Cartella", - "spreadsheet": "Foglio di calcolo", - "presentation": "Presentazione", - "archive": "Archivio", - "installer": "Programma di installazione", - "code": "Codice" - }, - "owner": "Proprietario" - }, - "dialogs": { - "rename_folder": "Rinomina cartella", - "rename_file": "Rinomina file", - "new_name": "Nuovo nome", - "new_folder_title": "Nuova cartella", - "folder_name": "Nome cartella", - "folder_placeholder": "La mia cartella", - "rename_title": "Rinomina", - "move_file": "Sposta file", - "move_folder": "Sposta cartella", - "select_destination": "Seleziona cartella di destinazione:", - "root": "Root", - "delete_confirmation": "Sei sicuro di voler eliminare", - "and_contents": "e tutto il suo contenuto", - "no_undo": "Questa azione non può essere annullata", - "confirm_title": "Conferma azione", - "confirm_delete": "Sposta nel cestino", - "confirm_delete_file": "Sei sicuro di voler spostare il file \"{{name}}\" nel cestino?", - "confirm_delete_folder": "Sei sicuro di voler spostare la cartella \"{{name}}\" e tutto il suo contenuto nel cestino?", - "confirm_permanent_delete": "Elimina definitivamente", - "confirm_permanent_delete_msg": "Sei sicuro di voler eliminare definitivamente questo elemento? Questa azione non può essere annullata.", - "confirm_empty_trash": "Svuota il cestino", - "confirm_delete_share": "Elimina link di condivisione", - "confirm_delete_share_msg": "Sei sicuro di voler eliminare questo link di condivisione?", - "share_file": "Condividi File", - "share_folder": "Condividi Cartella", - "existing_shares": "Condivisioni Esistenti", - "share_options": "Opzioni di Condivisione", - "password": "Password", - "expiration": "Scadenza", - "permissions": "Permessi", - "generated_link": "Link Generato", - "notify": "Invia Notifica", - "recipient": "Destinatario", - "message": "Messaggio", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Sposta nella cartella home" - }, - "dropzone": { - "drag_files": "Trascina i file qui o clicca per selezionare", - "drop_files": "Rilascia i file per caricarli" - }, - "permissions": { - "read": "Lettura", - "write": "Scrittura", - "reshare": "Ricondividi" - }, - "errors": { - "file_not_found": "File non trovato", - "folder_not_found": "Cartella non trovata", - "delete_error": "Errore durante l'eliminazione", - "upload_error": "Errore durante il caricamento del file", - "rename_error": "Errore durante la rinomina", - "move_error": "Errore durante lo spostamento", - "empty_name": "Il nome non può essere vuoto", - "name_exists": "Un file o una cartella con quel nome esiste già", - "generic_error": "Si è verificato un errore", - "group_name_invalid": "Il nome del gruppo deve rispettare il formato del prefisso email (lettere, cifre, punto, trattino, trattino basso; 1–64 caratteri).", - "group_cycle": "Questo membro creerebbe un riferimento circolare tra gruppi.", - "group_depth_exceeded": "Questa profondità di annidamento supera il massimo consentito (8).", - "group_virtual_immutable": "Il gruppo «Internal» è gestito dal sistema e non può essere modificato.", - "group_not_found": "Gruppo non trovato.", - "group_name_taken": "Un gruppo con questo nome esiste già." - }, - "breadcrumb": { - "home": "Home" - }, - "trash": { - "empty_trash": "Svuota il cestino", - "empty_state": "Il cestino è vuoto", - "original_location": "Posizione originale", - "deleted_date": "Data di eliminazione", - "remaining": "Rimanente", - "actions": "Azioni", - "restore": "Ripristina", - "delete_permanently": "Elimina definitivamente", - "empty_confirm": "Sei sicuro di voler svuotare il cestino? Questa operazione eliminerà definitivamente tutti gli elementi.", - "groupby": { - "remaining_days": "Giorni rimanenti", - "trashed_time": "Data di eliminazione" - } - }, - "daysRemaining": { - "expired": "Scaduto", - "today": "Oggi", - "tomorrow": "Domani", - "inDays": "{{count}} giorni" - }, - "expiryChip": { - "never": "Non scade mai", - "expired": "Scaduto", - "today": "Scade oggi", - "tomorrow": "Scade domani", - "inDays": "Scade tra {{count}} giorni", - "onDate": "Scade il {{date}}" - }, - "auth": { - "login_title": "Accedi", - "username": "Nome utente", - "username_placeholder": "Inserisci il tuo nome utente", - "login_identifier": "Nome utente o email", - "login_identifier_placeholder": "Inserisci il tuo nome utente o email", - "password": "Password", - "password_placeholder": "Inserisci la tua password", - "login_button": "Accedi", - "no_account": "Non hai un account?", - "register": "Registrati", - "admin_setup": "È la prima volta?", - "setup": "Configura amministratore", - "register_title": "Crea account", - "email": "Email", - "email_placeholder": "Inserisci la tua email", - "confirm_password": "Conferma password", - "confirm_password_placeholder": "Conferma la tua password", - "register_button": "Crea account", - "have_account": "Hai già un account?", - "login": "Accedi", - "setup_title": "Configurazione iniziale", - "setup_step1": "Amministratore", - "setup_step2": "Sistema", - "setup_step3": "Completa", - "admin_username": "Nome utente amministratore", - "admin_email": "Email amministratore", - "admin_password": "Password amministratore", - "create_admin": "Crea amministratore", - "back_to_login": "Già configurato?", - "admin_success": "Account amministratore creato con successo! Ora puoi accedere.", - "account_success": "Account creato con successo! Ora puoi accedere.", - "passwords_mismatch": "Le password non corrispondono", - "admin_create_error": "Errore durante la creazione dell'account amministratore", - "or": "o", - "sso_login": "Accedi con SSO", - "sso_login_provider": "Accedi con {{provider}}", - "magicLinkHint": "Niente password? Inserisci la tua email e ti invieremo un link di accesso monouso.", - "magicLinkEmailLabel": "Indirizzo email", - "magicLinkEmailPlaceholder": "tu@esempio.com", - "magicLinkSubmit": "Invia link di accesso", - "magicLinkSent": "Se esiste un account per questa email, è stato inviato un link di accesso. Controlla la tua casella di posta.", - "magicLinkUnavailable": "L'accesso tramite email non è disponibile su questo server.", - "magicLinkNetworkError": "Impossibile raggiungere il server: {{message}}", - "magicLinkToggle": "Nessuna password? Ricevi un link via e-mail", - "passwordsMatch": "Le password corrispondono", - "capsLock": "Bloc Maiusc attivo" - }, - "storage": { - "title": "Archiviazione", - "calculating": "Calcolo in corso...", - "used": "{{percentage}}% utilizzato ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Questo tipo di file non può essere visualizzato in anteprima.", - "download_file": "Scarica file", - "zoom_in": "Ingrandisci", - "zoom_out": "Riduci", - "zoom_reset": "Reimposta zoom" - }, - "language_selector": { - "title": "Benvenuto!", - "subtitle": "Seleziona la tua lingua per continuare", - "continue": "Continua", - "languages": { - "en": "Inglese", - "es": "Spagnolo", - "zh": "Cinese", - "fa": "Persiano", - "fr": "Francese", - "de": "Tedesco", - "pt": "Portoghese", - "it": "Italiano", - "ar": "العربية", - "hi": "हिन्दी", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Ancora nessun preferito", - "empty_hint": "Aggiungi file o cartelle ai preferiti per inserirli qui", - "add": "Aggiungi ai preferiti", - "remove": "Rimuovi dai preferiti", - "added_title": "Aggiunto ai preferiti", - "added_msg": "aggiunto ai preferiti", - "removed_title": "Rimosso dai preferiti", - "removed_msg": "rimosso dai preferiti" - }, - "recent": { - "title": "Recenti", - "clear": "Cancella recenti", - "accessed": "Accesso", - "empty_state": "Nessun file recente", - "empty_hint": "I file che apri appariranno qui", - "loadMore": "Carica altri" - }, - "notifications": { - "file_renamed": "File rinominato", - "file_renamed_to": "File rinominato in \"{{name}}\"", - "folder_renamed": "Cartella rinominata", - "folder_renamed_to": "Cartella rinominata in \"{{name}}\"", - "file_uploaded": "File caricato", - "file_deleted": "File spostato nel cestino", - "folder_deleted": "Cartella spostata nel cestino", - "item_deleted_permanently": "Elemento eliminato definitivamente", - "trash_emptied": "Cestino svuotato con successo", - "empty": "No notifications", - "title": "Notifications", - "link_created": "Link creato", - "share_success": "Link di condivisione creato con successo", - "upload_files_section_title": "Caricamento non disponibile qui", - "upload_files_section_body": "Vai alla sezione File per caricare i file" - }, - "batch": { - "one_selected": "1 elemento selezionato", - "n_selected": "{{count}} elementi selezionati", - "confirm_delete": "Sei sicuro di voler spostare {{count}} elementi nel cestino?", - "move_title": "Sposta {{count}} elemento/i", - "add_favorites": "Aggiungi ai preferiti", - "move_copy": "Sposta o copia" - }, - "admin": { - "page_title": "Pannello di Amministrazione", - "back_to_app": "Torna a OxiCloud", - "loading": "Caricamento…", - "access_denied": "Accesso Negato", - "access_denied_desc": "Privilegi di amministratore necessari.", - "sign_in": "Accedi", - "tab_dashboard": "Dashboard", - "tab_users": "Utenti", - "tab_oidc": "SSO / OIDC", - "total_users": "Utenti Totali", - "active_users": "Utenti Attivi", - "admins": "Amministratori", - "version": "Versione", - "storage_overview": "Panoramica Archiviazione", - "used": "Usato", - "total_quota": "Quota Totale", - "usage_pct": "Utilizzo %", - "users_over_80": "Utenti >80% quota", - "users_over_quota": "Utenti oltre la quota", - "system": "Sistema", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quote", - "enabled": "Abilitato", - "disabled": "Disabilitato", - "active": "Attivo", - "off": "Spento", - "allow_registration": "Consenti registrazione pubblica", - "registration_warning": "La registrazione pubblica è disabilitata. Solo gli admin possono creare utenti.", - "user_management": "Gestione Utenti", - "create_user": "Crea Utente", - "col_user": "Utente", - "col_role": "Ruolo", - "col_auth": "Auth", - "col_status": "Stato", - "col_storage": "Archiviazione", - "col_last_login": "Ultimo Accesso", - "col_actions": "Azioni", - "loading_users": "Caricamento utenti…", - "failed_load_users": "Impossibile caricare", - "no_users_found": "Nessun utente trovato", - "showing_users": "Mostrando {{from}}-{{to}} di {{total}}", - "prev": "Precedente", - "next": "Successivo", - "inactive": "Inattivo", - "you_badge": "(tu)", - "local": "Locale", - "never": "Mai", - "just_now": "Proprio adesso", - "minutes_ago": "{{n}}min fa", - "hours_ago": "{{n}}h fa", - "days_ago": "{{n}}g fa", - "edit_quota_title": "Modifica quota", - "reset_password_title": "Reimposta password", - "toggle_role_title": "Cambia ruolo", - "deactivate_title": "Disattiva", - "activate_title": "Attiva", - "delete_title": "Elimina", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "Abilita autenticazione SSO", - "provider_name": "Nome Provider", - "issuer_url": "URL Emittente", - "issuer_url_hint": "URL dell'emittente OpenID Connect", - "auto_discover": "Auto-scoperta", - "discovering": "Scoperta…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Lascia vuoto per mantenere il valore", - "secret_configured": "Un client secret è già configurato", - "callback_url": "URL di Callback", - "callback_url_hint": "(registra nel tuo IdP)", - "advanced_settings": "Impostazioni Avanzate", - "scopes": "Scopes", - "auto_provision": "Provisioning automatico degli utenti", - "admin_groups": "Gruppi Admin", - "admin_groups_hint": "Nomi di gruppi OIDC separati da virgola", - "disable_password": "Disabilita accesso con password (solo OIDC)", - "password_warning": "Questo impedirà TUTTI gli accessi tramite password!", - "test_btn": "Test", - "save_btn": "Salva", - "saving": "Salvataggio…", - "settings_saved": "Impostazioni salvate — OIDC ora è {{status}}", - "quota_modal_title": "Aggiorna Quota", - "quota_user_label": "Utente:", - "new_quota": "Nuova Quota", - "quota_unlimited_hint": "0 per illimitato", - "cancel": "Annulla", - "create_user_title": "Crea Nuovo Utente", - "username_label": "Nome utente", - "username_placeholder": "mariorossi", - "username_hint": "3–32 caratteri", - "password_label": "Password", - "password_placeholder": "Min 8 caratteri", - "email_label": "Email", - "email_optional": "(facoltativo)", - "email_placeholder": "utente@esempio.com (auto-generata se vuoto)", - "role_label": "Ruolo", - "role_user": "Utente", - "role_admin": "Admin", - "quota_label": "Quota", - "creating": "Creazione…", - "reset_pw_title": "Reimposta Password", - "new_password_label": "Nuova Password", - "resetting": "Reimpostazione…", - "reset_btn": "Reimposta", - "confirm_role_change": "Cambiare ruolo a {{role}}?", - "confirm_deactivate": "Sei sicuro di voler disattivare questo utente?", - "confirm_activate": "Sei sicuro di voler attivare questo utente?", - "confirm_delete_user": "ELIMINARE l'utente \"{{name}}\"? Azione irreversibile!", - "confirm_action": "Conferma Azione", - "confirm_yes": "Conferma", - "confirm_no": "Annulla", - "error_username_short": "Il nome utente deve avere almeno 3 caratteri", - "error_password_short": "La password deve avere almeno 8 caratteri", - "error_generic": "Fallito", - "error_network": "Errore di rete: {{message}}", - "error_create_user": "Impossibile creare l'utente", - "tab_storage": "Archiviazione", - "storage_title": "Configurazione archiviazione", - "storage_current_backend": "Backend corrente", - "storage_total_blobs": "Blob totali", - "storage_total_size": "Dimensione totale", - "storage_dedup_ratio": "Rapporto deduplicazione", - "storage_backend": "Backend", - "storage_local": "Locale", - "storage_s3": "Compatibile S3", - "storage_provider_preset": "Preset fornitore", - "storage_preset_custom": "Personalizzato", - "storage_endpoint_url": "URL endpoint", - "storage_endpoint_hint": "Lasciare vuoto per AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Regione", - "storage_access_key": "Chiave di accesso", - "storage_secret_key": "Chiave segreta", - "storage_secret_configured": "Chiave configurata", - "storage_key_placeholder": "Inserisci nuova chiave", - "storage_path_style": "Forza stile percorso", - "storage_path_style_hint": "Richiesto per MinIO e alcuni servizi compatibili S3", - "storage_test_connection": "Testa connessione", - "storage_test_success": "Connessione riuscita", - "storage_test_failure": "Connessione fallita", - "storage_save": "Salva configurazione", - "storage_saved": "Configurazione salvata", - "storage_migration": "Migrazione dati", - "storage_migration_coming_soon": "Strumenti di migrazione in arrivo", - "migration_status_label": "Stato migrazione", - "migration_start": "Avvia migrazione", - "migration_pause": "Pausa", - "migration_resume": "Riprendi", - "migration_verify": "Verifica", - "migration_complete": "Completa", - "migration_started": "Migrazione avviata", - "migration_paused_msg": "Migrazione in pausa", - "migration_resumed_msg": "Migrazione ripresa", - "migration_completed_msg": "Migrazione completata con successo", - "migration_verifying": "Verifica in corso...", - "migration_verify_passed": "Verifica superata", - "migration_verify_failed": "Verifica fallita", - "migration_failed_blobs": "Blob falliti", - "testing": "Test in corso...", - "smtp_disabled": "Disabilitato (host non impostato)", - "smtp_enabled": "Abilitato", - "smtp_enabled_label": "Stato", - "smtp_intro": "SMTP è configurato esclusivamente tramite variabili d'ambiente (OXICLOUD_SMTP_*). I valori sottostanti sono letti dal server in esecuzione — per modificarli, modifica l'ambiente e riavvia OxiCloud.", - "smtp_not_configured": "SMTP non è configurato su questo server.", - "smtp_send_failed": "Invio non riuscito.", - "smtp_send_test": "Invia email di prova", - "smtp_sending": "Invio in corso…", - "smtp_sent": "Email di prova inviata.", - "smtp_server_code": "Risposta del server", - "smtp_test_intro": "Invia un messaggio diagnostico predefinito al destinatario indicato sotto e riporta la risposta del server SMTP, così puoi correlarla con i log del tuo relay.", - "smtp_test_missing_to": "Inserisci un indirizzo destinatario.", - "smtp_test_title": "Invia un'email di prova", - "smtp_test_to": "Indirizzo destinatario", - "smtp_title": "Email in uscita (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profilo", - "back_to_app": "Torna a OxiCloud", - "loading": "Caricamento…", - "not_authenticated": "Non Autenticato", - "not_authenticated_desc": "Accedi per visualizzare il tuo profilo.", - "sign_in": "Accedi", - "role_admin": "Amministratore", - "role_user": "Utente", - "account_details": "Dettagli Account", - "username": "Nome utente", - "email": "Email", - "role": "Ruolo", - "last_login": "Ultimo accesso", - "storage": "Archiviazione", - "used": "Usato", - "quota": "Quota", - "usage": "Utilizzo", - "unlimited": "Illimitato", - "app_passwords": "Password Applicazione", - "app_pw_desc": "Genera password per client WebDAV, CalDAV e CardDAV. Ogni password viene mostrata una sola volta.", - "app_pw_label_placeholder": "Etichetta (es. Thunderbird, macOS)", - "generate": "Genera", - "generating": "Generazione…", - "new_password_for": "Nuova password per", - "copy_warning": "Copia questa password ora. Non potrai rivederla.", - "copy_to_clipboard": "Copia negli appunti", - "col_label": "Etichetta", - "col_created": "Creato", - "col_last_used": "Ultimo utilizzo", - "col_status": "Stato", - "active": "Attiva", - "revoked": "Revocata", - "revoke_title": "Revoca", - "no_app_passwords": "Nessuna password applicazione ancora.", - "client_sessions": "Sessioni client", - "client_sessions_desc": "Generate automaticamente quando connetti un client compatibile Nextcloud.", - "col_client": "Client", - "never": "Mai", - "just_now": "Proprio adesso", - "minutes_ago": "{{n}} min fa", - "hours_ago": "{{n}}h fa", - "days_ago": "{{n}} giorni fa", - "edit_profile": "Modifica profilo", - "edit_oidc_managed": "Per modificare le tue informazioni (nome, cognome, foto profilo, …), aggiornale presso il tuo identity provider. Le modifiche compariranno al prossimo accesso.", - "username_claim_hint": "Da 2 a 64 caratteri, lettere / cifre / punto / trattino / sottolineatura. Una volta scelto, il nome utente non può essere modificato (i client DAV/NextCloud dipendono da esso).", - "username_already_claimed": "Nome utente impostato e non modificabile (i client DAV/NextCloud dipendono da esso).", - "given_name": "Nome", - "family_name": "Cognome", - "notify_on_share": "Avvisami via email quando qualcuno condivide con me", - "notify_on_share_hint": "Se deselezionato, le condivisioni continueranno ad apparire nel tuo account — semplicemente non riceverai un'email a riguardo.", - "save_profile": "Salva modifiche", - "profile_saved": "Profilo aggiornato", - "profile_no_changes": "Nessuna modifica da salvare.", - "profile_save_failed": "Salvataggio non riuscito", - "username_taken_error": "Questo nome utente è già in uso.", - "username_immutable_error": "Il tuo nome utente è già impostato e non può essere cambiato qui. Contatta un amministratore se desideri rinominarlo.", - "change_password": "Cambia Password", - "current_password": "Password Attuale", - "new_password": "Nuova Password", - "min_8_chars": "Almeno 8 caratteri", - "confirm_password": "Conferma Nuova Password", - "update_password": "Aggiorna Password", - "updating": "Aggiornamento…", - "password_updated": "Password aggiornata con successo", - "passwords_no_match": "Le password non corrispondono", - "password_too_short": "La password deve avere almeno 8 caratteri", - "password_change_failed": "Impossibile cambiare la password", - "error_network": "Errore di rete: {{message}}", - "error_label_required": "Inserisci un'etichetta", - "error_create_pw": "Impossibile creare la password", - "confirm_revoke": "Revocare la password \"{{label}}\"? I client che la usano smetteranno di funzionare.", - "error_revoke": "Revoca fallita", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Caricamento in corso...", - "files": "file", - "complete": "{{count}} / {{total}} caricati" - }, - "storage_quota_exceeded": "Quota di archiviazione superata", - "sharedwithme": { - "pageTitle": "Condiviso con me", - "pageDescription": "File e cartelle che altri utenti hanno condiviso con te", - "emptyStateTitle": "Niente è ancora condiviso con te", - "emptyStateDesc": "Gli elementi condivisi con te da altri utenti appariranno qui", - "loadMore": "Carica altri", - "sharedBy": "Condiviso da", - "colName": "Nome", - "colType": "Tipo", - "colSharedBy": "Condiviso da", - "colDate": "Data condivisione", - "colPermissions": "Permessi" - }, - "groupby": { - "none": "Nessuno", - "title": "Raggruppa per", - "owner": "Proprietario", - "shareDate": "Data condivisione", - "type": "Tipo", - "type.folders": "Cartelle", - "accessedAt": "Data di accesso", - "modifiedAt": "Data di modifica", - "createdAt": "Data di creazione", - "size": "Dimensione", - "favoriteDate": "Data preferito", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nuovo" - }, - "dateBucket": { - "today": "Oggi", - "last7days": "Ultimi 7 giorni", - "last30days": "Ultimi 30 giorni" - }, - "groups": { - "title": "Gestisci gruppi", - "create_button": "Crea gruppo", - "create_dialog_title": "Nuovo gruppo", - "edit_dialog_title": "Rinomina gruppo", - "name_label": "Nome", - "name_placeholder": "ingegneria", - "description_label": "Descrizione (opzionale)", - "members_section": "Membri", - "add_member_placeholder": "Aggiungi un utente o un gruppo…", - "no_members": "Nessun membro al momento.", - "remove_member": "Rimuovi", - "delete_group": "Elimina gruppo", - "delete_confirm": "Eliminare il gruppo \"{name}\"? Le autorizzazioni che fanno riferimento a questo gruppo saranno revocate.", - "empty_state": "Nessun gruppo al momento.", - "load_more": "Carica altro", - "back_to_list": "Indietro", - "loading": "Caricamento…", - "virtual_badge": "Sistema", - "member_count_zero": "Nessun membro", - "member_count_one": "1 membro", - "member_count_other": "{count} membri", - "delete_confirm_label": "Digita il nome del gruppo per confermare:", - "delete_confirm_mismatch": "Digita esattamente il nome del gruppo per confermare.", - "virtual_internal_name": "Interno", - "members_loading": "Caricamento membri…", - "members_empty": "Nessun membro", - "virtual_internal_explanation": "Ogni utente interno su questo server" - }, - "myshares": { - "copyLink": "Copia link", - "deleteLink": "Elimina link", - "notifyByEmail": "Notifica via email", - "notifyFailed": "Impossibile inviare la notifica.", - "notifyGroupMembers": "Notifica i membri del gruppo", - "notifyRateLimited": "Troppe notifiche per questo destinatario — riprova più tardi.", - "removeAccess": "Rimuovi accesso", - "resendInvitation": "Reinvia email di invito" - }, - "sort": { - "asc": "crescente", - "desc": "decrescente" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", + "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nApri OxiCloud per vedere la tua nuova condivisione:\n{{login_link}}\n\nPotresti avere altre nuove condivisioni da {{inviter}} — accedi per vedere tutti gli elementi condivisi con te.\n\n— OxiCloud\n\nRicevi questo messaggio perché hai un account OxiCloud e la preferenza di notifica delle condivisioni è attiva. Puoi disattivarla dal tuo profilo (Avvisami via email quando qualcuno condivide con me)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Sistema di archiviazione cloud minimalista" + }, + "nav": { + "files": "File", + "shared": "Condivisioni", + "recent": "Recenti", + "favorites": "Preferiti", + "photos": "Foto", + "music": "Musica", + "trash": "Cestino", + "sharedwithme": "Condivisi con me", + "profile": "Profilo", + "shared_with_me": "Condivisi con me" + }, + "photos": { + "empty_state": "Nessuna foto ancora", + "empty_hint": "Carica immagini o video per vederli qui", + "items_selected": "selezionati", + "view_daily": "Giorno", + "view_monthly": "Mese", + "view_yearly": "Anno", + "group_by": "Raggruppa per" + }, + "music": { + "create_playlist": "Crea Playlist", + "playlists": "Playlist", + "no_playlists": "Nessuna playlist", + "select_playlist": "Seleziona una playlist", + "select_hint": "Scegli una playlist dalla barra laterale o creane una nuova", + "add_tracks": "Aggiungi Tracce", + "no_tracks": "Nessuna traccia in questa playlist", + "unknown_artist": "Artista Sconosciuto", + "unknown_title": "Sconosciuto", + "confirm_delete": "Eliminare questa playlist?", + "playlist_name": "Nome playlist", + "create": "Crea", + "delete": "Elimina", + "share": "Condividi", + "edit": "Modifica", + "play_all": "Riproduci Tutto", + "shuffle": "Casuale", + "repeat": "Ripeti", + "repeat_one": "Ripeti Una", + "queue": "Coda", + "queue_empty": "Coda vuota", + "not_playing": "Non in riproduzione", + "play": "Riproduci", + "pause": "Pausa", + "previous": "Precedente", + "next": "Successivo", + "volume": "Volume", + "mute": "Muto", + "unmute": "Attiva audio", + "title": "Titolo", + "artist": "Artista", + "album": "Album", + "tracks": "tracce", + "add": "Aggiungi", + "added": "Aggiunto!", + "added_to_playlist": "aggiunto alla playlist", + "add_to_playlist": "Aggiungi alla playlist", + "load_error": "Errore nel caricamento delle playlist", + "add_error": "Impossibile aggiungere le tracce", + "no_playlists_yet": "Nessuna playlist ancora. Creane una prima!", + "selected_files": "Selezionati:", + "error": "Errore", + "search_audio": "Cerca file audio…", + "no_audio_files": "Nessun file audio trovato", + "selected": "selezionati", + "loading": "Caricamento…", + "search_error": "Impossibile caricare i file audio", + "adding": "Aggiunta in corso…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "Precedente" + }, + "actions": { + "search": "Cerca file...", + "new_folder": "Nuova cartella", + "upload": "Carica", + "upload_files": "Carica file", + "upload_folder": "Carica cartella", + "upload.uploading": "Caricamento...", + "upload.complete": "{count} / {total} caricati", + "upload.files": "file", + "rename": "Rinomina", + "move": "Sposta in...", + "move_to": "Sposta in", + "delete": "Elimina", + "download": "Scarica", + "view": "Visualizza", + "cancel": "Annulla", + "confirm": "Conferma", + "share": "Condividi", + "favorite": "Aggiungi ai preferiti", + "unfavorite": "Rimuovi dai preferiti", + "copy": "Copia", + "notify": "Notifica", + "send": "Invia", + "clear_recent": "Cancella recenti", + "logout": "Disconnetti", + "create": "Crea", + "search_btn": "Cerca", + "close": "Chiudi", + "delete_permanently": "Elimina definitivamente", + "empty_trash": "Svuota il cestino", + "open_parent_folder": "Vai alla cartella padre", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Aspetto", + "about": "Informazioni su OxiCloud", + "about_description": "Piattaforma di archiviazione cloud realizzata con Rust & Architettura Pulita. Veloce, sicura e privata.", + "admin_panel": "Pannello di amministrazione", + "profile": "Il mio profilo", + "role_user": "Utente", + "theme": { + "light": "Chiaro", + "dark": "Scuro", + "auto": "Come il sistema" + }, + "manage_groups": "Gestisci gruppi", + "admin": "Amministratore" + }, + "share": { + "dialogTitle": "Link di condivisione", + "linkLabel": "Link di condivisione:", + "copyLink": "Copia", + "permissions": "Permessi:", + "permissionRead": "Lettura", + "permissionWrite": "Scrittura", + "permissionReshare": "Ricondivisione", + "password": "Protezione password:", + "generatePassword": "Genera", + "expiration": "Data di scadenza:", + "update": "Aggiorna condivisione", + "remove": "Rimuovi condivisione", + "notifyTitle": "Invia notifica", + "notifyEmailLabel": "Indirizzo email:", + "notifyMessageLabel": "Messaggio (opzionale):", + "notifySend": "Invia notifica", + "shareWithOthers": "Condividi con altri", + "sharePublicly": "Condividi pubblicamente", + "shareSettings": "Impostazioni di condivisione", + "shareCopied": "Link copiato negli appunti", + "shareCreated": "Link di condivisione creato con successo", + "shareUpdated": "Impostazioni di condivisione aggiornate con successo", + "shareRemoved": "Condivisione rimossa con successo", + "inviteByEmail": "Invita via email — verrà inviato un invito", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Copia", + "copy_failed": "Could not copy link", + "download": "Scarica", + "files": "File", + "folders": "Cartelle", + "link_name": "Link name (optional)", + "notifyByEmail": "Notifica via email", + "revoke": "Remove", + "role_label": "Ruolo" + }, + "share_dialogTitle": "Link di condivisione", + "share_linkLabel": "Link di condivisione:", + "share_copyLink": "Copia", + "share_permissions": "Permessi:", + "share_permissionRead": "Lettura", + "share_permissionWrite": "Scrittura", + "share_permissionReshare": "Ricondivisione", + "share_password": "Protezione password:", + "share_generatePassword": "Genera", + "share_expiration": "Data di scadenza:", + "share_update": "Aggiorna condivisione", + "share_remove": "Rimuovi condivisione", + "share_notifyTitle": "Invia notifica", + "share_notifyEmailLabel": "Indirizzo email:", + "share_notifyMessageLabel": "Messaggio (opzionale):", + "share_notifySend": "Invia notifica", + "shared": { + "backToFiles": "Torna ai file", + "pageTitle": "Risorse condivise", + "pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", + "filterType": "Tipo:", + "filterAll": "Tutti", + "filterFiles": "File", + "filterFolders": "Cartelle", + "sortBy": "Ordina per:", + "sortByName": "Nome", + "sortByDate": "Data di condivisione", + "sortByExpiration": "Scadenza", + "search": "Cerca", + "colName": "Nome", + "colType": "Tipo", + "colDateShared": "Data di condivisione", + "colExpiration": "Scadenza", + "colPermissions": "Permessi", + "colPassword": "Password", + "colActions": "Azioni", + "emptyStateTitle": "Ancora nessuna risorsa condivisa", + "emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", + "goToFiles": "Vai ai file", + "typeFile": "File", + "typeFolder": "Cartella", + "noExpiration": "Nessuna scadenza", + "hasPassword": "Sì", + "noPassword": "No", + "editShare": "Modifica condivisione", + "notifyShare": "Notifica a qualcuno", + "copyLink": "Copia link", + "removeShare": "Rimuovi condivisione", + "linkCopied": "Link copiato negli appunti!", + "linkCopyFailed": "Impossibile copiare il link", + "itemUpdated": "Impostazioni di condivisione aggiornate con successo", + "itemRemoved": "Condivisione rimossa con successo", + "invalidEmail": "Inserisci un indirizzo email valido", + "notificationSent": "Notifica inviata con successo", + "notificationFailed": "Impossibile inviare la notifica", + "shared_backToFiles": "Torna ai file", + "shared_pageTitle": "Risorse condivise", + "shared_pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", + "shared_filterType": "Tipo:", + "shared_filterAll": "Tutti", + "shared_filterFiles": "File", + "shared_filterFolders": "Cartelle", + "shared_sortBy": "Ordina per:", + "shared_sortByName": "Nome", + "shared_sortByDate": "Data di condivisione", + "shared_sortByExpiration": "Scadenza", + "shared_search": "Cerca", + "shared_colName": "Nome", + "shared_colType": "Tipo", + "shared_colDateShared": "Data di condivisione", + "shared_colExpiration": "Scadenza", + "shared_colPermissions": "Permessi", + "shared_colPassword": "Password", + "shared_colActions": "Azioni", + "shared_emptyStateTitle": "Ancora nessuna risorsa condivisa", + "shared_emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", + "shared_goToFiles": "Vai ai file", + "shared_typeFile": "File", + "shared_typeFolder": "Cartella", + "shared_noExpiration": "Nessuna scadenza", + "shared_hasPassword": "Sì", + "shared_noPassword": "No", + "shared_editShare": "Modifica condivisione", + "shared_notifyShare": "Notifica a qualcuno", + "shared_copyLink": "Copia link", + "shared_removeShare": "Rimuovi condivisione", + "shared_linkCopied": "Link copiato negli appunti!", + "shared_linkCopyFailed": "Impossibile copiare il link", + "shared_itemUpdated": "Impostazioni di condivisione aggiornate con successo", + "shared_itemRemoved": "Condivisione rimossa con successo", + "shared_invalidEmail": "Inserisci un indirizzo email valido", + "shared_notificationSent": "Notifica inviata con successo", + "shared_notificationFailed": "Impossibile inviare la notifica" + }, + "files": { + "name": "Nome", + "type": "Tipo", + "size": "Dimensione", + "modified": "Modificato", + "no_files": "Nessun file in questa cartella", + "empty_hint": "Carica file o crea cartelle per iniziare", + "loading": "Caricamento file…", + "view_grid": "Visualizzazione griglia", + "view_list": "Visualizzazione elenco", + "file_types": { + "document": "Documento", + "image": "Immagine", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Testo", + "folder": "Cartella", + "spreadsheet": "Foglio di calcolo", + "presentation": "Presentazione", + "archive": "Archivio", + "installer": "Programma di installazione", + "code": "Codice" + }, + "owner": "Proprietario", + "add_favorites": "Aggiungi ai preferiti", + "added_favorites": "Aggiunto ai preferiti", + "col_name": "Nome", + "col_owner": "Proprietario", + "col_size": "Dimensione", + "col_type": "Tipo", + "copy": "Copia", + "edit": "Modifica", + "file": "File", + "folder": "Cartella", + "new_folder": "Nuova cartella", + "share": "Condividi", + "view": "Visualizza" + }, + "dialogs": { + "rename_folder": "Rinomina cartella", + "rename_file": "Rinomina file", + "new_name": "Nuovo nome", + "new_folder_title": "Nuova cartella", + "folder_name": "Nome cartella", + "folder_placeholder": "La mia cartella", + "rename_title": "Rinomina", + "move_file": "Sposta file", + "move_folder": "Sposta cartella", + "select_destination": "Seleziona cartella di destinazione:", + "root": "Root", + "delete_confirmation": "Sei sicuro di voler eliminare", + "and_contents": "e tutto il suo contenuto", + "no_undo": "Questa azione non può essere annullata", + "confirm_title": "Conferma azione", + "confirm_delete": "Sposta nel cestino", + "confirm_delete_file": "Sei sicuro di voler spostare il file \"{{name}}\" nel cestino?", + "confirm_delete_folder": "Sei sicuro di voler spostare la cartella \"{{name}}\" e tutto il suo contenuto nel cestino?", + "confirm_permanent_delete": "Elimina definitivamente", + "confirm_permanent_delete_msg": "Sei sicuro di voler eliminare definitivamente questo elemento? Questa azione non può essere annullata.", + "confirm_empty_trash": "Svuota il cestino", + "confirm_delete_share": "Elimina link di condivisione", + "confirm_delete_share_msg": "Sei sicuro di voler eliminare questo link di condivisione?", + "share_file": "Condividi File", + "share_folder": "Condividi Cartella", + "existing_shares": "Condivisioni Esistenti", + "share_options": "Opzioni di Condivisione", + "password": "Password", + "expiration": "Scadenza", + "permissions": "Permessi", + "generated_link": "Link Generato", + "notify": "Invia Notifica", + "recipient": "Destinatario", + "message": "Messaggio", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Sposta nella cartella home" + }, + "dropzone": { + "drag_files": "Trascina i file qui o clicca per selezionare", + "drop_files": "Rilascia i file per caricarli" + }, + "permissions": { + "read": "Lettura", + "write": "Scrittura", + "reshare": "Ricondividi" + }, + "errors": { + "file_not_found": "File non trovato", + "folder_not_found": "Cartella non trovata", + "delete_error": "Errore durante l'eliminazione", + "upload_error": "Errore durante il caricamento del file", + "rename_error": "Errore durante la rinomina", + "move_error": "Errore durante lo spostamento", + "empty_name": "Il nome non può essere vuoto", + "name_exists": "Un file o una cartella con quel nome esiste già", + "generic_error": "Si è verificato un errore", + "group_name_invalid": "Il nome del gruppo deve rispettare il formato del prefisso email (lettere, cifre, punto, trattino, trattino basso; 1–64 caratteri).", + "group_cycle": "Questo membro creerebbe un riferimento circolare tra gruppi.", + "group_depth_exceeded": "Questa profondità di annidamento supera il massimo consentito (8).", + "group_virtual_immutable": "Il gruppo «Internal» è gestito dal sistema e non può essere modificato.", + "group_not_found": "Gruppo non trovato.", + "group_name_taken": "Un gruppo con questo nome esiste già." + }, + "breadcrumb": { + "home": "Home" + }, + "trash": { + "empty_trash": "Svuota il cestino", + "empty_state": "Il cestino è vuoto", + "original_location": "Posizione originale", + "deleted_date": "Data di eliminazione", + "remaining": "Rimanente", + "actions": "Azioni", + "restore": "Ripristina", + "delete_permanently": "Elimina definitivamente", + "empty_confirm": "Sei sicuro di voler svuotare il cestino? Questa operazione eliminerà definitivamente tutti gli elementi.", + "groupby": { + "remaining_days": "Giorni rimanenti", + "trashed_time": "Data di eliminazione" + }, + "delete": "Elimina definitivamente", + "empty_action": "Svuota il cestino" + }, + "daysRemaining": { + "expired": "Scaduto", + "today": "Oggi", + "tomorrow": "Domani", + "inDays": "{{count}} giorni" + }, + "expiryChip": { + "never": "Non scade mai", + "expired": "Scaduto", + "today": "Scade oggi", + "tomorrow": "Scade domani", + "inDays": "Scade tra {{count}} giorni", + "onDate": "Scade il {{date}}" + }, + "auth": { + "login_title": "Accedi", + "username": "Nome utente", + "username_placeholder": "Inserisci il tuo nome utente", + "login_identifier": "Nome utente o email", + "login_identifier_placeholder": "Inserisci il tuo nome utente o email", + "password": "Password", + "password_placeholder": "Inserisci la tua password", + "login_button": "Accedi", + "no_account": "Non hai un account?", + "register": "Registrati", + "admin_setup": "È la prima volta?", + "setup": "Configura amministratore", + "register_title": "Crea account", + "email": "Email", + "email_placeholder": "Inserisci la tua email", + "confirm_password": "Conferma password", + "confirm_password_placeholder": "Conferma la tua password", + "register_button": "Crea account", + "have_account": "Hai già un account?", + "login": "Accedi", + "setup_title": "Configurazione iniziale", + "setup_step1": "Amministratore", + "setup_step2": "Sistema", + "setup_step3": "Completa", + "admin_username": "Nome utente amministratore", + "admin_email": "Email amministratore", + "admin_password": "Password amministratore", + "create_admin": "Crea amministratore", + "back_to_login": "Già configurato?", + "admin_success": "Account amministratore creato con successo! Ora puoi accedere.", + "account_success": "Account creato con successo! Ora puoi accedere.", + "passwords_mismatch": "Le password non corrispondono", + "admin_create_error": "Errore durante la creazione dell'account amministratore", + "or": "o", + "sso_login": "Accedi con SSO", + "sso_login_provider": "Accedi con {{provider}}", + "magicLinkHint": "Niente password? Inserisci la tua email e ti invieremo un link di accesso monouso.", + "magicLinkEmailLabel": "Indirizzo email", + "magicLinkEmailPlaceholder": "tu@esempio.com", + "magicLinkSubmit": "Invia link di accesso", + "magicLinkSent": "Se esiste un account per questa email, è stato inviato un link di accesso. Controlla la tua casella di posta.", + "magicLinkUnavailable": "L'accesso tramite email non è disponibile su questo server.", + "magicLinkNetworkError": "Impossibile raggiungere il server: {{message}}", + "magicLinkToggle": "Nessuna password? Ricevi un link via e-mail", + "passwordsMatch": "Le password corrispondono", + "capsLock": "Bloc Maiusc attivo", + "caps_lock": "Bloc Maiusc attivo", + "magic_email_label": "Indirizzo email", + "magic_hint": "Niente password? Inserisci la tua email e ti invieremo un link di accesso monouso.", + "magic_unavailable": "L'accesso tramite email non è disponibile su questo server.", + "passwords_match": "Le password corrispondono", + "sign_in": "Accedi" + }, + "storage": { + "title": "Archiviazione", + "calculating": "Calcolo in corso...", + "used": "{{percentage}}% utilizzato ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Questo tipo di file non può essere visualizzato in anteprima.", + "download_file": "Scarica file", + "zoom_in": "Ingrandisci", + "zoom_out": "Riduci", + "zoom_reset": "Reimposta zoom" + }, + "language_selector": { + "title": "Benvenuto!", + "subtitle": "Seleziona la tua lingua per continuare", + "continue": "Continua", + "languages": { + "en": "Inglese", + "es": "Spagnolo", + "zh": "Cinese", + "fa": "Persiano", + "fr": "Francese", + "de": "Tedesco", + "pt": "Portoghese", + "it": "Italiano", + "ar": "العربية", + "hi": "हिन्दी", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Ancora nessun preferito", + "empty_hint": "Aggiungi file o cartelle ai preferiti per inserirli qui", + "add": "Aggiungi ai preferiti", + "remove": "Rimuovi dai preferiti", + "added_title": "Aggiunto ai preferiti", + "added_msg": "aggiunto ai preferiti", + "removed_title": "Rimosso dai preferiti", + "removed_msg": "rimosso dai preferiti" + }, + "recent": { + "title": "Recenti", + "clear": "Cancella recenti", + "accessed": "Accesso", + "empty_state": "Nessun file recente", + "empty_hint": "I file che apri appariranno qui", + "loadMore": "Carica altri" + }, + "notifications": { + "file_renamed": "File rinominato", + "file_renamed_to": "File rinominato in \"{{name}}\"", + "folder_renamed": "Cartella rinominata", + "folder_renamed_to": "Cartella rinominata in \"{{name}}\"", + "file_uploaded": "File caricato", + "file_deleted": "File spostato nel cestino", + "folder_deleted": "Cartella spostata nel cestino", + "item_deleted_permanently": "Elemento eliminato definitivamente", + "trash_emptied": "Cestino svuotato con successo", + "empty": "No notifications", + "title": "Notifications", + "link_created": "Link creato", + "share_success": "Link di condivisione creato con successo", + "upload_files_section_title": "Caricamento non disponibile qui", + "upload_files_section_body": "Vai alla sezione File per caricare i file" + }, + "batch": { + "one_selected": "1 elemento selezionato", + "n_selected": "{{count}} elementi selezionati", + "confirm_delete": "Sei sicuro di voler spostare {{count}} elementi nel cestino?", + "move_title": "Sposta {{count}} elemento/i", + "add_favorites": "Aggiungi ai preferiti", + "move_copy": "Sposta o copia" + }, + "admin": { + "page_title": "Pannello di Amministrazione", + "back_to_app": "Torna a OxiCloud", + "loading": "Caricamento…", + "access_denied": "Accesso Negato", + "access_denied_desc": "Privilegi di amministratore necessari.", + "sign_in": "Accedi", + "tab_dashboard": "Dashboard", + "tab_users": "Utenti", + "tab_oidc": "SSO / OIDC", + "total_users": "Utenti Totali", + "active_users": "Utenti Attivi", + "admins": "Amministratori", + "version": "Versione", + "storage_overview": "Panoramica Archiviazione", + "used": "Usato", + "total_quota": "Quota Totale", + "usage_pct": "Utilizzo %", + "users_over_80": "Utenti >80% quota", + "users_over_quota": "Utenti oltre la quota", + "system": "Sistema", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quote", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "active": "Attivo", + "off": "Spento", + "allow_registration": "Consenti registrazione pubblica", + "registration_warning": "La registrazione pubblica è disabilitata. Solo gli admin possono creare utenti.", + "user_management": "Gestione Utenti", + "create_user": "Crea Utente", + "col_user": "Utente", + "col_role": "Ruolo", + "col_auth": "Auth", + "col_status": "Stato", + "col_storage": "Archiviazione", + "col_last_login": "Ultimo Accesso", + "col_actions": "Azioni", + "loading_users": "Caricamento utenti…", + "failed_load_users": "Impossibile caricare", + "no_users_found": "Nessun utente trovato", + "showing_users": "Mostrando {{from}}-{{to}} di {{total}}", + "prev": "Precedente", + "next": "Successivo", + "inactive": "Inattivo", + "you_badge": "(tu)", + "local": "Locale", + "never": "Mai", + "just_now": "Proprio adesso", + "minutes_ago": "{{n}}min fa", + "hours_ago": "{{n}}h fa", + "days_ago": "{{n}}g fa", + "edit_quota_title": "Modifica quota", + "reset_password_title": "Reimposta password", + "toggle_role_title": "Cambia ruolo", + "deactivate_title": "Disattiva", + "activate_title": "Attiva", + "delete_title": "Elimina", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "Abilita autenticazione SSO", + "provider_name": "Nome Provider", + "issuer_url": "URL Emittente", + "issuer_url_hint": "URL dell'emittente OpenID Connect", + "auto_discover": "Auto-scoperta", + "discovering": "Scoperta…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Lascia vuoto per mantenere il valore", + "secret_configured": "Un client secret è già configurato", + "callback_url": "URL di Callback", + "callback_url_hint": "(registra nel tuo IdP)", + "advanced_settings": "Impostazioni Avanzate", + "scopes": "Scopes", + "auto_provision": "Provisioning automatico degli utenti", + "admin_groups": "Gruppi Admin", + "admin_groups_hint": "Nomi di gruppi OIDC separati da virgola", + "disable_password": "Disabilita accesso con password (solo OIDC)", + "password_warning": "Questo impedirà TUTTI gli accessi tramite password!", + "test_btn": "Test", + "save_btn": "Salva", + "saving": "Salvataggio…", + "settings_saved": "Impostazioni salvate — OIDC ora è {{status}}", + "quota_modal_title": "Aggiorna Quota", + "quota_user_label": "Utente:", + "new_quota": "Nuova Quota", + "quota_unlimited_hint": "0 per illimitato", + "cancel": "Annulla", + "create_user_title": "Crea Nuovo Utente", + "username_label": "Nome utente", + "username_placeholder": "mariorossi", + "username_hint": "3–32 caratteri", + "password_label": "Password", + "password_placeholder": "Min 8 caratteri", + "email_label": "Email", + "email_optional": "(facoltativo)", + "email_placeholder": "utente@esempio.com (auto-generata se vuoto)", + "role_label": "Ruolo", + "role_user": "Utente", + "role_admin": "Admin", + "quota_label": "Quota", + "creating": "Creazione…", + "reset_pw_title": "Reimposta Password", + "new_password_label": "Nuova Password", + "resetting": "Reimpostazione…", + "reset_btn": "Reimposta", + "confirm_role_change": "Cambiare ruolo a {{role}}?", + "confirm_deactivate": "Sei sicuro di voler disattivare questo utente?", + "confirm_activate": "Sei sicuro di voler attivare questo utente?", + "confirm_delete_user": "ELIMINARE l'utente \"{{name}}\"? Azione irreversibile!", + "confirm_action": "Conferma Azione", + "confirm_yes": "Conferma", + "confirm_no": "Annulla", + "error_username_short": "Il nome utente deve avere almeno 3 caratteri", + "error_password_short": "La password deve avere almeno 8 caratteri", + "error_generic": "Fallito", + "error_network": "Errore di rete: {{message}}", + "error_create_user": "Impossibile creare l'utente", + "tab_storage": "Archiviazione", + "storage_title": "Configurazione archiviazione", + "storage_current_backend": "Backend corrente", + "storage_total_blobs": "Blob totali", + "storage_total_size": "Dimensione totale", + "storage_dedup_ratio": "Rapporto deduplicazione", + "storage_backend": "Backend", + "storage_local": "Locale", + "storage_s3": "Compatibile S3", + "storage_provider_preset": "Preset fornitore", + "storage_preset_custom": "Personalizzato", + "storage_endpoint_url": "URL endpoint", + "storage_endpoint_hint": "Lasciare vuoto per AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Regione", + "storage_access_key": "Chiave di accesso", + "storage_secret_key": "Chiave segreta", + "storage_secret_configured": "Chiave configurata", + "storage_key_placeholder": "Inserisci nuova chiave", + "storage_path_style": "Forza stile percorso", + "storage_path_style_hint": "Richiesto per MinIO e alcuni servizi compatibili S3", + "storage_test_connection": "Testa connessione", + "storage_test_success": "Connessione riuscita", + "storage_test_failure": "Connessione fallita", + "storage_save": "Salva configurazione", + "storage_saved": "Configurazione salvata", + "storage_migration": "Migrazione dati", + "storage_migration_coming_soon": "Strumenti di migrazione in arrivo", + "migration_status_label": "Stato migrazione", + "migration_start": "Avvia migrazione", + "migration_pause": "Pausa", + "migration_resume": "Riprendi", + "migration_verify": "Verifica", + "migration_complete": "Completa", + "migration_started": "Migrazione avviata", + "migration_paused_msg": "Migrazione in pausa", + "migration_resumed_msg": "Migrazione ripresa", + "migration_completed_msg": "Migrazione completata con successo", + "migration_verifying": "Verifica in corso...", + "migration_verify_passed": "Verifica superata", + "migration_verify_failed": "Verifica fallita", + "migration_failed_blobs": "Blob falliti", + "testing": "Test in corso...", + "smtp_disabled": "Disabilitato (host non impostato)", + "smtp_enabled": "Abilitato", + "smtp_enabled_label": "Stato", + "smtp_intro": "SMTP è configurato esclusivamente tramite variabili d'ambiente (OXICLOUD_SMTP_*). I valori sottostanti sono letti dal server in esecuzione — per modificarli, modifica l'ambiente e riavvia OxiCloud.", + "smtp_not_configured": "SMTP non è configurato su questo server.", + "smtp_send_failed": "Invio non riuscito.", + "smtp_send_test": "Invia email di prova", + "smtp_sending": "Invio in corso…", + "smtp_sent": "Email di prova inviata.", + "smtp_server_code": "Risposta del server", + "smtp_test_intro": "Invia un messaggio diagnostico predefinito al destinatario indicato sotto e riporta la risposta del server SMTP, così puoi correlarla con i log del tuo relay.", + "smtp_test_missing_to": "Inserisci un indirizzo destinatario.", + "smtp_test_title": "Invia un'email di prova", + "smtp_test_to": "Indirizzo destinatario", + "smtp_title": "Email in uscita (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Amministratori", + "confirm_role": "Cambiare ruolo a {{role}}?", + "dashboard": "Dashboard", + "email": "Email", + "mig_complete": "Completa", + "mig_pause": "Pausa", + "mig_resume": "Riprendi", + "mig_verify_failed": "Verifica fallita", + "mig_verify_passed": "Verifica superata", + "mig_verifying": "Verifica in corso...", + "oidc_auto_provision": "Provisioning automatico degli utenti", + "oidc_callback": "URL di Callback", + "oidc_client_id": "Client ID", + "oidc_disable_pw": "Disabilita accesso con password (solo OIDC)", + "oidc_issuer": "URL Emittente", + "oidc_scopes": "Scopes", + "password": "Password", + "quotas": "Quote", + "reset_pw_for": "Nuova password per", + "role": "Ruolo", + "smtp_fail": "Invio non riuscito.", + "smtp_send": "Invia", + "smtp_test": "Invia email di prova", + "smtp_user_state": "Auth", + "status": "Stato", + "storage": "Archiviazione", + "storage_endpoint": "URL endpoint", + "storage_tab": "Archiviazione", + "time_min_ago": "{{n}} min fa", + "title": "Admin", + "user": "Utente", + "username": "Nome utente", + "users": "Utenti" + }, + "profile": { + "page_title": "Profilo", + "back_to_app": "Torna a OxiCloud", + "loading": "Caricamento…", + "not_authenticated": "Non Autenticato", + "not_authenticated_desc": "Accedi per visualizzare il tuo profilo.", + "sign_in": "Accedi", + "role_admin": "Amministratore", + "role_user": "Utente", + "account_details": "Dettagli Account", + "username": "Nome utente", + "email": "Email", + "role": "Ruolo", + "last_login": "Ultimo accesso", + "storage": "Archiviazione", + "used": "Usato", + "quota": "Quota", + "usage": "Utilizzo", + "unlimited": "Illimitato", + "app_passwords": "Password Applicazione", + "app_pw_desc": "Genera password per client WebDAV, CalDAV e CardDAV. Ogni password viene mostrata una sola volta.", + "app_pw_label_placeholder": "Etichetta (es. Thunderbird, macOS)", + "generate": "Genera", + "generating": "Generazione…", + "new_password_for": "Nuova password per", + "copy_warning": "Copia questa password ora. Non potrai rivederla.", + "copy_to_clipboard": "Copia negli appunti", + "col_label": "Etichetta", + "col_created": "Creato", + "col_last_used": "Ultimo utilizzo", + "col_status": "Stato", + "active": "Attiva", + "revoked": "Revocata", + "revoke_title": "Revoca", + "no_app_passwords": "Nessuna password applicazione ancora.", + "client_sessions": "Sessioni client", + "client_sessions_desc": "Generate automaticamente quando connetti un client compatibile Nextcloud.", + "col_client": "Client", + "never": "Mai", + "just_now": "Proprio adesso", + "minutes_ago": "{{n}} min fa", + "hours_ago": "{{n}}h fa", + "days_ago": "{{n}} giorni fa", + "edit_profile": "Modifica profilo", + "edit_oidc_managed": "Per modificare le tue informazioni (nome, cognome, foto profilo, …), aggiornale presso il tuo identity provider. Le modifiche compariranno al prossimo accesso.", + "username_claim_hint": "Da 2 a 64 caratteri, lettere / cifre / punto / trattino / sottolineatura. Una volta scelto, il nome utente non può essere modificato (i client DAV/NextCloud dipendono da esso).", + "username_already_claimed": "Nome utente impostato e non modificabile (i client DAV/NextCloud dipendono da esso).", + "given_name": "Nome", + "family_name": "Cognome", + "notify_on_share": "Avvisami via email quando qualcuno condivide con me", + "notify_on_share_hint": "Se deselezionato, le condivisioni continueranno ad apparire nel tuo account — semplicemente non riceverai un'email a riguardo.", + "save_profile": "Salva modifiche", + "profile_saved": "Profilo aggiornato", + "profile_no_changes": "Nessuna modifica da salvare.", + "profile_save_failed": "Salvataggio non riuscito", + "username_taken_error": "Questo nome utente è già in uso.", + "username_immutable_error": "Il tuo nome utente è già impostato e non può essere cambiato qui. Contatta un amministratore se desideri rinominarlo.", + "change_password": "Cambia Password", + "current_password": "Password Attuale", + "new_password": "Nuova Password", + "min_8_chars": "Almeno 8 caratteri", + "confirm_password": "Conferma Nuova Password", + "update_password": "Aggiorna Password", + "updating": "Aggiornamento…", + "password_updated": "Password aggiornata con successo", + "passwords_no_match": "Le password non corrispondono", + "password_too_short": "La password deve avere almeno 8 caratteri", + "password_change_failed": "Impossibile cambiare la password", + "error_network": "Errore di rete: {{message}}", + "error_label_required": "Inserisci un'etichetta", + "error_create_pw": "Impossibile creare la password", + "confirm_revoke": "Revocare la password \"{{label}}\"? I client che la usano smetteranno di funzionare.", + "error_revoke": "Revoca fallita", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Le password non corrispondono" + }, + "upload": { + "uploading": "Caricamento in corso...", + "files": "file", + "complete": "{{count}} / {{total}} caricati" + }, + "storage_quota_exceeded": "Quota di archiviazione superata", + "sharedwithme": { + "pageTitle": "Condiviso con me", + "pageDescription": "File e cartelle che altri utenti hanno condiviso con te", + "emptyStateTitle": "Niente è ancora condiviso con te", + "emptyStateDesc": "Gli elementi condivisi con te da altri utenti appariranno qui", + "loadMore": "Carica altri", + "sharedBy": "Condiviso da", + "colName": "Nome", + "colType": "Tipo", + "colSharedBy": "Condiviso da", + "colDate": "Data condivisione", + "colPermissions": "Permessi" + }, + "groupby": { + "none": "Nessuno", + "title": "Raggruppa per", + "owner": "Proprietario", + "shareDate": "Data condivisione", + "type": "Tipo", + "type.folders": "Cartelle", + "accessedAt": "Data di accesso", + "modifiedAt": "Data di modifica", + "createdAt": "Data di creazione", + "size": "Dimensione", + "favoriteDate": "Data preferito", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nuovo", + "folders": "Cartelle" + }, + "dateBucket": { + "today": "Oggi", + "last7days": "Ultimi 7 giorni", + "last30days": "Ultimi 30 giorni", + "unknown": "Sconosciuto" + }, + "groups": { + "title": "Gestisci gruppi", + "create_button": "Crea gruppo", + "create_dialog_title": "Nuovo gruppo", + "edit_dialog_title": "Rinomina gruppo", + "name_label": "Nome", + "name_placeholder": "ingegneria", + "description_label": "Descrizione (opzionale)", + "members_section": "Membri", + "add_member_placeholder": "Aggiungi un utente o un gruppo…", + "no_members": "Nessun membro al momento.", + "remove_member": "Rimuovi", + "delete_group": "Elimina gruppo", + "delete_confirm": "Eliminare il gruppo \"{name}\"? Le autorizzazioni che fanno riferimento a questo gruppo saranno revocate.", + "empty_state": "Nessun gruppo al momento.", + "load_more": "Carica altro", + "back_to_list": "Indietro", + "loading": "Caricamento…", + "virtual_badge": "Sistema", + "member_count_zero": "Nessun membro", + "member_count_one": "1 membro", + "member_count_other": "{count} membri", + "delete_confirm_label": "Digita il nome del gruppo per confermare:", + "delete_confirm_mismatch": "Digita esattamente il nome del gruppo per confermare.", + "virtual_internal_name": "Interno", + "members_loading": "Caricamento membri…", + "members_empty": "Nessun membro", + "virtual_internal_explanation": "Ogni utente interno su questo server", + "create": "Crea gruppo", + "empty": "Nessun gruppo al momento.", + "members": "Membri" + }, + "myshares": { + "copyLink": "Copia link", + "deleteLink": "Elimina link", + "notifyByEmail": "Notifica via email", + "notifyFailed": "Impossibile inviare la notifica.", + "notifyGroupMembers": "Notifica i membri del gruppo", + "notifyRateLimited": "Troppe notifiche per questo destinatario — riprova più tardi.", + "removeAccess": "Rimuovi accesso", + "resendInvitation": "Reinvia email di invito", + "publicLinks": "Public links" + }, + "sort": { + "asc": "crescente", + "desc": "decrescente" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Audio", + "code": "Codice", + "text": "Testo" + }, + "common": { + "add": "Aggiungi", + "cancel": "Annulla", + "clear": "Clear", + "close": "Chiudi", + "confirm": "Conferma", + "copy": "Copia", + "create": "Crea", + "delete": "Elimina", + "download": "Scarica", + "load_more": "Carica altri", + "loading": "Caricamento…", + "next": "Successivo", + "no": "No", + "previous": "Precedente", + "remove": "Remove", + "rename": "Rinomina", + "save": "Salva", + "search": "Cerca", + "yes": "Sì" + }, + "device": { + "continue": "Continua", + "unknown": "Sconosciuto" + }, + "expiryBucket": { + "expired": "Scaduto", + "noExpiry": "Nessuna scadenza", + "today": "Oggi", + "tomorrow": "Domani" + }, + "nextcloud": { + "error_title": "Errore", + "sign_in_with": "Accedi con {{provider}}" + }, + "search": { + "size_label": "Dimensione", + "title": "Cerca", + "type": { + "audio": "Audio" + }, + "type_label": "Tipo" + }, + "sizeBucket": { + "folders": "Cartelle" + }, + "view": { + "grid": "Visualizzazione griglia", + "list": "Visualizzazione elenco" + } } diff --git a/frontend/static/locales/ja.json b/frontend/static/locales/ja.json index 66edf237..62b2f1b3 100644 --- a/frontend/static/locales/ja.json +++ b/frontend/static/locales/ja.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "このサインインリンクは無効になりました", - "expired_body": "リンクは期限切れか、すでに使用された可能性があります。新しいリンクをお送りできます — 数秒以内にメールが届きます。", - "resend_to": "{{email}} に新しいリンクを送信", - "generic_unavailable": "このサインインリンクは無効になりました。すでに使用されたか、期限が切れた可能性があります。ログインページから新しいリンクをリクエストしてください。", - "service_unavailable": "マジックリンクサインインは、このサーバーで有効になっていません。", - "internal_error": "サインイン中にエラーが発生しました。もう一度お試しください。", - "resend_failure": "リンクの送信中にエラーが発生しました。もう一度お試しください。", - "cross_browser_title": "このデバイスでサインインを続けますか?", - "cross_browser_body": "このサインインリンクを、リクエストしたものとは別のブラウザーまたはデバイスで開きました。", - "cross_browser_warning": "このリンクをあなたがリクエストしたのであれば、続行しても安全です。そうでない場合は、このページを閉じてください — 「続行」をクリックすると、他の人があなたのアカウントにサインインしてしまいます。", - "cross_browser_continue": "続行してサインイン", - "resend_confirmation_title": "受信トレイをご確認ください", - "resend_confirmation_body": "サインインリンクがアクティブなアカウントのものであれば、新しいリンクが今送信されました。受信トレイをご確認ください。", - "return_link": "OxiCloud に戻る" - }, - "email": { - "invitation": { - "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", - "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n以下のリンクをクリックして開いてください:\n{{link}}\n\nリンクは一度のみ有効で、{{ttl_hours}} 時間で期限切れになります。\nこの招待に心当たりがない場合は、このメッセージを無視していただいて結構です。\n\n— OxiCloud" - }, - "login": { - "subject": "OxiCloud にサインイン", - "body": "こんにちは、\n\n以下のリンクから OxiCloud にサインインしてください。リンクは一度のみ有効で、{{ttl_minutes}} 分で期限切れになります。リクエストしたものと同じデバイスで開いてください。\n\n{{link}}\n\nこのサインインリンクをリクエストしていない場合は、このメッセージを無視していただいて結構です — それ以上の操作は必要ありません。\n\n— OxiCloud" - }, - "kind_file": "ファイル", - "kind_folder": "フォルダー", - "english_fallback_divider": "--- 以下は英語版 ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "このサインインリンクは無効になりました", + "expired_body": "リンクは期限切れか、すでに使用された可能性があります。新しいリンクをお送りできます — 数秒以内にメールが届きます。", + "resend_to": "{{email}} に新しいリンクを送信", + "generic_unavailable": "このサインインリンクは無効になりました。すでに使用されたか、期限が切れた可能性があります。ログインページから新しいリンクをリクエストしてください。", + "service_unavailable": "マジックリンクサインインは、このサーバーで有効になっていません。", + "internal_error": "サインイン中にエラーが発生しました。もう一度お試しください。", + "resend_failure": "リンクの送信中にエラーが発生しました。もう一度お試しください。", + "cross_browser_title": "このデバイスでサインインを続けますか?", + "cross_browser_body": "このサインインリンクを、リクエストしたものとは別のブラウザーまたはデバイスで開きました。", + "cross_browser_warning": "このリンクをあなたがリクエストしたのであれば、続行しても安全です。そうでない場合は、このページを閉じてください — 「続行」をクリックすると、他の人があなたのアカウントにサインインしてしまいます。", + "cross_browser_continue": "続行してサインイン", + "resend_confirmation_title": "受信トレイをご確認ください", + "resend_confirmation_body": "サインインリンクがアクティブなアカウントのものであれば、新しいリンクが今送信されました。受信トレイをご確認ください。", + "return_link": "OxiCloud に戻る" + }, + "email": { + "invitation": { + "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", + "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n以下のリンクをクリックして開いてください:\n{{link}}\n\nリンクは一度のみ有効で、{{ttl_hours}} 時間で期限切れになります。\nこの招待に心当たりがない場合は、このメッセージを無視していただいて結構です。\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", - "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n新しい共有を確認するには OxiCloud を開いてください:\n{{login_link}}\n\n{{inviter}} さんから他にも新しい共有があるかもしれません — サインインしてあなたと共有されたすべての項目を確認してください。\n\n— OxiCloud\n\nOxiCloud のアカウントをお持ちで、共有通知の設定が有効になっているため、このメッセージが届いています。プロフィールでオフにできます(誰かが共有したときにメールで通知する)。" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "ミニマリストクラウドストレージシステム" - }, - "nav": { - "files": "ファイル", - "shared": "共有", - "recent": "最近", - "favorites": "お気に入り", - "photos": "写真", - "music": "音楽", - "trash": "ゴミ箱", - "sharedwithme": "自分と共有" - }, - "photos": { - "empty_state": "写真はまだありません", - "empty_hint": "画像や動画をアップロードするとここに表示されます", - "items_selected": "件選択中", - "view_daily": "日", - "view_monthly": "月", - "view_yearly": "年" - }, - "music": { - "create_playlist": "プレイリストを作成", - "playlists": "プレイリスト", - "no_playlists": "プレイリストがありません", - "select_playlist": "プレイリストを選択", - "select_hint": "サイドバーからプレイリストを選択するか、新しいものを作成してください", - "add_tracks": "トラックを追加", - "no_tracks": "このプレイリストにトラックがありません", - "unknown_artist": "不明なアーティスト", - "unknown_title": "不明", - "confirm_delete": "このプレイリストを削除しますか?", - "playlist_name": "プレイリスト名", - "create": "作成", - "delete": "削除", - "share": "共有", - "edit": "編集", - "play_all": "すべて再生", - "shuffle": "シャッフル", - "repeat": "リピート", - "repeat_one": "1曲リピート", - "queue": "キュー", - "queue_empty": "キューが空です", - "not_playing": "再生していません", - "play": "再生", - "pause": "一時停止", - "previous": "前へ", - "next": "次へ", - "volume": "音量", - "mute": "ミュート", - "unmute": "ミュート解除", - "title": "タイトル", - "artist": "アーティスト", - "album": "アルバム", - "tracks": "曲", - "add": "追加", - "added": "追加しました!", - "added_to_playlist": "プレイリストに追加しました", - "add_to_playlist": "プレイリストに追加", - "load_error": "プレイリストの読み込みエラー", - "add_error": "曲を追加できませんでした", - "no_playlists_yet": "プレイリストがありません。最初に作成してください!", - "selected_files": "選択中:", - "error": "エラー", - "search_audio": "オーディオファイルを検索…", - "no_audio_files": "オーディオファイルが見つかりません", - "selected": "件選択中", - "loading": "読み込み中…", - "search_error": "オーディオファイルを読み込めませんでした", - "adding": "追加中…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "ファイルを検索...", - "new_folder": "新しいフォルダ", - "upload": "アップロード", - "upload_files": "ファイルをアップロード", - "upload_folder": "フォルダをアップロード", - "upload.uploading": "アップロード中...", - "upload.complete": "{count} / {total} アップロード完了", - "upload.files": "ファイル", - "rename": "名前を変更", - "move": "移動先...", - "move_to": "移動先", - "delete": "削除", - "download": "ダウンロード", - "view": "表示", - "cancel": "キャンセル", - "confirm": "確認", - "share": "共有", - "favorite": "お気に入りに追加", - "unfavorite": "お気に入りから削除", - "copy": "コピー", - "notify": "通知", - "send": "送信", - "clear_recent": "最近をクリア", - "logout": "ログアウト", - "create": "作成", - "search_btn": "検索", - "close": "閉じる", - "delete_permanently": "完全に削除", - "empty_trash": "ゴミ箱を空にする", - "open_parent_folder": "親フォルダへ移動", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "外観", - "about": "OxiCloudについて", - "about_description": "RustとClean Architectureで構築されたクラウドストレージプラットフォーム。高速・安全・プライベート。", - "admin_panel": "管理パネル", - "profile": "マイプロフィール", - "role_user": "ユーザー", - "theme": { - "light": "ライト", - "dark": "ダーク", - "auto": "システムに合わせる" + "login": { + "subject": "OxiCloud にサインイン", + "body": "こんにちは、\n\n以下のリンクから OxiCloud にサインインしてください。リンクは一度のみ有効で、{{ttl_minutes}} 分で期限切れになります。リクエストしたものと同じデバイスで開いてください。\n\n{{link}}\n\nこのサインインリンクをリクエストしていない場合は、このメッセージを無視していただいて結構です — それ以上の操作は必要ありません。\n\n— OxiCloud" }, - "manage_groups": "グループを管理" + "kind_file": "ファイル", + "kind_folder": "フォルダー", + "english_fallback_divider": "--- 以下は英語版 ---" + } }, - "share": { - "dialogTitle": "共有リンク", - "linkLabel": "共有リンク:", - "copyLink": "コピー", - "permissions": "権限:", - "permissionRead": "読み取り", - "permissionWrite": "書き込み", - "permissionReshare": "再共有", - "password": "パスワード保護:", - "generatePassword": "生成", - "expiration": "有効期限:", - "update": "共有を更新", - "remove": "共有を削除", - "notifyTitle": "通知を送信", - "notifyEmailLabel": "メールアドレス:", - "notifyMessageLabel": "メッセージ(任意):", - "notifySend": "通知を送信", - "shareWithOthers": "他のユーザーと共有", - "sharePublicly": "公開共有", - "shareSettings": "共有設定", - "shareCopied": "リンクがクリップボードにコピーされました", - "shareCreated": "共有リンクが正常に作成されました", - "shareUpdated": "共有設定が正常に更新されました", - "shareRemoved": "共有が正常に削除されました", - "inviteByEmail": "メールで招待 — 招待を送信します", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "共有リンク", - "share_linkLabel": "共有リンク:", - "share_copyLink": "コピー", - "share_permissions": "権限:", - "share_permissionRead": "読み取り", - "share_permissionWrite": "書き込み", - "share_permissionReshare": "再共有", - "share_password": "パスワード保護:", - "share_generatePassword": "生成", - "share_expiration": "有効期限:", - "share_update": "共有を更新", - "share_remove": "共有を削除", - "share_notifyTitle": "通知を送信", - "share_notifyEmailLabel": "メールアドレス:", - "share_notifyMessageLabel": "メッセージ(任意):", - "share_notifySend": "通知を送信", - "shared": { - "backToFiles": "ファイルに戻る", - "pageTitle": "共有リソース", - "pageDescription": "共有ファイルとフォルダの管理", - "filterType": "種類:", - "filterAll": "すべて", - "filterFiles": "ファイル", - "filterFolders": "フォルダ", - "sortBy": "並び替え:", - "sortByName": "名前", - "sortByDate": "共有日", - "sortByExpiration": "有効期限", - "search": "検索", - "colName": "名前", - "colType": "種類", - "colDateShared": "共有日", - "colExpiration": "有効期限", - "colPermissions": "権限", - "colPassword": "パスワード", - "colActions": "操作", - "emptyStateTitle": "共有リソースはまだありません", - "emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", - "goToFiles": "ファイルへ移動", - "typeFile": "ファイル", - "typeFolder": "フォルダ", - "noExpiration": "期限なし", - "hasPassword": "あり", - "noPassword": "なし", - "editShare": "共有を編集", - "notifyShare": "通知する", - "copyLink": "リンクをコピー", - "removeShare": "共有を削除", - "linkCopied": "リンクがクリップボードにコピーされました!", - "linkCopyFailed": "リンクのコピーに失敗しました", - "itemUpdated": "共有設定が正常に更新されました", - "itemRemoved": "共有が正常に削除されました", - "invalidEmail": "有効なメールアドレスを入力してください", - "notificationSent": "通知が正常に送信されました", - "notificationFailed": "通知の送信に失敗しました", - "shared_backToFiles": "ファイルに戻る", - "shared_pageTitle": "共有リソース", - "shared_pageDescription": "共有ファイルとフォルダの管理", - "shared_filterType": "種類:", - "shared_filterAll": "すべて", - "shared_filterFiles": "ファイル", - "shared_filterFolders": "フォルダ", - "shared_sortBy": "並び替え:", - "shared_sortByName": "名前", - "shared_sortByDate": "共有日", - "shared_sortByExpiration": "有効期限", - "shared_search": "検索", - "shared_colName": "名前", - "shared_colType": "種類", - "shared_colDateShared": "共有日", - "shared_colExpiration": "有効期限", - "shared_colPermissions": "権限", - "shared_colPassword": "パスワード", - "shared_colActions": "操作", - "shared_emptyStateTitle": "共有リソースはまだありません", - "shared_emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", - "shared_goToFiles": "ファイルへ移動", - "shared_typeFile": "ファイル", - "shared_typeFolder": "フォルダ", - "shared_noExpiration": "期限なし", - "shared_hasPassword": "あり", - "shared_noPassword": "なし", - "shared_editShare": "共有を編集", - "shared_notifyShare": "通知する", - "shared_copyLink": "リンクをコピー", - "shared_removeShare": "共有を削除", - "shared_linkCopied": "リンクがクリップボードにコピーされました!", - "shared_linkCopyFailed": "リンクのコピーに失敗しました", - "shared_itemUpdated": "共有設定が正常に更新されました", - "shared_itemRemoved": "共有が正常に削除されました", - "shared_invalidEmail": "有効なメールアドレスを入力してください", - "shared_notificationSent": "通知が正常に送信されました", - "shared_notificationFailed": "通知の送信に失敗しました" - }, - "files": { - "name": "名前", - "type": "種類", - "size": "サイズ", - "modified": "更新日", - "no_files": "このフォルダにファイルはありません", - "empty_hint": "ファイルをアップロードするかフォルダを作成して始めましょう", - "loading": "ファイルを読み込み中…", - "view_grid": "グリッド表示", - "view_list": "リスト表示", - "file_types": { - "document": "ドキュメント", - "image": "画像", - "video": "動画", - "audio": "音声", - "pdf": "PDF", - "text": "テキスト", - "folder": "フォルダ", - "spreadsheet": "スプレッドシート", - "presentation": "プレゼンテーション", - "archive": "アーカイブ", - "installer": "インストーラー", - "code": "コード" - }, - "owner": "オーナー" - }, - "dialogs": { - "rename_folder": "フォルダ名を変更", - "rename_file": "ファイル名を変更", - "new_name": "新しい名前", - "new_folder_title": "新しいフォルダ", - "folder_name": "フォルダ名", - "folder_placeholder": "マイフォルダ", - "rename_title": "名前を変更", - "move_file": "ファイルを移動", - "move_folder": "フォルダを移動", - "select_destination": "移動先フォルダを選択:", - "select_this_folder": "このフォルダを選択", - "go_to_parent": ".. (親フォルダ)", - "no_subfolders": "サブフォルダなし", - "root": "ルート", - "delete_confirmation": "本当に削除しますか", - "and_contents": "およびすべての内容", - "no_undo": "この操作は元に戻せません", - "confirm_title": "操作の確認", - "confirm_delete": "ゴミ箱に移動", - "confirm_delete_file": "ファイル「{{name}}」をゴミ箱に移動しますか?", - "confirm_delete_folder": "フォルダ「{{name}}」とそのすべての内容をゴミ箱に移動しますか?", - "confirm_permanent_delete": "完全に削除", - "confirm_permanent_delete_msg": "このアイテムを完全に削除しますか?この操作は元に戻せません。", - "confirm_empty_trash": "ゴミ箱を空にする", - "confirm_delete_share": "共有リンクを削除", - "confirm_delete_share_msg": "この共有リンクを削除しますか?", - "share_file": "ファイルを共有", - "share_folder": "フォルダを共有", - "existing_shares": "既存の共有", - "share_options": "共有オプション", - "password": "パスワード", - "expiration": "有効期限", - "permissions": "権限", - "generated_link": "生成されたリンク", - "notify": "通知を送信", - "recipient": "宛先", - "message": "メッセージ", - "move_to_home": "ホームフォルダへ移動" - }, - "dropzone": { - "drag_files": "ファイルをここにドラッグするか、クリックして選択", - "drop_files": "ファイルをドロップしてアップロード" - }, - "permissions": { - "read": "読み取り", - "write": "書き込み", - "reshare": "再共有" - }, - "errors": { - "file_not_found": "ファイルが見つかりません", - "folder_not_found": "フォルダが見つかりません", - "delete_error": "削除エラー", - "upload_error": "ファイルのアップロードエラー", - "rename_error": "名前変更エラー", - "move_error": "移動エラー", - "empty_name": "名前を空にすることはできません", - "name_exists": "同じ名前のファイルまたはフォルダが既に存在します", - "generic_error": "エラーが発生しました", - "group_name_invalid": "グループ名はメールプレフィックス形式に一致している必要があります(文字、数字、ドット、ダッシュ、アンダースコア;1~64文字)。", - "group_cycle": "このメンバーはグループ間で循環参照を作成します。", - "group_depth_exceeded": "ネストの深さが許容されている最大値(8)を超えています。", - "group_virtual_immutable": "「Internal」グループはシステム管理であり、変更できません。", - "group_not_found": "グループが見つかりません。", - "group_name_taken": "この名前のグループはすでに存在します。" - }, - "breadcrumb": { - "home": "ホーム" - }, - "trash": { - "empty_trash": "ゴミ箱を空にする", - "empty_state": "ゴミ箱は空です", - "original_location": "元の場所", - "deleted_date": "削除日", - "remaining": "残り", - "actions": "操作", - "restore": "復元", - "delete_permanently": "完全に削除", - "empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。", - "groupby": { - "remaining_days": "残り日数", - "trashed_time": "削除日時" - } - }, - "daysRemaining": { - "expired": "期限切れ", - "today": "今日", - "tomorrow": "明日", - "inDays": "{{count}}日" - }, - "expiryChip": { - "never": "期限なし", - "expired": "期限切れ", - "today": "今日で期限切れ", - "tomorrow": "明日で期限切れ", - "inDays": "{{count}}日後に期限切れ", - "onDate": "{{date}}に期限切れ" - }, - "auth": { - "login_title": "サインイン", - "username": "ユーザー名", - "username_placeholder": "ユーザー名を入力", - "login_identifier": "ユーザー名またはメールアドレス", - "login_identifier_placeholder": "ユーザー名またはメールアドレスを入力", - "password": "パスワード", - "password_placeholder": "パスワードを入力", - "login_button": "サインイン", - "no_account": "アカウントをお持ちでないですか?", - "register": "登録", - "admin_setup": "初回ですか?", - "setup": "管理者をセットアップ", - "register_title": "アカウント作成", - "email": "メール", - "email_placeholder": "メールアドレスを入力", - "confirm_password": "パスワードの確認", - "confirm_password_placeholder": "パスワードを再入力", - "register_button": "アカウント作成", - "have_account": "既にアカウントをお持ちですか?", - "login": "サインイン", - "setup_title": "初期設定", - "setup_step1": "管理者", - "setup_step2": "システム", - "setup_step3": "完了", - "admin_username": "管理者ユーザー名", - "admin_email": "管理者メール", - "admin_password": "管理者パスワード", - "create_admin": "管理者を作成", - "back_to_login": "設定済みですか?", - "admin_success": "管理者アカウントが正常に作成されました!サインインできます。", - "account_success": "アカウントが正常に作成されました!サインインできます。", - "passwords_mismatch": "パスワードが一致しません", - "admin_create_error": "管理者アカウントの作成エラー", - "or": "または", - "sso_login": "SSOでサインイン", - "sso_login_provider": "{{provider}}でサインイン", - "magicLinkHint": "パスワードをお持ちでない方は、メールアドレスを入力するとワンタイムサインインリンクをお送りします。", - "magicLinkEmailLabel": "メールアドレス", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "サインインリンクを送信", - "magicLinkSent": "そのメールアドレスのアカウントが存在する場合、サインインリンクが送信されました。受信トレイをご確認ください。", - "magicLinkUnavailable": "このサーバーではメールでのサインインは利用できません。", - "magicLinkNetworkError": "サーバーに接続できませんでした: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "ストレージ", - "calculating": "計算中...", - "used": "{{percentage}}% 使用中 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "このファイル形式はプレビューできません。", - "download_file": "ファイルをダウンロード", - "zoom_in": "拡大", - "zoom_out": "縮小", - "zoom_reset": "ズームリセット" - }, - "language_selector": { - "title": "ようこそ!", - "subtitle": "続行するには言語を選択してください", - "continue": "続行", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ja": "日本語", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "お気に入りはまだありません", - "empty_hint": "ファイルやフォルダにスターを付けてお気に入りに追加", - "add": "お気に入りに追加", - "remove": "お気に入りから削除", - "added_title": "お気に入りに追加しました", - "added_msg": "お気に入りに追加しました", - "removed_title": "お気に入りから削除しました", - "removed_msg": "お気に入りから削除しました" - }, - "recent": { - "title": "最近", - "clear": "最近をクリア", - "accessed": "アクセス日", - "empty_state": "最近のファイルはありません", - "empty_hint": "開いたファイルがここに表示されます", - "loadMore": "さらに読み込む" - }, - "notifications": { - "file_renamed": "ファイル名を変更しました", - "file_renamed_to": "ファイル名を「{{name}}」に変更しました", - "folder_renamed": "フォルダ名を変更しました", - "folder_renamed_to": "フォルダ名を「{{name}}」に変更しました", - "file_uploaded": "ファイルをアップロードしました", - "file_deleted": "ファイルをゴミ箱に移動しました", - "folder_deleted": "フォルダをゴミ箱に移動しました", - "item_deleted_permanently": "アイテムを完全に削除しました", - "trash_emptied": "ゴミ箱を正常に空にしました", - "title": "通知", - "empty": "通知はありません", - "link_created": "リンクを作成しました", - "share_success": "共有リンクを正常に作成しました", - "upload_files_section_title": "ここではアップロードできません", - "upload_files_section_body": "ファイルをアップロードするには「ファイル」セクションに移動してください" - }, - "batch": { - "one_selected": "1件選択中", - "n_selected": "{{count}}件選択中", - "confirm_delete": "{{count}}件のアイテムをゴミ箱に移動しますか?", - "move_title": "{{count}}件のアイテムを移動", - "add_favorites": "お気に入りに追加", - "move_copy": "移動またはコピー" - }, - "admin": { - "page_title": "管理パネル", - "back_to_app": "OxiCloudに戻る", - "loading": "読み込み中…", - "access_denied": "アクセス拒否", - "access_denied_desc": "管理者権限が必要です。", - "sign_in": "サインイン", - "tab_dashboard": "ダッシュボード", - "tab_users": "ユーザー", - "tab_oidc": "SSO / OIDC", - "total_users": "総ユーザー数", - "active_users": "アクティブ", - "admins": "管理者", - "version": "バージョン", - "storage_overview": "ストレージ概要", - "used": "使用済み", - "total_quota": "合計クォータ", - "usage_pct": "使用率", - "users_over_80": "クォータ80%超", - "users_over_quota": "クォータ超過", - "system": "システム", - "auth_label": "認証", - "oidc_label": "OIDC", - "quotas_label": "クォータ", - "enabled": "有効", - "disabled": "無効", - "active": "アクティブ", - "off": "オフ", - "allow_registration": "公開セルフ登録を許可", - "registration_warning": "公開登録は無効です。管理者のみがユーザーを作成できます。", - "user_management": "ユーザー管理", - "create_user": "ユーザー作成", - "col_user": "ユーザー", - "col_role": "役割", - "col_auth": "認証", - "col_status": "ステータス", - "col_storage": "ストレージ", - "col_last_login": "最終ログイン", - "col_actions": "操作", - "loading_users": "ユーザーを読み込み中…", - "failed_load_users": "読み込み失敗", - "no_users_found": "ユーザーなし", - "showing_users": "{{from}}-{{to}} / {{total}} を表示", - "prev": "前へ", - "next": "次へ", - "inactive": "非アクティブ", - "you_badge": "(あなた)", - "local": "ローカル", - "never": "未ログイン", - "just_now": "たった今", - "minutes_ago": "{{n}}分前", - "hours_ago": "{{n}}時間前", - "days_ago": "{{n}}日前", - "edit_quota_title": "クォータを編集", - "reset_password_title": "パスワードリセット", - "toggle_role_title": "役割を切替", - "deactivate_title": "無効化", - "activate_title": "有効化", - "delete_title": "削除", - "sso_title": "シングルサインオン (OIDC / SSO)", - "enable_sso": "SSO認証を有効化", - "provider_name": "プロバイダー名", - "issuer_url": "発行者URL", - "issuer_url_hint": "OpenID Connect発行者URL", - "auto_discover": "自動検出", - "discovering": "検出中…", - "client_id": "クライアントID", - "client_secret": "クライアントシークレット", - "client_secret_placeholder": "現在の値を維持するには空に", - "secret_configured": "クライアントシークレット設定済み", - "callback_url": "コールバックURL", - "callback_url_hint": "(IdPに登録)", - "advanced_settings": "詳細設定", - "scopes": "スコープ", - "auto_provision": "初回ログイン時に自動プロビジョニング", - "admin_groups": "管理者グループ", - "admin_groups_hint": "カンマ区切りのOIDCグループ名", - "disable_password": "パスワードログイン無効化(OIDCのみ)", - "password_warning": "すべてのパスワードログインが無効に!", - "test_btn": "テスト", - "save_btn": "保存", - "saving": "保存中…", - "settings_saved": "設定が保存されました — OIDC: {{status}}", - "quota_modal_title": "ストレージクォータ更新", - "quota_user_label": "ユーザー:", - "new_quota": "新しいクォータ", - "quota_unlimited_hint": "0で無制限", - "cancel": "キャンセル", - "create_user_title": "新規ユーザー作成", - "username_label": "ユーザー名", - "username_placeholder": "taro", - "username_hint": "3〜32文字", - "password_label": "パスワード", - "password_placeholder": "8文字以上", - "email_label": "メール", - "email_optional": "(任意)", - "email_placeholder": "user@example.com(空なら自動生成)", - "role_label": "役割", - "role_user": "ユーザー", - "role_admin": "管理者", - "quota_label": "クォータ", - "creating": "作成中…", - "reset_pw_title": "パスワードリセット", - "new_password_label": "新しいパスワード", - "resetting": "リセット中…", - "reset_btn": "リセット", - "confirm_role_change": "役割を{{role}}に変更?", - "confirm_deactivate": "このユーザーを無効化しますか?", - "confirm_activate": "このユーザーを有効化しますか?", - "confirm_delete_user": "ユーザー「{{name}}」を削除?取り消せません!", - "confirm_action": "操作の確認", - "confirm_yes": "確認", - "confirm_no": "キャンセル", - "error_username_short": "ユーザー名は3文字以上", - "error_password_short": "パスワードは8文字以上", - "error_generic": "失敗", - "error_network": "ネットワークエラー: {{message}}", - "error_create_user": "ユーザー作成失敗", - "tab_storage": "ストレージ", - "storage_title": "ストレージ設定", - "storage_current_backend": "現在のバックエンド", - "storage_total_blobs": "総ブロブ数", - "storage_total_size": "合計サイズ", - "storage_dedup_ratio": "重複排除率", - "storage_backend": "バックエンド", - "storage_local": "ローカル", - "storage_s3": "S3互換", - "storage_provider_preset": "プロバイダープリセット", - "storage_preset_custom": "カスタム", - "storage_endpoint_url": "エンドポイントURL", - "storage_endpoint_hint": "AWS S3の場合は空欄のまま", - "storage_bucket": "バケット", - "storage_region": "リージョン", - "storage_access_key": "アクセスキー", - "storage_secret_key": "シークレットキー", - "storage_secret_configured": "キーが設定済み", - "storage_key_placeholder": "新しいキーを入力", - "storage_path_style": "パススタイルを強制", - "storage_path_style_hint": "MinIOおよび一部のS3互換サービスに必要", - "storage_test_connection": "接続テスト", - "storage_test_success": "接続成功", - "storage_test_failure": "接続失敗", - "storage_save": "設定を保存", - "storage_saved": "設定を保存しました", - "storage_migration": "データ移行", - "storage_migration_coming_soon": "移行ツールは近日公開予定", - "migration_status_label": "移行状況", - "migration_start": "移行を開始", - "migration_pause": "一時停止", - "migration_resume": "再開", - "migration_verify": "検証", - "migration_complete": "完了", - "migration_started": "移行を開始しました", - "migration_paused_msg": "移行を一時停止しました", - "migration_resumed_msg": "移行を再開しました", - "migration_completed_msg": "移行が正常に完了しました", - "migration_verifying": "検証中...", - "migration_verify_passed": "検証に合格", - "migration_verify_failed": "検証に失敗", - "migration_failed_blobs": "失敗したブロブ", - "testing": "テスト中...", - "smtp_disabled": "無効 (ホスト未設定)", - "smtp_enabled": "有効", - "smtp_enabled_label": "ステータス", - "smtp_intro": "SMTP は環境変数 (OXICLOUD_SMTP_*) でのみ設定します。以下の値は稼働中のサーバーから読み取られます — 変更するには環境を編集して OxiCloud を再起動してください。", - "smtp_not_configured": "このサーバーでは SMTP が設定されていません。", - "smtp_send_failed": "送信に失敗しました。", - "smtp_send_test": "テストメールを送信", - "smtp_sending": "送信中…", - "smtp_sent": "テストメールを送信しました。", - "smtp_server_code": "サーバーの応答", - "smtp_test_intro": "あらかじめ定義された診断メッセージを下記の宛先に送信し、SMTP サーバーの応答を表示します。これを使ってリレーのログと突き合わせて確認できます。", - "smtp_test_missing_to": "宛先アドレスを入力してください。", - "smtp_test_title": "テストメールを送信", - "smtp_test_to": "宛先アドレス", - "smtp_title": "送信メール (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "プロフィール", - "back_to_app": "OxiCloudに戻る", - "loading": "読み込み中…", - "not_authenticated": "未認証", - "not_authenticated_desc": "プロフィールを表示するにはサインインしてください。", - "sign_in": "サインイン", - "role_admin": "管理者", - "role_user": "ユーザー", - "account_details": "アカウント詳細", - "username": "ユーザー名", - "email": "メール", - "role": "役割", - "last_login": "最終ログイン", - "storage": "ストレージ", - "used": "使用済み", - "quota": "クォータ", - "usage": "使用率", - "unlimited": "無制限", - "app_passwords": "アプリパスワード", - "app_pw_desc": "WebDAV、CalDAV、CardDAVクライアント用のパスワードを生成します。各パスワードは一度だけ表示されます。", - "app_pw_label_placeholder": "ラベル(例:Thunderbird、macOS)", - "generate": "生成", - "generating": "生成中…", - "new_password_for": "新しいパスワード:", - "copy_warning": "このパスワードを今コピーしてください。再度表示できません。", - "copy_to_clipboard": "クリップボードにコピー", - "col_label": "ラベル", - "col_created": "作成日", - "col_last_used": "最終使用", - "col_status": "ステータス", - "active": "アクティブ", - "revoked": "失効済み", - "revoke_title": "失効", - "no_app_passwords": "アプリパスワードはまだありません。", - "client_sessions": "クライアントセッション", - "client_sessions_desc": "Nextcloud互換クライアント接続時に自動生成されます。", - "col_client": "クライアント", - "never": "未ログイン", - "just_now": "たった今", - "minutes_ago": "{{n}}分前", - "hours_ago": "{{n}}時間前", - "days_ago": "{{n}}日前", - "edit_profile": "プロフィールを編集", - "edit_oidc_managed": "情報(姓、名、プロフィール写真など)を変更するには、IDプロバイダーで更新してください。次回サインイン時に反映されます。", - "username_claim_hint": "2〜64文字、英数字 / ドット / ハイフン / アンダースコア。一度選択すると、ユーザー名は変更できません(DAV/NextCloudクライアントが依存します)。", - "username_already_claimed": "ユーザー名は設定済みで変更できません(DAV/NextCloudクライアントが依存します)。", - "given_name": "名", - "family_name": "姓", - "notify_on_share": "誰かが共有したときにメールで通知する", - "notify_on_share_hint": "チェックを外しても、共有はアカウントに表示されますが、メールでの通知は届きません。", - "save_profile": "変更を保存", - "profile_saved": "プロフィールを更新しました", - "profile_no_changes": "保存する変更はありません。", - "profile_save_failed": "保存に失敗しました", - "username_taken_error": "このユーザー名はすでに使用されています。", - "username_immutable_error": "ユーザー名はすでに設定されており、ここでは変更できません。名前を変更したい場合は管理者にお問い合わせください。", - "change_password": "パスワード変更", - "current_password": "現在のパスワード", - "new_password": "新しいパスワード", - "min_8_chars": "8文字以上", - "confirm_password": "新しいパスワードの確認", - "update_password": "パスワードを更新", - "updating": "更新中…", - "password_updated": "パスワードが正常に更新されました", - "passwords_no_match": "パスワードが一致しません", - "password_too_short": "パスワードは8文字以上必要です", - "password_change_failed": "パスワードの変更に失敗しました", - "error_network": "ネットワークエラー: {{message}}", - "error_label_required": "ラベルを入力してください", - "error_create_pw": "アプリパスワードの作成に失敗しました", - "confirm_revoke": "アプリパスワード「{{label}}」を失効させますか?使用中のクライアントは動作しなくなります。", - "error_revoke": "失効に失敗しました", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "アップロード中...", - "files": "ファイル", - "complete": "{{count}} / {{total}} アップロード済み" - }, - "storage_quota_exceeded": "ストレージ容量を超過しました", - "sharedwithme": { - "pageTitle": "自分と共有", - "pageDescription": "他のユーザーがあなたと共有したファイルとフォルダー", - "emptyStateTitle": "まだ何も共有されていません", - "emptyStateDesc": "他のユーザーがあなたと共有したアイテムがここに表示されます", - "loadMore": "さらに読み込む", - "sharedBy": "共有者", - "colName": "名前", - "colType": "タイプ", - "colSharedBy": "共有者", - "colDate": "共有日", - "colPermissions": "権限" - }, - "groupby": { - "none": "なし", - "title": "グループ化", - "owner": "オーナー", - "shareDate": "共有日", - "type": "種類", - "type.folders": "フォルダー", - "accessedAt": "アクセス日", - "modifiedAt": "更新日", - "createdAt": "作成日", - "size": "サイズ", - "favoriteDate": "お気に入り登録日", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "新規" - }, - "dateBucket": { - "today": "今日", - "last7days": "過去7日間", - "last30days": "過去30日間" - }, - "groups": { - "title": "グループを管理", - "create_button": "グループを作成", - "create_dialog_title": "新規グループ", - "edit_dialog_title": "グループ名を変更", - "name_label": "名前", - "name_placeholder": "engineering", - "description_label": "説明(任意)", - "members_section": "メンバー", - "add_member_placeholder": "ユーザーまたはグループを追加…", - "no_members": "メンバーはまだいません。", - "remove_member": "削除", - "delete_group": "グループを削除", - "delete_confirm": "グループ「{name}」を削除しますか?このグループを参照しているすべての権限が取り消されます。", - "empty_state": "グループはまだありません。", - "load_more": "もっと読み込む", - "back_to_list": "戻る", - "loading": "読み込み中…", - "virtual_badge": "システム", - "member_count_zero": "メンバーなし", - "member_count_one": "1 メンバー", - "member_count_other": "{count} メンバー", - "delete_confirm_label": "確認のためにグループ名を入力してください:", - "delete_confirm_mismatch": "確認のためにグループ名を正確に入力してください。", - "virtual_internal_name": "内部", - "members_loading": "メンバーを読み込み中…", - "members_empty": "メンバーなし", - "virtual_internal_explanation": "このサーバー上のすべての内部ユーザー" - }, - "myshares": { - "copyLink": "リンクをコピー", - "deleteLink": "リンクを削除", - "notifyByEmail": "メールで通知", - "notifyFailed": "通知を送信できませんでした。", - "notifyGroupMembers": "グループメンバーに通知", - "notifyRateLimited": "この受信者への通知が多すぎます — しばらくしてから再試行してください。", - "removeAccess": "アクセスを削除", - "resendInvitation": "招待メールを再送信" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", + "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n新しい共有を確認するには OxiCloud を開いてください:\n{{login_link}}\n\n{{inviter}} さんから他にも新しい共有があるかもしれません — サインインしてあなたと共有されたすべての項目を確認してください。\n\n— OxiCloud\n\nOxiCloud のアカウントをお持ちで、共有通知の設定が有効になっているため、このメッセージが届いています。プロフィールでオフにできます(誰かが共有したときにメールで通知する)。" + } } + }, + "app": { + "title": "OxiCloud", + "description": "ミニマリストクラウドストレージシステム" + }, + "nav": { + "files": "ファイル", + "shared": "共有", + "recent": "最近", + "favorites": "お気に入り", + "photos": "写真", + "music": "音楽", + "trash": "ゴミ箱", + "sharedwithme": "自分と共有", + "profile": "プロフィール", + "shared_with_me": "自分と共有" + }, + "photos": { + "empty_state": "写真はまだありません", + "empty_hint": "画像や動画をアップロードするとここに表示されます", + "items_selected": "件選択中", + "view_daily": "日", + "view_monthly": "月", + "view_yearly": "年", + "group_by": "グループ化" + }, + "music": { + "create_playlist": "プレイリストを作成", + "playlists": "プレイリスト", + "no_playlists": "プレイリストがありません", + "select_playlist": "プレイリストを選択", + "select_hint": "サイドバーからプレイリストを選択するか、新しいものを作成してください", + "add_tracks": "トラックを追加", + "no_tracks": "このプレイリストにトラックがありません", + "unknown_artist": "不明なアーティスト", + "unknown_title": "不明", + "confirm_delete": "このプレイリストを削除しますか?", + "playlist_name": "プレイリスト名", + "create": "作成", + "delete": "削除", + "share": "共有", + "edit": "編集", + "play_all": "すべて再生", + "shuffle": "シャッフル", + "repeat": "リピート", + "repeat_one": "1曲リピート", + "queue": "キュー", + "queue_empty": "キューが空です", + "not_playing": "再生していません", + "play": "再生", + "pause": "一時停止", + "previous": "前へ", + "next": "次へ", + "volume": "音量", + "mute": "ミュート", + "unmute": "ミュート解除", + "title": "タイトル", + "artist": "アーティスト", + "album": "アルバム", + "tracks": "曲", + "add": "追加", + "added": "追加しました!", + "added_to_playlist": "プレイリストに追加しました", + "add_to_playlist": "プレイリストに追加", + "load_error": "プレイリストの読み込みエラー", + "add_error": "曲を追加できませんでした", + "no_playlists_yet": "プレイリストがありません。最初に作成してください!", + "selected_files": "選択中:", + "error": "エラー", + "search_audio": "オーディオファイルを検索…", + "no_audio_files": "オーディオファイルが見つかりません", + "selected": "件選択中", + "loading": "読み込み中…", + "search_error": "オーディオファイルを読み込めませんでした", + "adding": "追加中…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "前へ" + }, + "actions": { + "search": "ファイルを検索...", + "new_folder": "新しいフォルダ", + "upload": "アップロード", + "upload_files": "ファイルをアップロード", + "upload_folder": "フォルダをアップロード", + "upload.uploading": "アップロード中...", + "upload.complete": "{count} / {total} アップロード完了", + "upload.files": "ファイル", + "rename": "名前を変更", + "move": "移動先...", + "move_to": "移動先", + "delete": "削除", + "download": "ダウンロード", + "view": "表示", + "cancel": "キャンセル", + "confirm": "確認", + "share": "共有", + "favorite": "お気に入りに追加", + "unfavorite": "お気に入りから削除", + "copy": "コピー", + "notify": "通知", + "send": "送信", + "clear_recent": "最近をクリア", + "logout": "ログアウト", + "create": "作成", + "search_btn": "検索", + "close": "閉じる", + "delete_permanently": "完全に削除", + "empty_trash": "ゴミ箱を空にする", + "open_parent_folder": "親フォルダへ移動", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "外観", + "about": "OxiCloudについて", + "about_description": "RustとClean Architectureで構築されたクラウドストレージプラットフォーム。高速・安全・プライベート。", + "admin_panel": "管理パネル", + "profile": "マイプロフィール", + "role_user": "ユーザー", + "theme": { + "light": "ライト", + "dark": "ダーク", + "auto": "システムに合わせる" + }, + "manage_groups": "グループを管理", + "admin": "管理者" + }, + "share": { + "dialogTitle": "共有リンク", + "linkLabel": "共有リンク:", + "copyLink": "コピー", + "permissions": "権限:", + "permissionRead": "読み取り", + "permissionWrite": "書き込み", + "permissionReshare": "再共有", + "password": "パスワード保護:", + "generatePassword": "生成", + "expiration": "有効期限:", + "update": "共有を更新", + "remove": "共有を削除", + "notifyTitle": "通知を送信", + "notifyEmailLabel": "メールアドレス:", + "notifyMessageLabel": "メッセージ(任意):", + "notifySend": "通知を送信", + "shareWithOthers": "他のユーザーと共有", + "sharePublicly": "公開共有", + "shareSettings": "共有設定", + "shareCopied": "リンクがクリップボードにコピーされました", + "shareCreated": "共有リンクが正常に作成されました", + "shareUpdated": "共有設定が正常に更新されました", + "shareRemoved": "共有が正常に削除されました", + "inviteByEmail": "メールで招待 — 招待を送信します", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "コピー", + "copy_failed": "Could not copy link", + "download": "ダウンロード", + "files": "ファイル", + "folders": "フォルダ", + "link_name": "Link name (optional)", + "notifyByEmail": "メールで通知", + "revoke": "Remove", + "role_label": "役割" + }, + "share_dialogTitle": "共有リンク", + "share_linkLabel": "共有リンク:", + "share_copyLink": "コピー", + "share_permissions": "権限:", + "share_permissionRead": "読み取り", + "share_permissionWrite": "書き込み", + "share_permissionReshare": "再共有", + "share_password": "パスワード保護:", + "share_generatePassword": "生成", + "share_expiration": "有効期限:", + "share_update": "共有を更新", + "share_remove": "共有を削除", + "share_notifyTitle": "通知を送信", + "share_notifyEmailLabel": "メールアドレス:", + "share_notifyMessageLabel": "メッセージ(任意):", + "share_notifySend": "通知を送信", + "shared": { + "backToFiles": "ファイルに戻る", + "pageTitle": "共有リソース", + "pageDescription": "共有ファイルとフォルダの管理", + "filterType": "種類:", + "filterAll": "すべて", + "filterFiles": "ファイル", + "filterFolders": "フォルダ", + "sortBy": "並び替え:", + "sortByName": "名前", + "sortByDate": "共有日", + "sortByExpiration": "有効期限", + "search": "検索", + "colName": "名前", + "colType": "種類", + "colDateShared": "共有日", + "colExpiration": "有効期限", + "colPermissions": "権限", + "colPassword": "パスワード", + "colActions": "操作", + "emptyStateTitle": "共有リソースはまだありません", + "emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", + "goToFiles": "ファイルへ移動", + "typeFile": "ファイル", + "typeFolder": "フォルダ", + "noExpiration": "期限なし", + "hasPassword": "あり", + "noPassword": "なし", + "editShare": "共有を編集", + "notifyShare": "通知する", + "copyLink": "リンクをコピー", + "removeShare": "共有を削除", + "linkCopied": "リンクがクリップボードにコピーされました!", + "linkCopyFailed": "リンクのコピーに失敗しました", + "itemUpdated": "共有設定が正常に更新されました", + "itemRemoved": "共有が正常に削除されました", + "invalidEmail": "有効なメールアドレスを入力してください", + "notificationSent": "通知が正常に送信されました", + "notificationFailed": "通知の送信に失敗しました", + "shared_backToFiles": "ファイルに戻る", + "shared_pageTitle": "共有リソース", + "shared_pageDescription": "共有ファイルとフォルダの管理", + "shared_filterType": "種類:", + "shared_filterAll": "すべて", + "shared_filterFiles": "ファイル", + "shared_filterFolders": "フォルダ", + "shared_sortBy": "並び替え:", + "shared_sortByName": "名前", + "shared_sortByDate": "共有日", + "shared_sortByExpiration": "有効期限", + "shared_search": "検索", + "shared_colName": "名前", + "shared_colType": "種類", + "shared_colDateShared": "共有日", + "shared_colExpiration": "有効期限", + "shared_colPermissions": "権限", + "shared_colPassword": "パスワード", + "shared_colActions": "操作", + "shared_emptyStateTitle": "共有リソースはまだありません", + "shared_emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", + "shared_goToFiles": "ファイルへ移動", + "shared_typeFile": "ファイル", + "shared_typeFolder": "フォルダ", + "shared_noExpiration": "期限なし", + "shared_hasPassword": "あり", + "shared_noPassword": "なし", + "shared_editShare": "共有を編集", + "shared_notifyShare": "通知する", + "shared_copyLink": "リンクをコピー", + "shared_removeShare": "共有を削除", + "shared_linkCopied": "リンクがクリップボードにコピーされました!", + "shared_linkCopyFailed": "リンクのコピーに失敗しました", + "shared_itemUpdated": "共有設定が正常に更新されました", + "shared_itemRemoved": "共有が正常に削除されました", + "shared_invalidEmail": "有効なメールアドレスを入力してください", + "shared_notificationSent": "通知が正常に送信されました", + "shared_notificationFailed": "通知の送信に失敗しました" + }, + "files": { + "name": "名前", + "type": "種類", + "size": "サイズ", + "modified": "更新日", + "no_files": "このフォルダにファイルはありません", + "empty_hint": "ファイルをアップロードするかフォルダを作成して始めましょう", + "loading": "ファイルを読み込み中…", + "view_grid": "グリッド表示", + "view_list": "リスト表示", + "file_types": { + "document": "ドキュメント", + "image": "画像", + "video": "動画", + "audio": "音声", + "pdf": "PDF", + "text": "テキスト", + "folder": "フォルダ", + "spreadsheet": "スプレッドシート", + "presentation": "プレゼンテーション", + "archive": "アーカイブ", + "installer": "インストーラー", + "code": "コード" + }, + "owner": "オーナー", + "add_favorites": "お気に入りに追加", + "added_favorites": "お気に入りに追加しました", + "col_name": "名前", + "col_owner": "オーナー", + "col_size": "サイズ", + "col_type": "種類", + "copy": "コピー", + "edit": "編集", + "file": "ファイル", + "folder": "フォルダ", + "new_folder": "新しいフォルダ", + "share": "共有", + "view": "表示" + }, + "dialogs": { + "rename_folder": "フォルダ名を変更", + "rename_file": "ファイル名を変更", + "new_name": "新しい名前", + "new_folder_title": "新しいフォルダ", + "folder_name": "フォルダ名", + "folder_placeholder": "マイフォルダ", + "rename_title": "名前を変更", + "move_file": "ファイルを移動", + "move_folder": "フォルダを移動", + "select_destination": "移動先フォルダを選択:", + "select_this_folder": "このフォルダを選択", + "go_to_parent": ".. (親フォルダ)", + "no_subfolders": "サブフォルダなし", + "root": "ルート", + "delete_confirmation": "本当に削除しますか", + "and_contents": "およびすべての内容", + "no_undo": "この操作は元に戻せません", + "confirm_title": "操作の確認", + "confirm_delete": "ゴミ箱に移動", + "confirm_delete_file": "ファイル「{{name}}」をゴミ箱に移動しますか?", + "confirm_delete_folder": "フォルダ「{{name}}」とそのすべての内容をゴミ箱に移動しますか?", + "confirm_permanent_delete": "完全に削除", + "confirm_permanent_delete_msg": "このアイテムを完全に削除しますか?この操作は元に戻せません。", + "confirm_empty_trash": "ゴミ箱を空にする", + "confirm_delete_share": "共有リンクを削除", + "confirm_delete_share_msg": "この共有リンクを削除しますか?", + "share_file": "ファイルを共有", + "share_folder": "フォルダを共有", + "existing_shares": "既存の共有", + "share_options": "共有オプション", + "password": "パスワード", + "expiration": "有効期限", + "permissions": "権限", + "generated_link": "生成されたリンク", + "notify": "通知を送信", + "recipient": "宛先", + "message": "メッセージ", + "move_to_home": "ホームフォルダへ移動" + }, + "dropzone": { + "drag_files": "ファイルをここにドラッグするか、クリックして選択", + "drop_files": "ファイルをドロップしてアップロード" + }, + "permissions": { + "read": "読み取り", + "write": "書き込み", + "reshare": "再共有" + }, + "errors": { + "file_not_found": "ファイルが見つかりません", + "folder_not_found": "フォルダが見つかりません", + "delete_error": "削除エラー", + "upload_error": "ファイルのアップロードエラー", + "rename_error": "名前変更エラー", + "move_error": "移動エラー", + "empty_name": "名前を空にすることはできません", + "name_exists": "同じ名前のファイルまたはフォルダが既に存在します", + "generic_error": "エラーが発生しました", + "group_name_invalid": "グループ名はメールプレフィックス形式に一致している必要があります(文字、数字、ドット、ダッシュ、アンダースコア;1~64文字)。", + "group_cycle": "このメンバーはグループ間で循環参照を作成します。", + "group_depth_exceeded": "ネストの深さが許容されている最大値(8)を超えています。", + "group_virtual_immutable": "「Internal」グループはシステム管理であり、変更できません。", + "group_not_found": "グループが見つかりません。", + "group_name_taken": "この名前のグループはすでに存在します。" + }, + "breadcrumb": { + "home": "ホーム" + }, + "trash": { + "empty_trash": "ゴミ箱を空にする", + "empty_state": "ゴミ箱は空です", + "original_location": "元の場所", + "deleted_date": "削除日", + "remaining": "残り", + "actions": "操作", + "restore": "復元", + "delete_permanently": "完全に削除", + "empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。", + "groupby": { + "remaining_days": "残り日数", + "trashed_time": "削除日時" + }, + "delete": "完全に削除", + "empty_action": "ゴミ箱を空にする" + }, + "daysRemaining": { + "expired": "期限切れ", + "today": "今日", + "tomorrow": "明日", + "inDays": "{{count}}日" + }, + "expiryChip": { + "never": "期限なし", + "expired": "期限切れ", + "today": "今日で期限切れ", + "tomorrow": "明日で期限切れ", + "inDays": "{{count}}日後に期限切れ", + "onDate": "{{date}}に期限切れ" + }, + "auth": { + "login_title": "サインイン", + "username": "ユーザー名", + "username_placeholder": "ユーザー名を入力", + "login_identifier": "ユーザー名またはメールアドレス", + "login_identifier_placeholder": "ユーザー名またはメールアドレスを入力", + "password": "パスワード", + "password_placeholder": "パスワードを入力", + "login_button": "サインイン", + "no_account": "アカウントをお持ちでないですか?", + "register": "登録", + "admin_setup": "初回ですか?", + "setup": "管理者をセットアップ", + "register_title": "アカウント作成", + "email": "メール", + "email_placeholder": "メールアドレスを入力", + "confirm_password": "パスワードの確認", + "confirm_password_placeholder": "パスワードを再入力", + "register_button": "アカウント作成", + "have_account": "既にアカウントをお持ちですか?", + "login": "サインイン", + "setup_title": "初期設定", + "setup_step1": "管理者", + "setup_step2": "システム", + "setup_step3": "完了", + "admin_username": "管理者ユーザー名", + "admin_email": "管理者メール", + "admin_password": "管理者パスワード", + "create_admin": "管理者を作成", + "back_to_login": "設定済みですか?", + "admin_success": "管理者アカウントが正常に作成されました!サインインできます。", + "account_success": "アカウントが正常に作成されました!サインインできます。", + "passwords_mismatch": "パスワードが一致しません", + "admin_create_error": "管理者アカウントの作成エラー", + "or": "または", + "sso_login": "SSOでサインイン", + "sso_login_provider": "{{provider}}でサインイン", + "magicLinkHint": "パスワードをお持ちでない方は、メールアドレスを入力するとワンタイムサインインリンクをお送りします。", + "magicLinkEmailLabel": "メールアドレス", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "サインインリンクを送信", + "magicLinkSent": "そのメールアドレスのアカウントが存在する場合、サインインリンクが送信されました。受信トレイをご確認ください。", + "magicLinkUnavailable": "このサーバーではメールでのサインインは利用できません。", + "magicLinkNetworkError": "サーバーに接続できませんでした: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "メールアドレス", + "magic_hint": "パスワードをお持ちでない方は、メールアドレスを入力するとワンタイムサインインリンクをお送りします。", + "magic_unavailable": "このサーバーではメールでのサインインは利用できません。", + "passwords_match": "Passwords match", + "sign_in": "サインイン" + }, + "storage": { + "title": "ストレージ", + "calculating": "計算中...", + "used": "{{percentage}}% 使用中 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "このファイル形式はプレビューできません。", + "download_file": "ファイルをダウンロード", + "zoom_in": "拡大", + "zoom_out": "縮小", + "zoom_reset": "ズームリセット" + }, + "language_selector": { + "title": "ようこそ!", + "subtitle": "続行するには言語を選択してください", + "continue": "続行", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ja": "日本語", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "お気に入りはまだありません", + "empty_hint": "ファイルやフォルダにスターを付けてお気に入りに追加", + "add": "お気に入りに追加", + "remove": "お気に入りから削除", + "added_title": "お気に入りに追加しました", + "added_msg": "お気に入りに追加しました", + "removed_title": "お気に入りから削除しました", + "removed_msg": "お気に入りから削除しました" + }, + "recent": { + "title": "最近", + "clear": "最近をクリア", + "accessed": "アクセス日", + "empty_state": "最近のファイルはありません", + "empty_hint": "開いたファイルがここに表示されます", + "loadMore": "さらに読み込む" + }, + "notifications": { + "file_renamed": "ファイル名を変更しました", + "file_renamed_to": "ファイル名を「{{name}}」に変更しました", + "folder_renamed": "フォルダ名を変更しました", + "folder_renamed_to": "フォルダ名を「{{name}}」に変更しました", + "file_uploaded": "ファイルをアップロードしました", + "file_deleted": "ファイルをゴミ箱に移動しました", + "folder_deleted": "フォルダをゴミ箱に移動しました", + "item_deleted_permanently": "アイテムを完全に削除しました", + "trash_emptied": "ゴミ箱を正常に空にしました", + "title": "通知", + "empty": "通知はありません", + "link_created": "リンクを作成しました", + "share_success": "共有リンクを正常に作成しました", + "upload_files_section_title": "ここではアップロードできません", + "upload_files_section_body": "ファイルをアップロードするには「ファイル」セクションに移動してください" + }, + "batch": { + "one_selected": "1件選択中", + "n_selected": "{{count}}件選択中", + "confirm_delete": "{{count}}件のアイテムをゴミ箱に移動しますか?", + "move_title": "{{count}}件のアイテムを移動", + "add_favorites": "お気に入りに追加", + "move_copy": "移動またはコピー" + }, + "admin": { + "page_title": "管理パネル", + "back_to_app": "OxiCloudに戻る", + "loading": "読み込み中…", + "access_denied": "アクセス拒否", + "access_denied_desc": "管理者権限が必要です。", + "sign_in": "サインイン", + "tab_dashboard": "ダッシュボード", + "tab_users": "ユーザー", + "tab_oidc": "SSO / OIDC", + "total_users": "総ユーザー数", + "active_users": "アクティブ", + "admins": "管理者", + "version": "バージョン", + "storage_overview": "ストレージ概要", + "used": "使用済み", + "total_quota": "合計クォータ", + "usage_pct": "使用率", + "users_over_80": "クォータ80%超", + "users_over_quota": "クォータ超過", + "system": "システム", + "auth_label": "認証", + "oidc_label": "OIDC", + "quotas_label": "クォータ", + "enabled": "有効", + "disabled": "無効", + "active": "アクティブ", + "off": "オフ", + "allow_registration": "公開セルフ登録を許可", + "registration_warning": "公開登録は無効です。管理者のみがユーザーを作成できます。", + "user_management": "ユーザー管理", + "create_user": "ユーザー作成", + "col_user": "ユーザー", + "col_role": "役割", + "col_auth": "認証", + "col_status": "ステータス", + "col_storage": "ストレージ", + "col_last_login": "最終ログイン", + "col_actions": "操作", + "loading_users": "ユーザーを読み込み中…", + "failed_load_users": "読み込み失敗", + "no_users_found": "ユーザーなし", + "showing_users": "{{from}}-{{to}} / {{total}} を表示", + "prev": "前へ", + "next": "次へ", + "inactive": "非アクティブ", + "you_badge": "(あなた)", + "local": "ローカル", + "never": "未ログイン", + "just_now": "たった今", + "minutes_ago": "{{n}}分前", + "hours_ago": "{{n}}時間前", + "days_ago": "{{n}}日前", + "edit_quota_title": "クォータを編集", + "reset_password_title": "パスワードリセット", + "toggle_role_title": "役割を切替", + "deactivate_title": "無効化", + "activate_title": "有効化", + "delete_title": "削除", + "sso_title": "シングルサインオン (OIDC / SSO)", + "enable_sso": "SSO認証を有効化", + "provider_name": "プロバイダー名", + "issuer_url": "発行者URL", + "issuer_url_hint": "OpenID Connect発行者URL", + "auto_discover": "自動検出", + "discovering": "検出中…", + "client_id": "クライアントID", + "client_secret": "クライアントシークレット", + "client_secret_placeholder": "現在の値を維持するには空に", + "secret_configured": "クライアントシークレット設定済み", + "callback_url": "コールバックURL", + "callback_url_hint": "(IdPに登録)", + "advanced_settings": "詳細設定", + "scopes": "スコープ", + "auto_provision": "初回ログイン時に自動プロビジョニング", + "admin_groups": "管理者グループ", + "admin_groups_hint": "カンマ区切りのOIDCグループ名", + "disable_password": "パスワードログイン無効化(OIDCのみ)", + "password_warning": "すべてのパスワードログインが無効に!", + "test_btn": "テスト", + "save_btn": "保存", + "saving": "保存中…", + "settings_saved": "設定が保存されました — OIDC: {{status}}", + "quota_modal_title": "ストレージクォータ更新", + "quota_user_label": "ユーザー:", + "new_quota": "新しいクォータ", + "quota_unlimited_hint": "0で無制限", + "cancel": "キャンセル", + "create_user_title": "新規ユーザー作成", + "username_label": "ユーザー名", + "username_placeholder": "taro", + "username_hint": "3〜32文字", + "password_label": "パスワード", + "password_placeholder": "8文字以上", + "email_label": "メール", + "email_optional": "(任意)", + "email_placeholder": "user@example.com(空なら自動生成)", + "role_label": "役割", + "role_user": "ユーザー", + "role_admin": "管理者", + "quota_label": "クォータ", + "creating": "作成中…", + "reset_pw_title": "パスワードリセット", + "new_password_label": "新しいパスワード", + "resetting": "リセット中…", + "reset_btn": "リセット", + "confirm_role_change": "役割を{{role}}に変更?", + "confirm_deactivate": "このユーザーを無効化しますか?", + "confirm_activate": "このユーザーを有効化しますか?", + "confirm_delete_user": "ユーザー「{{name}}」を削除?取り消せません!", + "confirm_action": "操作の確認", + "confirm_yes": "確認", + "confirm_no": "キャンセル", + "error_username_short": "ユーザー名は3文字以上", + "error_password_short": "パスワードは8文字以上", + "error_generic": "失敗", + "error_network": "ネットワークエラー: {{message}}", + "error_create_user": "ユーザー作成失敗", + "tab_storage": "ストレージ", + "storage_title": "ストレージ設定", + "storage_current_backend": "現在のバックエンド", + "storage_total_blobs": "総ブロブ数", + "storage_total_size": "合計サイズ", + "storage_dedup_ratio": "重複排除率", + "storage_backend": "バックエンド", + "storage_local": "ローカル", + "storage_s3": "S3互換", + "storage_provider_preset": "プロバイダープリセット", + "storage_preset_custom": "カスタム", + "storage_endpoint_url": "エンドポイントURL", + "storage_endpoint_hint": "AWS S3の場合は空欄のまま", + "storage_bucket": "バケット", + "storage_region": "リージョン", + "storage_access_key": "アクセスキー", + "storage_secret_key": "シークレットキー", + "storage_secret_configured": "キーが設定済み", + "storage_key_placeholder": "新しいキーを入力", + "storage_path_style": "パススタイルを強制", + "storage_path_style_hint": "MinIOおよび一部のS3互換サービスに必要", + "storage_test_connection": "接続テスト", + "storage_test_success": "接続成功", + "storage_test_failure": "接続失敗", + "storage_save": "設定を保存", + "storage_saved": "設定を保存しました", + "storage_migration": "データ移行", + "storage_migration_coming_soon": "移行ツールは近日公開予定", + "migration_status_label": "移行状況", + "migration_start": "移行を開始", + "migration_pause": "一時停止", + "migration_resume": "再開", + "migration_verify": "検証", + "migration_complete": "完了", + "migration_started": "移行を開始しました", + "migration_paused_msg": "移行を一時停止しました", + "migration_resumed_msg": "移行を再開しました", + "migration_completed_msg": "移行が正常に完了しました", + "migration_verifying": "検証中...", + "migration_verify_passed": "検証に合格", + "migration_verify_failed": "検証に失敗", + "migration_failed_blobs": "失敗したブロブ", + "testing": "テスト中...", + "smtp_disabled": "無効 (ホスト未設定)", + "smtp_enabled": "有効", + "smtp_enabled_label": "ステータス", + "smtp_intro": "SMTP は環境変数 (OXICLOUD_SMTP_*) でのみ設定します。以下の値は稼働中のサーバーから読み取られます — 変更するには環境を編集して OxiCloud を再起動してください。", + "smtp_not_configured": "このサーバーでは SMTP が設定されていません。", + "smtp_send_failed": "送信に失敗しました。", + "smtp_send_test": "テストメールを送信", + "smtp_sending": "送信中…", + "smtp_sent": "テストメールを送信しました。", + "smtp_server_code": "サーバーの応答", + "smtp_test_intro": "あらかじめ定義された診断メッセージを下記の宛先に送信し、SMTP サーバーの応答を表示します。これを使ってリレーのログと突き合わせて確認できます。", + "smtp_test_missing_to": "宛先アドレスを入力してください。", + "smtp_test_title": "テストメールを送信", + "smtp_test_to": "宛先アドレス", + "smtp_title": "送信メール (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "管理者", + "confirm_role": "役割を{{role}}に変更?", + "dashboard": "ダッシュボード", + "email": "メール", + "mig_complete": "完了", + "mig_pause": "一時停止", + "mig_resume": "再開", + "mig_verify_failed": "検証に失敗", + "mig_verify_passed": "検証に合格", + "mig_verifying": "検証中...", + "oidc_auto_provision": "初回ログイン時に自動プロビジョニング", + "oidc_callback": "コールバックURL", + "oidc_client_id": "クライアントID", + "oidc_disable_pw": "パスワードログイン無効化(OIDCのみ)", + "oidc_issuer": "発行者URL", + "oidc_scopes": "スコープ", + "password": "パスワード", + "quotas": "クォータ", + "reset_pw_for": "新しいパスワード:", + "role": "役割", + "smtp_fail": "送信に失敗しました。", + "smtp_send": "送信", + "smtp_test": "テストメールを送信", + "smtp_user_state": "認証", + "status": "ステータス", + "storage": "ストレージ", + "storage_endpoint": "エンドポイントURL", + "storage_tab": "ストレージ", + "time_min_ago": "{{n}}分前", + "title": "管理者", + "user": "ユーザー", + "username": "ユーザー名", + "users": "ユーザー" + }, + "profile": { + "page_title": "プロフィール", + "back_to_app": "OxiCloudに戻る", + "loading": "読み込み中…", + "not_authenticated": "未認証", + "not_authenticated_desc": "プロフィールを表示するにはサインインしてください。", + "sign_in": "サインイン", + "role_admin": "管理者", + "role_user": "ユーザー", + "account_details": "アカウント詳細", + "username": "ユーザー名", + "email": "メール", + "role": "役割", + "last_login": "最終ログイン", + "storage": "ストレージ", + "used": "使用済み", + "quota": "クォータ", + "usage": "使用率", + "unlimited": "無制限", + "app_passwords": "アプリパスワード", + "app_pw_desc": "WebDAV、CalDAV、CardDAVクライアント用のパスワードを生成します。各パスワードは一度だけ表示されます。", + "app_pw_label_placeholder": "ラベル(例:Thunderbird、macOS)", + "generate": "生成", + "generating": "生成中…", + "new_password_for": "新しいパスワード:", + "copy_warning": "このパスワードを今コピーしてください。再度表示できません。", + "copy_to_clipboard": "クリップボードにコピー", + "col_label": "ラベル", + "col_created": "作成日", + "col_last_used": "最終使用", + "col_status": "ステータス", + "active": "アクティブ", + "revoked": "失効済み", + "revoke_title": "失効", + "no_app_passwords": "アプリパスワードはまだありません。", + "client_sessions": "クライアントセッション", + "client_sessions_desc": "Nextcloud互換クライアント接続時に自動生成されます。", + "col_client": "クライアント", + "never": "未ログイン", + "just_now": "たった今", + "minutes_ago": "{{n}}分前", + "hours_ago": "{{n}}時間前", + "days_ago": "{{n}}日前", + "edit_profile": "プロフィールを編集", + "edit_oidc_managed": "情報(姓、名、プロフィール写真など)を変更するには、IDプロバイダーで更新してください。次回サインイン時に反映されます。", + "username_claim_hint": "2〜64文字、英数字 / ドット / ハイフン / アンダースコア。一度選択すると、ユーザー名は変更できません(DAV/NextCloudクライアントが依存します)。", + "username_already_claimed": "ユーザー名は設定済みで変更できません(DAV/NextCloudクライアントが依存します)。", + "given_name": "名", + "family_name": "姓", + "notify_on_share": "誰かが共有したときにメールで通知する", + "notify_on_share_hint": "チェックを外しても、共有はアカウントに表示されますが、メールでの通知は届きません。", + "save_profile": "変更を保存", + "profile_saved": "プロフィールを更新しました", + "profile_no_changes": "保存する変更はありません。", + "profile_save_failed": "保存に失敗しました", + "username_taken_error": "このユーザー名はすでに使用されています。", + "username_immutable_error": "ユーザー名はすでに設定されており、ここでは変更できません。名前を変更したい場合は管理者にお問い合わせください。", + "change_password": "パスワード変更", + "current_password": "現在のパスワード", + "new_password": "新しいパスワード", + "min_8_chars": "8文字以上", + "confirm_password": "新しいパスワードの確認", + "update_password": "パスワードを更新", + "updating": "更新中…", + "password_updated": "パスワードが正常に更新されました", + "passwords_no_match": "パスワードが一致しません", + "password_too_short": "パスワードは8文字以上必要です", + "password_change_failed": "パスワードの変更に失敗しました", + "error_network": "ネットワークエラー: {{message}}", + "error_label_required": "ラベルを入力してください", + "error_create_pw": "アプリパスワードの作成に失敗しました", + "confirm_revoke": "アプリパスワード「{{label}}」を失効させますか?使用中のクライアントは動作しなくなります。", + "error_revoke": "失効に失敗しました", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "パスワードが一致しません" + }, + "upload": { + "uploading": "アップロード中...", + "files": "ファイル", + "complete": "{{count}} / {{total}} アップロード済み" + }, + "storage_quota_exceeded": "ストレージ容量を超過しました", + "sharedwithme": { + "pageTitle": "自分と共有", + "pageDescription": "他のユーザーがあなたと共有したファイルとフォルダー", + "emptyStateTitle": "まだ何も共有されていません", + "emptyStateDesc": "他のユーザーがあなたと共有したアイテムがここに表示されます", + "loadMore": "さらに読み込む", + "sharedBy": "共有者", + "colName": "名前", + "colType": "タイプ", + "colSharedBy": "共有者", + "colDate": "共有日", + "colPermissions": "権限" + }, + "groupby": { + "none": "なし", + "title": "グループ化", + "owner": "オーナー", + "shareDate": "共有日", + "type": "種類", + "type.folders": "フォルダー", + "accessedAt": "アクセス日", + "modifiedAt": "更新日", + "createdAt": "作成日", + "size": "サイズ", + "favoriteDate": "お気に入り登録日", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "新規", + "folders": "フォルダ" + }, + "dateBucket": { + "today": "今日", + "last7days": "過去7日間", + "last30days": "過去30日間", + "unknown": "不明" + }, + "groups": { + "title": "グループを管理", + "create_button": "グループを作成", + "create_dialog_title": "新規グループ", + "edit_dialog_title": "グループ名を変更", + "name_label": "名前", + "name_placeholder": "engineering", + "description_label": "説明(任意)", + "members_section": "メンバー", + "add_member_placeholder": "ユーザーまたはグループを追加…", + "no_members": "メンバーはまだいません。", + "remove_member": "削除", + "delete_group": "グループを削除", + "delete_confirm": "グループ「{name}」を削除しますか?このグループを参照しているすべての権限が取り消されます。", + "empty_state": "グループはまだありません。", + "load_more": "もっと読み込む", + "back_to_list": "戻る", + "loading": "読み込み中…", + "virtual_badge": "システム", + "member_count_zero": "メンバーなし", + "member_count_one": "1 メンバー", + "member_count_other": "{count} メンバー", + "delete_confirm_label": "確認のためにグループ名を入力してください:", + "delete_confirm_mismatch": "確認のためにグループ名を正確に入力してください。", + "virtual_internal_name": "内部", + "members_loading": "メンバーを読み込み中…", + "members_empty": "メンバーなし", + "virtual_internal_explanation": "このサーバー上のすべての内部ユーザー", + "create": "グループを作成", + "empty": "グループはまだありません。", + "members": "メンバー" + }, + "myshares": { + "copyLink": "リンクをコピー", + "deleteLink": "リンクを削除", + "notifyByEmail": "メールで通知", + "notifyFailed": "通知を送信できませんでした。", + "notifyGroupMembers": "グループメンバーに通知", + "notifyRateLimited": "この受信者への通知が多すぎます — しばらくしてから再試行してください。", + "removeAccess": "アクセスを削除", + "resendInvitation": "招待メールを再送信", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "音声", + "code": "コード", + "text": "テキスト" + }, + "common": { + "add": "追加", + "cancel": "キャンセル", + "clear": "Clear", + "close": "閉じる", + "confirm": "確認", + "copy": "コピー", + "create": "作成", + "delete": "削除", + "download": "ダウンロード", + "load_more": "さらに読み込む", + "loading": "読み込み中…", + "next": "次へ", + "no": "なし", + "previous": "前へ", + "remove": "Remove", + "rename": "名前を変更", + "save": "保存", + "search": "検索", + "yes": "あり" + }, + "device": { + "continue": "続行", + "unknown": "不明" + }, + "expiryBucket": { + "expired": "期限切れ", + "noExpiry": "期限なし", + "today": "今日", + "tomorrow": "明日" + }, + "nextcloud": { + "error_title": "エラー", + "sign_in_with": "{{provider}}でサインイン" + }, + "search": { + "size_label": "サイズ", + "title": "検索", + "type": { + "audio": "音声" + }, + "type_label": "種類" + }, + "sizeBucket": { + "folders": "フォルダ" + }, + "view": { + "grid": "グリッド表示", + "list": "リスト表示" + } } diff --git a/frontend/static/locales/ko.json b/frontend/static/locales/ko.json index b8fa12c0..caf10ccf 100644 --- a/frontend/static/locales/ko.json +++ b/frontend/static/locales/ko.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "이 로그인 링크는 더 이상 유효하지 않습니다", - "expired_body": "링크가 만료되었거나 이미 사용되었을 수 있습니다. 새 링크를 보내드릴 수 있습니다 — 몇 초 안에 받은편지함에 도착합니다.", - "resend_to": "{{email}}로 새 링크 보내기", - "generic_unavailable": "이 로그인 링크는 더 이상 유효하지 않습니다. 이미 사용되었거나 만료되었을 수 있습니다. 로그인 페이지에서 새 링크를 요청하세요.", - "service_unavailable": "이 서버에서는 매직 링크 로그인이 활성화되어 있지 않습니다.", - "internal_error": "로그인 중 오류가 발생했습니다. 다시 시도해 주세요.", - "resend_failure": "링크 전송 중 오류가 발생했습니다. 다시 시도해 주세요.", - "cross_browser_title": "이 기기에서 로그인을 계속하시겠습니까?", - "cross_browser_body": "요청한 곳과 다른 브라우저나 기기에서 이 로그인 링크를 열었습니다.", - "cross_browser_warning": "이 링크를 본인이 요청했다면 안전하게 계속할 수 있습니다. 그렇지 않다면 이 페이지를 닫으세요 — 계속을 클릭하면 다른 사람이 당신의 계정에 로그인하게 됩니다.", - "cross_browser_continue": "계속하고 로그인", - "resend_confirmation_title": "받은편지함을 확인하세요", - "resend_confirmation_body": "로그인 링크가 활성 계정의 것이었다면 새 링크가 방금 전송되었습니다. 받은편지함을 확인하세요.", - "return_link": "OxiCloud로 돌아가기" - }, - "email": { - "invitation": { - "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", - "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n아래 링크를 클릭하여 여세요:\n{{link}}\n\n링크는 한 번만 사용 가능하며 {{ttl_hours}}시간 후에 만료됩니다.\n이 초대를 예상하지 못했다면 이 메시지를 무시하셔도 됩니다.\n\n— OxiCloud" - }, - "login": { - "subject": "OxiCloud 로그인", - "body": "안녕하세요,\n\n아래 링크를 사용하여 OxiCloud에 로그인하세요. 링크는 한 번만 사용 가능하며 {{ttl_minutes}}분 후에 만료됩니다. 요청한 것과 동일한 기기에서 여세요.\n\n{{link}}\n\n이 로그인 링크를 요청하지 않으셨다면 이 메시지를 무시하셔도 됩니다 — 추가 조치가 필요하지 않습니다.\n\n— OxiCloud" - }, - "kind_file": "파일", - "kind_folder": "폴더", - "english_fallback_divider": "--- 영어 버전은 아래 ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "이 로그인 링크는 더 이상 유효하지 않습니다", + "expired_body": "링크가 만료되었거나 이미 사용되었을 수 있습니다. 새 링크를 보내드릴 수 있습니다 — 몇 초 안에 받은편지함에 도착합니다.", + "resend_to": "{{email}}로 새 링크 보내기", + "generic_unavailable": "이 로그인 링크는 더 이상 유효하지 않습니다. 이미 사용되었거나 만료되었을 수 있습니다. 로그인 페이지에서 새 링크를 요청하세요.", + "service_unavailable": "이 서버에서는 매직 링크 로그인이 활성화되어 있지 않습니다.", + "internal_error": "로그인 중 오류가 발생했습니다. 다시 시도해 주세요.", + "resend_failure": "링크 전송 중 오류가 발생했습니다. 다시 시도해 주세요.", + "cross_browser_title": "이 기기에서 로그인을 계속하시겠습니까?", + "cross_browser_body": "요청한 곳과 다른 브라우저나 기기에서 이 로그인 링크를 열었습니다.", + "cross_browser_warning": "이 링크를 본인이 요청했다면 안전하게 계속할 수 있습니다. 그렇지 않다면 이 페이지를 닫으세요 — 계속을 클릭하면 다른 사람이 당신의 계정에 로그인하게 됩니다.", + "cross_browser_continue": "계속하고 로그인", + "resend_confirmation_title": "받은편지함을 확인하세요", + "resend_confirmation_body": "로그인 링크가 활성 계정의 것이었다면 새 링크가 방금 전송되었습니다. 받은편지함을 확인하세요.", + "return_link": "OxiCloud로 돌아가기" + }, + "email": { + "invitation": { + "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", + "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n아래 링크를 클릭하여 여세요:\n{{link}}\n\n링크는 한 번만 사용 가능하며 {{ttl_hours}}시간 후에 만료됩니다.\n이 초대를 예상하지 못했다면 이 메시지를 무시하셔도 됩니다.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", - "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n새 공유 항목을 확인하려면 OxiCloud를 여세요:\n{{login_link}}\n\n{{inviter}}님이 추가로 공유한 항목이 있을 수 있습니다 — 로그인하여 공유받은 모든 항목을 확인하세요.\n\n— OxiCloud\n\nOxiCloud 계정이 있고 공유 알림 기본 설정이 켜져 있어 이 메시지를 받았습니다. 프로필에서 끌 수 있습니다(다른 사람이 나에게 공유할 때 이메일로 알림 받기)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "미니멀리스트 클라우드 스토리지 시스템" - }, - "nav": { - "files": "파일", - "shared": "공유", - "recent": "최근", - "favorites": "즐겨찾기", - "photos": "사진", - "music": "음악", - "trash": "휴지통", - "sharedwithme": "나와 공유됨" - }, - "photos": { - "empty_state": "아직 사진이 없습니다", - "empty_hint": "이미지나 동영상을 업로드하면 여기에 표시됩니다", - "items_selected": "개 선택됨", - "view_daily": "일", - "view_monthly": "월", - "view_yearly": "년" - }, - "music": { - "create_playlist": "재생목록 만들기", - "playlists": "재생목록", - "no_playlists": "아직 재생목록이 없습니다", - "select_playlist": "재생목록 선택", - "select_hint": "사이드바에서 재생목록을 선택하거나 새로 만드세요", - "add_tracks": "트랙 추가", - "no_tracks": "이 재생목록에 트랙이 없습니다", - "unknown_artist": "알 수 없는 아티스트", - "unknown_title": "알 수 없음", - "confirm_delete": "이 재생목록을 삭제하시겠습니까?", - "playlist_name": "재생목록 이름", - "create": "만들기", - "delete": "삭제", - "share": "공유", - "edit": "편집", - "play_all": "전체 재생", - "shuffle": "셔플", - "repeat": "반복", - "repeat_one": "한 곡 반복", - "queue": "대기열", - "queue_empty": "대기열이 비어 있습니다", - "not_playing": "재생 중이 아닙니다", - "play": "재생", - "pause": "일시정지", - "previous": "이전", - "next": "다음", - "volume": "볼륨", - "mute": "음소거", - "unmute": "음소거 해제", - "title": "제목", - "artist": "아티스트", - "album": "앨범", - "tracks": "개 트랙", - "add": "추가", - "added": "추가됨!", - "added_to_playlist": "플레이리스트에 추가됨", - "add_to_playlist": "플레이리스트에 추가", - "load_error": "플레이리스트 로드 오류", - "add_error": "트랙을 플레이리스트에 추가할 수 없습니다", - "no_playlists_yet": "플레이리스트가 없습니다. 먼저 하나를 만드세요!", - "selected_files": "선택됨:", - "error": "오류", - "search_audio": "오디오 파일 검색…", - "no_audio_files": "오디오 파일을 찾을 수 없습니다", - "selected": "선택됨", - "loading": "로딩 중…", - "search_error": "오디오 파일을 불러올 수 없습니다", - "adding": "추가 중…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "파일 검색...", - "new_folder": "새 폴더", - "upload": "업로드", - "upload_files": "파일 업로드", - "upload_folder": "폴더 업로드", - "upload.uploading": "업로드 중...", - "upload.complete": "{count} / {total} 업로드 완료", - "upload.files": "파일", - "rename": "이름 변경", - "move": "이동...", - "move_to": "이동 대상", - "delete": "삭제", - "download": "다운로드", - "view": "보기", - "cancel": "취소", - "confirm": "확인", - "share": "공유", - "favorite": "즐겨찾기 추가", - "unfavorite": "즐겨찾기 해제", - "copy": "복사", - "notify": "알림", - "send": "보내기", - "clear_recent": "최근 항목 지우기", - "logout": "로그아웃", - "create": "만들기", - "search_btn": "검색", - "close": "닫기", - "delete_permanently": "영구 삭제", - "empty_trash": "휴지통 비우기", - "open_parent_folder": "상위 폴더로 이동", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "외관", - "about": "OxiCloud 정보", - "about_description": "Rust와 Clean Architecture로 구축된 클라우드 스토리지 플랫폼. 빠르고, 안전하고, 프라이빗합니다.", - "admin_panel": "관리자 패널", - "profile": "내 프로필", - "role_user": "사용자", - "theme": { - "light": "라이트", - "dark": "다크", - "auto": "시스템과 동일" + "login": { + "subject": "OxiCloud 로그인", + "body": "안녕하세요,\n\n아래 링크를 사용하여 OxiCloud에 로그인하세요. 링크는 한 번만 사용 가능하며 {{ttl_minutes}}분 후에 만료됩니다. 요청한 것과 동일한 기기에서 여세요.\n\n{{link}}\n\n이 로그인 링크를 요청하지 않으셨다면 이 메시지를 무시하셔도 됩니다 — 추가 조치가 필요하지 않습니다.\n\n— OxiCloud" }, - "manage_groups": "그룹 관리" + "kind_file": "파일", + "kind_folder": "폴더", + "english_fallback_divider": "--- 영어 버전은 아래 ---" + } }, - "share": { - "dialogTitle": "공유 링크", - "linkLabel": "공유 링크:", - "copyLink": "복사", - "permissions": "권한:", - "permissionRead": "읽기", - "permissionWrite": "쓰기", - "permissionReshare": "재공유", - "password": "비밀번호 보호:", - "generatePassword": "생성", - "expiration": "만료일:", - "update": "공유 업데이트", - "remove": "공유 삭제", - "notifyTitle": "알림 보내기", - "notifyEmailLabel": "이메일 주소:", - "notifyMessageLabel": "메시지 (선택사항):", - "notifySend": "알림 보내기", - "shareWithOthers": "다른 사용자와 공유", - "sharePublicly": "공개 공유", - "shareSettings": "공유 설정", - "shareCopied": "링크가 클립보드에 복사되었습니다", - "shareCreated": "공유 링크가 성공적으로 생성되었습니다", - "shareUpdated": "공유 설정이 성공적으로 업데이트되었습니다", - "shareRemoved": "공유가 성공적으로 삭제되었습니다", - "inviteByEmail": "이메일로 초대 — 초대장이 전송됩니다", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "공유 링크", - "share_linkLabel": "공유 링크:", - "share_copyLink": "복사", - "share_permissions": "권한:", - "share_permissionRead": "읽기", - "share_permissionWrite": "쓰기", - "share_permissionReshare": "재공유", - "share_password": "비밀번호 보호:", - "share_generatePassword": "생성", - "share_expiration": "만료일:", - "share_update": "공유 업데이트", - "share_remove": "공유 삭제", - "share_notifyTitle": "알림 보내기", - "share_notifyEmailLabel": "이메일 주소:", - "share_notifyMessageLabel": "메시지 (선택사항):", - "share_notifySend": "알림 보내기", - "shared": { - "backToFiles": "파일로 돌아가기", - "pageTitle": "공유 리소스", - "pageDescription": "공유 파일 및 폴더 관리", - "filterType": "유형:", - "filterAll": "전체", - "filterFiles": "파일", - "filterFolders": "폴더", - "sortBy": "정렬:", - "sortByName": "이름", - "sortByDate": "공유일", - "sortByExpiration": "만료일", - "search": "검색", - "colName": "이름", - "colType": "유형", - "colDateShared": "공유일", - "colExpiration": "만료일", - "colPermissions": "권한", - "colPassword": "비밀번호", - "colActions": "작업", - "emptyStateTitle": "아직 공유된 리소스가 없습니다", - "emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", - "goToFiles": "파일로 이동", - "typeFile": "파일", - "typeFolder": "폴더", - "noExpiration": "만료 없음", - "hasPassword": "있음", - "noPassword": "없음", - "editShare": "공유 편집", - "notifyShare": "알림", - "copyLink": "링크 복사", - "removeShare": "공유 삭제", - "linkCopied": "링크가 클립보드에 복사되었습니다!", - "linkCopyFailed": "링크 복사에 실패했습니다", - "itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", - "itemRemoved": "공유가 성공적으로 삭제되었습니다", - "invalidEmail": "유효한 이메일 주소를 입력하세요", - "notificationSent": "알림이 성공적으로 전송되었습니다", - "notificationFailed": "알림 전송에 실패했습니다", - "shared_backToFiles": "파일로 돌아가기", - "shared_pageTitle": "공유 리소스", - "shared_pageDescription": "공유 파일 및 폴더 관리", - "shared_filterType": "유형:", - "shared_filterAll": "전체", - "shared_filterFiles": "파일", - "shared_filterFolders": "폴더", - "shared_sortBy": "정렬:", - "shared_sortByName": "이름", - "shared_sortByDate": "공유일", - "shared_sortByExpiration": "만료일", - "shared_search": "검색", - "shared_colName": "이름", - "shared_colType": "유형", - "shared_colDateShared": "공유일", - "shared_colExpiration": "만료일", - "shared_colPermissions": "권한", - "shared_colPassword": "비밀번호", - "shared_colActions": "작업", - "shared_emptyStateTitle": "아직 공유된 리소스가 없습니다", - "shared_emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", - "shared_goToFiles": "파일로 이동", - "shared_typeFile": "파일", - "shared_typeFolder": "폴더", - "shared_noExpiration": "만료 없음", - "shared_hasPassword": "있음", - "shared_noPassword": "없음", - "shared_editShare": "공유 편집", - "shared_notifyShare": "알림", - "shared_copyLink": "링크 복사", - "shared_removeShare": "공유 삭제", - "shared_linkCopied": "링크가 클립보드에 복사되었습니다!", - "shared_linkCopyFailed": "링크 복사에 실패했습니다", - "shared_itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", - "shared_itemRemoved": "공유가 성공적으로 삭제되었습니다", - "shared_invalidEmail": "유효한 이메일 주소를 입력하세요", - "shared_notificationSent": "알림이 성공적으로 전송되었습니다", - "shared_notificationFailed": "알림 전송에 실패했습니다" - }, - "files": { - "name": "이름", - "type": "유형", - "size": "크기", - "modified": "수정일", - "no_files": "이 폴더에 파일이 없습니다", - "empty_hint": "파일을 업로드하거나 폴더를 만들어 시작하세요", - "loading": "파일 로딩 중…", - "view_grid": "그리드 보기", - "view_list": "목록 보기", - "file_types": { - "document": "문서", - "image": "이미지", - "video": "동영상", - "audio": "오디오", - "pdf": "PDF", - "text": "텍스트", - "folder": "폴더", - "spreadsheet": "스프레드시트", - "presentation": "프레젠테이션", - "archive": "아카이브", - "installer": "설치 프로그램", - "code": "코드" - }, - "owner": "소유자" - }, - "dialogs": { - "rename_folder": "폴더 이름 변경", - "rename_file": "파일 이름 변경", - "new_name": "새 이름", - "new_folder_title": "새 폴더", - "folder_name": "폴더 이름", - "folder_placeholder": "내 폴더", - "rename_title": "이름 변경", - "move_file": "파일 이동", - "move_folder": "폴더 이동", - "select_destination": "대상 폴더를 선택하세요:", - "select_this_folder": "이 폴더 선택", - "go_to_parent": ".. (상위 폴더)", - "no_subfolders": "하위 폴더 없음", - "root": "루트", - "delete_confirmation": "정말 삭제하시겠습니까", - "and_contents": "및 모든 내용", - "no_undo": "이 작업은 되돌릴 수 없습니다", - "confirm_title": "작업 확인", - "confirm_delete": "휴지통으로 이동", - "confirm_delete_file": "파일 «{{name}}»을(를) 휴지통으로 이동하시겠습니까?", - "confirm_delete_folder": "폴더 «{{name}}» 및 모든 내용을 휴지통으로 이동하시겠습니까?", - "confirm_permanent_delete": "영구 삭제", - "confirm_permanent_delete_msg": "이 항목을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", - "confirm_empty_trash": "휴지통 비우기", - "confirm_delete_share": "공유 링크 삭제", - "confirm_delete_share_msg": "이 공유 링크를 삭제하시겠습니까?", - "share_file": "파일 공유", - "share_folder": "폴��� 공유", - "existing_shares": "기존 공유", - "share_options": "공유 옵션", - "password": "비밀번호", - "expiration": "만료일", - "permissions": "권한", - "generated_link": "생성된 링크", - "notify": "알림 보내기", - "recipient": "수신자", - "message": "메시지", - "move_to_home": "홈 폴더로 이동" - }, - "dropzone": { - "drag_files": "여기에 파일을 드래그하거나 클릭하여 선택하세요", - "drop_files": "파일을 놓아 업로드하세요" - }, - "permissions": { - "read": "읽기", - "write": "쓰기", - "reshare": "재공유" - }, - "errors": { - "file_not_found": "파일을 찾을 수 없습니다", - "folder_not_found": "폴더를 찾을 수 없습니다", - "delete_error": "삭제 오류", - "upload_error": "파일 업로드 오류", - "rename_error": "이름 변경 오류", - "move_error": "이동 오류", - "empty_name": "이름은 비워둘 수 없습니다", - "name_exists": "같은 이름의 파일 또는 폴더가 이미 존재합니다", - "generic_error": "오류가 발생했습니다", - "group_name_invalid": "그룹 이름은 이메일 접두사 형식과 일치해야 합니다(문자, 숫자, 점, 대시, 밑줄; 1–64자).", - "group_cycle": "이 구성원은 그룹 간 순환 참조를 만들 것입니다.", - "group_depth_exceeded": "중첩 깊이가 허용 최대값(8)을 초과합니다.", - "group_virtual_immutable": "«Internal» 그룹은 시스템이 관리하며 수정할 수 없습니다.", - "group_not_found": "그룹을 찾을 수 없습니다.", - "group_name_taken": "이 이름의 그룹이 이미 존재합니다." - }, - "breadcrumb": { - "home": "홈" - }, - "trash": { - "empty_trash": "휴지통 비우기", - "empty_state": "휴지통이 비어 있습니다", - "original_location": "원래 위치", - "deleted_date": "삭제일", - "remaining": "남음", - "actions": "작업", - "restore": "복원", - "delete_permanently": "영구 삭제", - "empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다.", - "groupby": { - "remaining_days": "남은 일수", - "trashed_time": "삭제 시간" - } - }, - "daysRemaining": { - "expired": "만료됨", - "today": "오늘", - "tomorrow": "내일", - "inDays": "{{count}}일" - }, - "expiryChip": { - "never": "만료되지 않음", - "expired": "만료됨", - "today": "오늘 만료", - "tomorrow": "내일 만료", - "inDays": "{{count}}일 후 만료", - "onDate": "{{date}}에 만료" - }, - "auth": { - "login_title": "로그인", - "username": "사용자 이름", - "username_placeholder": "사용자 이름을 입력하세요", - "login_identifier": "사용자 이름 또는 이메일", - "login_identifier_placeholder": "사용자 이름 또는 이메일을 입력하세요", - "password": "비밀번호", - "password_placeholder": "비밀번호를 입력하세요", - "login_button": "로그인", - "no_account": "계정이 없으신가요?", - "register": "가입하기", - "admin_setup": "처음이신가요?", - "setup": "관리자 설정", - "register_title": "계정 만들기", - "email": "이메일", - "email_placeholder": "이메일 주소를 입력하세요", - "confirm_password": "비밀번호 확인", - "confirm_password_placeholder": "비밀번호를 다시 입력하세요", - "register_button": "계정 만들기", - "have_account": "이미 계정이 있으신가요?", - "login": "로그인", - "setup_title": "초기 설정", - "setup_step1": "관리자", - "setup_step2": "시스템", - "setup_step3": "완료", - "admin_username": "관리자 사용자 이름", - "admin_email": "관리자 이메일", - "admin_password": "관리자 비밀번호", - "create_admin": "관리자 생성", - "back_to_login": "이미 설정하셨나요?", - "admin_success": "관리자 계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", - "account_success": "계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", - "passwords_mismatch": "비밀번호가 일치하지 않습니다", - "admin_create_error": "관리자 계정 생성 오류", - "or": "또는", - "sso_login": "SSO로 로그인", - "sso_login_provider": "{{provider}}(으)로 로그인", - "magicLinkHint": "비밀번호가 없으신가요? 이메일을 입력하시면 일회용 로그인 링크를 보내드립니다.", - "magicLinkEmailLabel": "이메일 주소", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "로그인 링크 보내기", - "magicLinkSent": "해당 이메일에 대한 계정이 있는 경우 로그인 링크가 전송되었습니다. 받은편지함을 확인하세요.", - "magicLinkUnavailable": "이 서버에서는 이메일 로그인을 사용할 수 없습니다.", - "magicLinkNetworkError": "서버에 연결할 수 없습니다: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "저장소", - "calculating": "계산 중...", - "used": "{{percentage}}% 사용 중 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "이 파일 형식은 미리보기를 지원하지 않습니다.", - "download_file": "파일 다운로드", - "zoom_in": "확대", - "zoom_out": "축소", - "zoom_reset": "줌 초기화" - }, - "language_selector": { - "title": "환영합니다!", - "subtitle": "계속하려면 언어를 선택하세요", - "continue": "계속", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ko": "한국어", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "아직 즐겨찾기가 없습니다", - "empty_hint": "파일이나 폴더에 별표를 눌러 즐겨찾기에 추가하세요", - "add": "즐겨찾기 추가", - "remove": "즐겨찾기 해제", - "added_title": "즐겨찾기에 추가됨", - "added_msg": "즐겨찾기에 추가되었습니다", - "removed_title": "즐겨찾기에서 삭제됨", - "removed_msg": "즐겨찾기에서 삭제되었습니다" - }, - "recent": { - "title": "최근", - "clear": "최근 항목 지우기", - "accessed": "접근일", - "empty_state": "최근 파일이 없습니다", - "empty_hint": "열어본 파일이 여기에 표시됩니다", - "loadMore": "더 불러오기" - }, - "notifications": { - "file_renamed": "파일 이름이 변경되었습니다", - "file_renamed_to": "파일 이름이 «{{name}}»(으)로 변경되었습니다", - "folder_renamed": "폴더 이름이 변경되었습니다", - "folder_renamed_to": "폴더 이름이 «{{name}}»(으)로 변경되었습니다", - "file_uploaded": "파일이 업로드되었습니다", - "file_deleted": "파일이 휴지통으로 이동되었습니다", - "folder_deleted": "폴더가 휴지통으로 이동되었습니다", - "item_deleted_permanently": "항목이 영구적으로 삭제되었습니다", - "trash_emptied": "휴지통이 성공적으로 비워졌습니다", - "title": "알림", - "empty": "알림이 없습니다", - "link_created": "링크 생성됨", - "share_success": "공유 링크가 성공적으로 생성되었습니다", - "upload_files_section_title": "여기서는 업로드할 수 없습니다", - "upload_files_section_body": "파일을 업로드하려면 파일 섹션으로 이동하세요" - }, - "batch": { - "one_selected": "1개 선택됨", - "n_selected": "{{count}}개 선택됨", - "confirm_delete": "{{count}}개 항목을 휴지통으로 이동하시겠습니까?", - "move_title": "{{count}}개 항목 이동", - "add_favorites": "즐겨찾기에 추가", - "move_copy": "이동 또는 복사" - }, - "admin": { - "page_title": "관리자 패널", - "back_to_app": "OxiCloud로 돌아가기", - "loading": "로딩 중…", - "access_denied": "접근 거부", - "access_denied_desc": "관리자 권한이 필요합니다.", - "sign_in": "로그인", - "tab_dashboard": "대시보드", - "tab_users": "사용자", - "tab_oidc": "SSO / OIDC", - "total_users": "전체 사용자", - "active_users": "활성 사용자", - "admins": "관리자", - "version": "버전", - "storage_overview": "스토리지 개요", - "used": "사용됨", - "total_quota": "총 할당량", - "usage_pct": "사용률", - "users_over_80": "할당량 80% 초과", - "users_over_quota": "할당량 초과", - "system": "시스템", - "auth_label": "인증", - "oidc_label": "OIDC", - "quotas_label": "할당량", - "enabled": "활성화됨", - "disabled": "비활성화됨", - "active": "활성", - "off": "꺼짐", - "allow_registration": "공개 자가 등록 허용", - "registration_warning": "공개 등록이 비활성화되어 있습니다. 관리자만 사용자를 만들 수 있습니다.", - "user_management": "사용자 관리", - "create_user": "사용자 생성", - "col_user": "사용자", - "col_role": "역할", - "col_auth": "인증", - "col_status": "상태", - "col_storage": "스토리지", - "col_last_login": "마지막 로그인", - "col_actions": "작업", - "loading_users": "사용자 로딩 중…", - "failed_load_users": "로드 실패", - "no_users_found": "사용자 없음", - "showing_users": "{{from}}-{{to}} / {{total}} 표시", - "prev": "이전", - "next": "다음", - "inactive": "비활성", - "you_badge": "(나)", - "local": "로컬", - "never": "없음", - "just_now": "방금", - "minutes_ago": "{{n}}분 전", - "hours_ago": "{{n}}시간 전", - "days_ago": "{{n}}일 전", - "edit_quota_title": "할당량 편집", - "reset_password_title": "비밀번호 재설정", - "toggle_role_title": "역할 전환", - "deactivate_title": "비활성화", - "activate_title": "활성화", - "delete_title": "삭제", - "sso_title": "싱글 사인온 (OIDC / SSO)", - "enable_sso": "SSO 인증 활성화", - "provider_name": "제공자 이름", - "issuer_url": "발급자 URL", - "issuer_url_hint": "OpenID Connect 발급자 URL", - "auto_discover": "자동 검색", - "discovering": "검색 중…", - "client_id": "클라이언트 ID", - "client_secret": "클라이언트 시크릿", - "client_secret_placeholder": "현재 값 유지하려면 비워두세요", - "secret_configured": "클라이언트 시크릿 구성됨", - "callback_url": "콜백 URL", - "callback_url_hint": "(IdP에 등록)", - "advanced_settings": "고급 설정", - "scopes": "스코프", - "auto_provision": "첫 로그인 시 자동 프로비저닝", - "admin_groups": "관리자 그룹", - "admin_groups_hint": "쉼표로 구분된 OIDC 그룹 이름", - "disable_password": "비밀번호 로그인 비활성화 (OIDC만)", - "password_warning": "모든 비밀번호 로그인이 차단됩니다!", - "test_btn": "테스트", - "save_btn": "저장", - "saving": "저장 중…", - "settings_saved": "설정 저장됨 — OIDC: {{status}}", - "quota_modal_title": "스토리지 할당량 업데이트", - "quota_user_label": "사용자:", - "new_quota": "새 할당량", - "quota_unlimited_hint": "무제한은 0", - "cancel": "취소", - "create_user_title": "새 사용자 생성", - "username_label": "사용자 이름", - "username_placeholder": "username", - "username_hint": "3–32자", - "password_label": "비밀번호", - "password_placeholder": "최소 8자", - "email_label": "이메일", - "email_optional": "(선택사항)", - "email_placeholder": "user@example.com (비어있으면 자동 생성)", - "role_label": "역할", - "role_user": "사용자", - "role_admin": "관리자", - "quota_label": "할당량", - "creating": "생성 중…", - "reset_pw_title": "비밀번호 재설정", - "new_password_label": "새 비밀번호", - "resetting": "재설정 중…", - "reset_btn": "재설정", - "confirm_role_change": "역할을 {{role}}(으)로 변경?", - "confirm_deactivate": "이 사용자를 비활성화하시겠습니까?", - "confirm_activate": "이 사용자를 활성화하시겠습니까?", - "confirm_delete_user": "사용자 \"{{name}}\" 삭제? 되돌릴 수 없습니다!", - "confirm_action": "작업 확인", - "confirm_yes": "확인", - "confirm_no": "취소", - "error_username_short": "사용자 이름 최소 3자", - "error_password_short": "비밀번호 최소 8자", - "error_generic": "실패", - "error_network": "네트워크 오류: {{message}}", - "error_create_user": "사용자 생성 실패", - "tab_storage": "저장소", - "storage_title": "저장소 구성", - "storage_current_backend": "현재 백엔드", - "storage_total_blobs": "총 블롭 수", - "storage_total_size": "총 크기", - "storage_dedup_ratio": "중복 제거 비율", - "storage_backend": "백엔드", - "storage_local": "로컬", - "storage_s3": "S3 호환", - "storage_provider_preset": "공급자 프리셋", - "storage_preset_custom": "사용자 지정", - "storage_endpoint_url": "엔드포인트 URL", - "storage_endpoint_hint": "AWS S3의 경우 비워두세요", - "storage_bucket": "버킷", - "storage_region": "지역", - "storage_access_key": "액세스 키", - "storage_secret_key": "시크릿 키", - "storage_secret_configured": "키 구성됨", - "storage_key_placeholder": "새 키 입력", - "storage_path_style": "경로 스타일 강제", - "storage_path_style_hint": "MinIO 및 일부 S3 호환 서비스에 필요", - "storage_test_connection": "연결 테스트", - "storage_test_success": "연결 성공", - "storage_test_failure": "연결 실패", - "storage_save": "구성 저장", - "storage_saved": "구성이 저장되었습니다", - "storage_migration": "데이터 마이그레이션", - "storage_migration_coming_soon": "마이그레이션 도구 곧 출시", - "migration_status_label": "마이그레이션 상태", - "migration_start": "마이그레이션 시작", - "migration_pause": "일시 중지", - "migration_resume": "재개", - "migration_verify": "확인", - "migration_complete": "완료", - "migration_started": "마이그레이션 시작됨", - "migration_paused_msg": "마이그레이션 일시 중지됨", - "migration_resumed_msg": "마이그레이션 재개됨", - "migration_completed_msg": "마이그레이션이 성공적으로 완료되었습니다", - "migration_verifying": "확인 중...", - "migration_verify_passed": "확인 통과", - "migration_verify_failed": "확인 실패", - "migration_failed_blobs": "실패한 블롭", - "testing": "테스트 중...", - "smtp_disabled": "비활성화됨 (호스트 미설정)", - "smtp_enabled": "활성화됨", - "smtp_enabled_label": "상태", - "smtp_intro": "SMTP는 환경 변수(OXICLOUD_SMTP_*)로만 구성됩니다. 아래 값들은 실행 중인 서버에서 읽어옵니다 — 변경하려면 환경을 수정하고 OxiCloud를 다시 시작하세요.", - "smtp_not_configured": "이 서버에는 SMTP가 구성되어 있지 않습니다.", - "smtp_send_failed": "전송 실패.", - "smtp_send_test": "테스트 이메일 보내기", - "smtp_sending": "보내는 중…", - "smtp_sent": "테스트 이메일을 보냈습니다.", - "smtp_server_code": "서버 응답", - "smtp_test_intro": "아래 수신자에게 미리 정의된 진단 메시지를 보내고 SMTP 서버의 응답을 표시합니다. 이를 통해 릴레이 로그와 대조하여 확인할 수 있습니다.", - "smtp_test_missing_to": "수신자 주소를 입력하세요.", - "smtp_test_title": "테스트 이메일 보내기", - "smtp_test_to": "수신자 주소", - "smtp_title": "발신 이메일 (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "프로필", - "back_to_app": "OxiCloud로 돌아가기", - "loading": "로딩 중…", - "not_authenticated": "인증되지 않음", - "not_authenticated_desc": "프로필을 보려면 로그인하세요.", - "sign_in": "로그인", - "role_admin": "관리자", - "role_user": "사용자", - "account_details": "계정 정보", - "username": "사용자 이름", - "email": "이메일", - "role": "역할", - "last_login": "마지막 로그인", - "storage": "스토리지", - "used": "사용됨", - "quota": "할당량", - "usage": "사용률", - "unlimited": "무제한", - "app_passwords": "앱 비밀번호", - "app_pw_desc": "WebDAV, CalDAV, CardDAV 클라이언트용 비밀번호를 생성합니다. 각 비밀번호는 한 번만 표시됩니다.", - "app_pw_label_placeholder": "라벨 (예: Thunderbird, macOS)", - "generate": "생성", - "generating": "생성 중…", - "new_password_for": "새 비밀번호:", - "copy_warning": "지금 이 비밀번호를 복사하세요. 다시 볼 수 없습니다.", - "copy_to_clipboard": "클립보드에 복사", - "col_label": "라벨", - "col_created": "생성일", - "col_last_used": "마지막 사용", - "col_status": "상태", - "active": "활성", - "revoked": "취소됨", - "revoke_title": "취소", - "no_app_passwords": "앱 비밀번호가 아직 없습니다.", - "client_sessions": "클라이언트 세션", - "client_sessions_desc": "Nextcloud 호환 클라이언트 연결 시 자동 생성됩니다.", - "col_client": "클라이언트", - "never": "없음", - "just_now": "방금", - "minutes_ago": "{{n}}분 전", - "hours_ago": "{{n}}시간 전", - "days_ago": "{{n}}일 전", - "edit_profile": "프로필 편집", - "edit_oidc_managed": "정보(이름, 성, 프로필 사진 등)를 변경하려면 ID 공급자에서 업데이트하세요. 변경 사항은 다음 로그인 시 반영됩니다.", - "username_claim_hint": "2~64자, 영문자 / 숫자 / 점 / 하이픈 / 밑줄. 선택한 후에는 사용자 이름을 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", - "username_already_claimed": "사용자 이름이 설정되어 있어 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", - "given_name": "이름", - "family_name": "성", - "notify_on_share": "다른 사람이 나에게 공유할 때 이메일로 알림 받기", - "notify_on_share_hint": "선택을 해제해도 공유 항목은 계정에 계속 표시되지만, 이메일 알림은 받지 않습니다.", - "save_profile": "변경 사항 저장", - "profile_saved": "프로필이 업데이트되었습니다", - "profile_no_changes": "저장할 변경 사항이 없습니다.", - "profile_save_failed": "저장 실패", - "username_taken_error": "이미 사용 중인 사용자 이름입니다.", - "username_immutable_error": "사용자 이름이 이미 설정되어 있어 여기서 변경할 수 없습니다. 이름을 변경하려면 관리자에게 문의하세요.", - "change_password": "비밀번호 변경", - "current_password": "현재 비밀번호", - "new_password": "새 비밀번호", - "min_8_chars": "최소 8자", - "confirm_password": "새 비밀번호 확인", - "update_password": "비밀번호 업데이트", - "updating": "업데이트 중…", - "password_updated": "비밀번호가 성공적으로 업데이트되었습니다", - "passwords_no_match": "비밀번호가 일치하지 않습니다", - "password_too_short": "비밀번호는 최소 8자여야 합니다", - "password_change_failed": "비밀번호 변경 실패", - "error_network": "네트워크 오류: {{message}}", - "error_label_required": "라벨을 입력하세요", - "error_create_pw": "앱 비밀번호 생성 실패", - "confirm_revoke": "앱 비밀번호 \"{{label}}\"을(를) 취소하시겠습니까? 이 비밀번호를 사용하는 클라이언트가 작동하지 않게 됩니다.", - "error_revoke": "취소 실패", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "업로드 중...", - "files": "파일", - "complete": "{{count}} / {{total}} 업로드됨" - }, - "storage_quota_exceeded": "저장 공간 할당량 초과", - "sharedwithme": { - "pageTitle": "나와 공유됨", - "pageDescription": "다른 사용자가 나와 공유한 파일 및 폴더", - "emptyStateTitle": "아직 공유된 항목이 없습니다", - "emptyStateDesc": "다른 사용자가 공유한 항목이 여기에 표시됩니다", - "loadMore": "더 불러오기", - "sharedBy": "공유한 사람", - "colName": "이름", - "colType": "유형", - "colSharedBy": "공유한 사람", - "colDate": "공유 날짜", - "colPermissions": "권한" - }, - "groupby": { - "none": "없음", - "title": "그룹화 기준", - "owner": "소유자", - "shareDate": "공유 날짜", - "type": "유형", - "type.folders": "폴더", - "accessedAt": "접근 날짜", - "modifiedAt": "수정 날짜", - "createdAt": "생성 날짜", - "size": "크기", - "favoriteDate": "즐겨찾기 날짜", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "새 항목" - }, - "dateBucket": { - "today": "오늘", - "last7days": "최근 7일", - "last30days": "최근 30일" - }, - "groups": { - "title": "그룹 관리", - "create_button": "그룹 생성", - "create_dialog_title": "새 그룹", - "edit_dialog_title": "그룹 이름 변경", - "name_label": "이름", - "name_placeholder": "engineering", - "description_label": "설명(선택사항)", - "members_section": "구성원", - "add_member_placeholder": "사용자 또는 그룹 추가…", - "no_members": "아직 구성원이 없습니다.", - "remove_member": "제거", - "delete_group": "그룹 삭제", - "delete_confirm": "\"{name}\" 그룹을 삭제하시겠습니까? 이 그룹을 참조하는 모든 권한이 해제됩니다.", - "empty_state": "아직 그룹이 없습니다.", - "load_more": "더 보기", - "back_to_list": "뒤로", - "loading": "로딩 중…", - "virtual_badge": "시스템", - "member_count_zero": "구성원 없음", - "member_count_one": "구성원 1명", - "member_count_other": "구성원 {count}명", - "delete_confirm_label": "확인을 위해 그룹 이름을 입력하세요:", - "delete_confirm_mismatch": "확인을 위해 그룹 이름을 정확히 입력하세요.", - "virtual_internal_name": "내부", - "members_loading": "구성원 로딩 중…", - "members_empty": "구성원 없음", - "virtual_internal_explanation": "이 서버의 모든 내부 사용자" - }, - "myshares": { - "copyLink": "링크 복사", - "deleteLink": "링크 삭제", - "notifyByEmail": "이메일로 알림", - "notifyFailed": "알림을 보낼 수 없습니다.", - "notifyGroupMembers": "그룹 구성원에게 알림", - "notifyRateLimited": "이 수신자에게 알림이 너무 많습니다 — 나중에 다시 시도하세요.", - "removeAccess": "액세스 제거", - "resendInvitation": "초대 이메일 다시 보내기" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", + "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n새 공유 항목을 확인하려면 OxiCloud를 여세요:\n{{login_link}}\n\n{{inviter}}님이 추가로 공유한 항목이 있을 수 있습니다 — 로그인하여 공유받은 모든 항목을 확인하세요.\n\n— OxiCloud\n\nOxiCloud 계정이 있고 공유 알림 기본 설정이 켜져 있어 이 메시지를 받았습니다. 프로필에서 끌 수 있습니다(다른 사람이 나에게 공유할 때 이메일로 알림 받기)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "미니멀리스트 클라우드 스토리지 시스템" + }, + "nav": { + "files": "파일", + "shared": "공유", + "recent": "최근", + "favorites": "즐겨찾기", + "photos": "사진", + "music": "음악", + "trash": "휴지통", + "sharedwithme": "나와 공유됨", + "profile": "프로필", + "shared_with_me": "나와 공유됨" + }, + "photos": { + "empty_state": "아직 사진이 없습니다", + "empty_hint": "이미지나 동영상을 업로드하면 여기에 표시됩니다", + "items_selected": "개 선택됨", + "view_daily": "일", + "view_monthly": "월", + "view_yearly": "년", + "group_by": "그룹화 기준" + }, + "music": { + "create_playlist": "재생목록 만들기", + "playlists": "재생목록", + "no_playlists": "아직 재생목록이 없습니다", + "select_playlist": "재생목록 선택", + "select_hint": "사이드바에서 재생목록을 선택하거나 새로 만드세요", + "add_tracks": "트랙 추가", + "no_tracks": "이 재생목록에 트랙이 없습니다", + "unknown_artist": "알 수 없는 아티스트", + "unknown_title": "알 수 없음", + "confirm_delete": "이 재생목록을 삭제하시겠습니까?", + "playlist_name": "재생목록 이름", + "create": "만들기", + "delete": "삭제", + "share": "공유", + "edit": "편집", + "play_all": "전체 재생", + "shuffle": "셔플", + "repeat": "반복", + "repeat_one": "한 곡 반복", + "queue": "대기열", + "queue_empty": "대기열이 비어 있습니다", + "not_playing": "재생 중이 아닙니다", + "play": "재생", + "pause": "일시정지", + "previous": "이전", + "next": "다음", + "volume": "볼륨", + "mute": "음소거", + "unmute": "음소거 해제", + "title": "제목", + "artist": "아티스트", + "album": "앨범", + "tracks": "개 트랙", + "add": "추가", + "added": "추가됨!", + "added_to_playlist": "플레이리스트에 추가됨", + "add_to_playlist": "플레이리스트에 추가", + "load_error": "플레이리스트 로드 오류", + "add_error": "트랙을 플레이리스트에 추가할 수 없습니다", + "no_playlists_yet": "플레이리스트가 없습니다. 먼저 하나를 만드세요!", + "selected_files": "선택됨:", + "error": "오류", + "search_audio": "오디오 파일 검색…", + "no_audio_files": "오디오 파일을 찾을 수 없습니다", + "selected": "선택됨", + "loading": "로딩 중…", + "search_error": "오디오 파일을 불러올 수 없습니다", + "adding": "추가 중…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "이전" + }, + "actions": { + "search": "파일 검색...", + "new_folder": "새 폴더", + "upload": "업로드", + "upload_files": "파일 업로드", + "upload_folder": "폴더 업로드", + "upload.uploading": "업로드 중...", + "upload.complete": "{count} / {total} 업로드 완료", + "upload.files": "파일", + "rename": "이름 변경", + "move": "이동...", + "move_to": "이동 대상", + "delete": "삭제", + "download": "다운로드", + "view": "보기", + "cancel": "취소", + "confirm": "확인", + "share": "공유", + "favorite": "즐겨찾기 추가", + "unfavorite": "즐겨찾기 해제", + "copy": "복사", + "notify": "알림", + "send": "보내기", + "clear_recent": "최근 항목 지우기", + "logout": "로그아웃", + "create": "만들기", + "search_btn": "검색", + "close": "닫기", + "delete_permanently": "영구 삭제", + "empty_trash": "휴지통 비우기", + "open_parent_folder": "상위 폴더로 이동", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "외관", + "about": "OxiCloud 정보", + "about_description": "Rust와 Clean Architecture로 구축된 클라우드 스토리지 플랫폼. 빠르고, 안전하고, 프라이빗합니다.", + "admin_panel": "관리자 패널", + "profile": "내 프로필", + "role_user": "사용자", + "theme": { + "light": "라이트", + "dark": "다크", + "auto": "시스템과 동일" + }, + "manage_groups": "그룹 관리", + "admin": "관리자" + }, + "share": { + "dialogTitle": "공유 링크", + "linkLabel": "공유 링크:", + "copyLink": "복사", + "permissions": "권한:", + "permissionRead": "읽기", + "permissionWrite": "쓰기", + "permissionReshare": "재공유", + "password": "비밀번호 보호:", + "generatePassword": "생성", + "expiration": "만료일:", + "update": "공유 업데이트", + "remove": "공유 삭제", + "notifyTitle": "알림 보내기", + "notifyEmailLabel": "이메일 주소:", + "notifyMessageLabel": "메시지 (선택사항):", + "notifySend": "알림 보내기", + "shareWithOthers": "다른 사용자와 공유", + "sharePublicly": "공개 공유", + "shareSettings": "공유 설정", + "shareCopied": "링크가 클립보드에 복사되었습니다", + "shareCreated": "공유 링크가 성공적으로 생성되었습니다", + "shareUpdated": "공유 설정이 성공적으로 업데이트되었습니다", + "shareRemoved": "공유가 성공적으로 삭제되었습니다", + "inviteByEmail": "이메일로 초대 — 초대장이 전송됩니다", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "복사", + "copy_failed": "Could not copy link", + "download": "다운로드", + "files": "파일", + "folders": "폴더", + "link_name": "Link name (optional)", + "notifyByEmail": "이메일로 알림", + "revoke": "Remove", + "role_label": "역할" + }, + "share_dialogTitle": "공유 링크", + "share_linkLabel": "공유 링크:", + "share_copyLink": "복사", + "share_permissions": "권한:", + "share_permissionRead": "읽기", + "share_permissionWrite": "쓰기", + "share_permissionReshare": "재공유", + "share_password": "비밀번호 보호:", + "share_generatePassword": "생성", + "share_expiration": "만료일:", + "share_update": "공유 업데이트", + "share_remove": "공유 삭제", + "share_notifyTitle": "알림 보내기", + "share_notifyEmailLabel": "이메일 주소:", + "share_notifyMessageLabel": "메시지 (선택사항):", + "share_notifySend": "알림 보내기", + "shared": { + "backToFiles": "파일로 돌아가기", + "pageTitle": "공유 리소스", + "pageDescription": "공유 파일 및 폴더 관리", + "filterType": "유형:", + "filterAll": "전체", + "filterFiles": "파일", + "filterFolders": "폴더", + "sortBy": "정렬:", + "sortByName": "이름", + "sortByDate": "공유일", + "sortByExpiration": "만료일", + "search": "검색", + "colName": "이름", + "colType": "유형", + "colDateShared": "공유일", + "colExpiration": "만료일", + "colPermissions": "권한", + "colPassword": "비밀번호", + "colActions": "작업", + "emptyStateTitle": "아직 공유된 리소스가 없습니다", + "emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", + "goToFiles": "파일로 이동", + "typeFile": "파일", + "typeFolder": "폴더", + "noExpiration": "만료 없음", + "hasPassword": "있음", + "noPassword": "없음", + "editShare": "공유 편집", + "notifyShare": "알림", + "copyLink": "링크 복사", + "removeShare": "공유 삭제", + "linkCopied": "링크가 클립보드에 복사되었습니다!", + "linkCopyFailed": "링크 복사에 실패했습니다", + "itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", + "itemRemoved": "공유가 성공적으로 삭제되었습니다", + "invalidEmail": "유효한 이메일 주소를 입력하세요", + "notificationSent": "알림이 성공적으로 전송되었습니다", + "notificationFailed": "알림 전송에 실패했습니다", + "shared_backToFiles": "파일로 돌아가기", + "shared_pageTitle": "공유 리소스", + "shared_pageDescription": "공유 파일 및 폴더 관리", + "shared_filterType": "유형:", + "shared_filterAll": "전체", + "shared_filterFiles": "파일", + "shared_filterFolders": "폴더", + "shared_sortBy": "정렬:", + "shared_sortByName": "이름", + "shared_sortByDate": "공유일", + "shared_sortByExpiration": "만료일", + "shared_search": "검색", + "shared_colName": "이름", + "shared_colType": "유형", + "shared_colDateShared": "공유일", + "shared_colExpiration": "만료일", + "shared_colPermissions": "권한", + "shared_colPassword": "비밀번호", + "shared_colActions": "작업", + "shared_emptyStateTitle": "아직 공유된 리소스가 없습니다", + "shared_emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", + "shared_goToFiles": "파일로 이동", + "shared_typeFile": "파일", + "shared_typeFolder": "폴더", + "shared_noExpiration": "만료 없음", + "shared_hasPassword": "있음", + "shared_noPassword": "없음", + "shared_editShare": "공유 편집", + "shared_notifyShare": "알림", + "shared_copyLink": "링크 복사", + "shared_removeShare": "공유 삭제", + "shared_linkCopied": "링크가 클립보드에 복사되었습니다!", + "shared_linkCopyFailed": "링크 복사에 실패했습니다", + "shared_itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", + "shared_itemRemoved": "공유가 성공적으로 삭제되었습니다", + "shared_invalidEmail": "유효한 이메일 주소를 입력하세요", + "shared_notificationSent": "알림이 성공적으로 전송되었습니다", + "shared_notificationFailed": "알림 전송에 실패했습니다" + }, + "files": { + "name": "이름", + "type": "유형", + "size": "크기", + "modified": "수정일", + "no_files": "이 폴더에 파일이 없습니다", + "empty_hint": "파일을 업로드하거나 폴더를 만들어 시작하세요", + "loading": "파일 로딩 중…", + "view_grid": "그리드 보기", + "view_list": "목록 보기", + "file_types": { + "document": "문서", + "image": "이미지", + "video": "동영상", + "audio": "오디오", + "pdf": "PDF", + "text": "텍스트", + "folder": "폴더", + "spreadsheet": "스프레드시트", + "presentation": "프레젠테이션", + "archive": "아카이브", + "installer": "설치 프로그램", + "code": "코드" + }, + "owner": "소유자", + "add_favorites": "즐겨찾기 추가", + "added_favorites": "즐겨찾기에 추가됨", + "col_name": "이름", + "col_owner": "소유자", + "col_size": "크기", + "col_type": "유형", + "copy": "복사", + "edit": "편집", + "file": "파일", + "folder": "폴더", + "new_folder": "새 폴더", + "share": "공유", + "view": "보기" + }, + "dialogs": { + "rename_folder": "폴더 이름 변경", + "rename_file": "파일 이름 변경", + "new_name": "새 이름", + "new_folder_title": "새 폴더", + "folder_name": "폴더 이름", + "folder_placeholder": "내 폴더", + "rename_title": "이름 변경", + "move_file": "파일 이동", + "move_folder": "폴더 이동", + "select_destination": "대상 폴더를 선택하세요:", + "select_this_folder": "이 폴더 선택", + "go_to_parent": ".. (상위 폴더)", + "no_subfolders": "하위 폴더 없음", + "root": "루트", + "delete_confirmation": "정말 삭제하시겠습니까", + "and_contents": "및 모든 내용", + "no_undo": "이 작업은 되돌릴 수 없습니다", + "confirm_title": "작업 확인", + "confirm_delete": "휴지통으로 이동", + "confirm_delete_file": "파일 «{{name}}»을(를) 휴지통으로 이동하시겠습니까?", + "confirm_delete_folder": "폴더 «{{name}}» 및 모든 내용을 휴지통으로 이동하시겠습니까?", + "confirm_permanent_delete": "영구 삭제", + "confirm_permanent_delete_msg": "이 항목을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "confirm_empty_trash": "휴지통 비우기", + "confirm_delete_share": "공유 링크 삭제", + "confirm_delete_share_msg": "이 공유 링크를 삭제하시겠습니까?", + "share_file": "파일 공유", + "share_folder": "폴��� 공유", + "existing_shares": "기존 공유", + "share_options": "공유 옵션", + "password": "비밀번호", + "expiration": "만료일", + "permissions": "권한", + "generated_link": "생성된 링크", + "notify": "알림 보내기", + "recipient": "수신자", + "message": "메시지", + "move_to_home": "홈 폴더로 이동" + }, + "dropzone": { + "drag_files": "여기에 파일을 드래그하거나 클릭하여 선택하세요", + "drop_files": "파일을 놓아 업로드하세요" + }, + "permissions": { + "read": "읽기", + "write": "쓰기", + "reshare": "재공유" + }, + "errors": { + "file_not_found": "파일을 찾을 수 없습니다", + "folder_not_found": "폴더를 찾을 수 없습니다", + "delete_error": "삭제 오류", + "upload_error": "파일 업로드 오류", + "rename_error": "이름 변경 오류", + "move_error": "이동 오류", + "empty_name": "이름은 비워둘 수 없습니다", + "name_exists": "같은 이름의 파일 또는 폴더가 이미 존재합니다", + "generic_error": "오류가 발생했습니다", + "group_name_invalid": "그룹 이름은 이메일 접두사 형식과 일치해야 합니다(문자, 숫자, 점, 대시, 밑줄; 1–64자).", + "group_cycle": "이 구성원은 그룹 간 순환 참조를 만들 것입니다.", + "group_depth_exceeded": "중첩 깊이가 허용 최대값(8)을 초과합니다.", + "group_virtual_immutable": "«Internal» 그룹은 시스템이 관리하며 수정할 수 없습니다.", + "group_not_found": "그룹을 찾을 수 없습니다.", + "group_name_taken": "이 이름의 그룹이 이미 존재합니다." + }, + "breadcrumb": { + "home": "홈" + }, + "trash": { + "empty_trash": "휴지통 비우기", + "empty_state": "휴지통이 비어 있습니다", + "original_location": "원래 위치", + "deleted_date": "삭제일", + "remaining": "남음", + "actions": "작업", + "restore": "복원", + "delete_permanently": "영구 삭제", + "empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다.", + "groupby": { + "remaining_days": "남은 일수", + "trashed_time": "삭제 시간" + }, + "delete": "영구 삭제", + "empty_action": "휴지통 비우기" + }, + "daysRemaining": { + "expired": "만료됨", + "today": "오늘", + "tomorrow": "내일", + "inDays": "{{count}}일" + }, + "expiryChip": { + "never": "만료되지 않음", + "expired": "만료됨", + "today": "오늘 만료", + "tomorrow": "내일 만료", + "inDays": "{{count}}일 후 만료", + "onDate": "{{date}}에 만료" + }, + "auth": { + "login_title": "로그인", + "username": "사용자 이름", + "username_placeholder": "사용자 이름을 입력하세요", + "login_identifier": "사용자 이름 또는 이메일", + "login_identifier_placeholder": "사용자 이름 또는 이메일을 입력하세요", + "password": "비밀번호", + "password_placeholder": "비밀번호를 입력하세요", + "login_button": "로그인", + "no_account": "계정이 없으신가요?", + "register": "가입하기", + "admin_setup": "처음이신가요?", + "setup": "관리자 설정", + "register_title": "계정 만들기", + "email": "이메일", + "email_placeholder": "이메일 주소를 입력하세요", + "confirm_password": "비밀번호 확인", + "confirm_password_placeholder": "비밀번호를 다시 입력하세요", + "register_button": "계정 만들기", + "have_account": "이미 계정이 있으신가요?", + "login": "로그인", + "setup_title": "초기 설정", + "setup_step1": "관리자", + "setup_step2": "시스템", + "setup_step3": "완료", + "admin_username": "관리자 사용자 이름", + "admin_email": "관리자 이메일", + "admin_password": "관리자 비밀번호", + "create_admin": "관리자 생성", + "back_to_login": "이미 설정하셨나요?", + "admin_success": "관리자 계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", + "account_success": "계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", + "passwords_mismatch": "비밀번호가 일치하지 않습니다", + "admin_create_error": "관리자 계정 생성 오류", + "or": "또는", + "sso_login": "SSO로 로그인", + "sso_login_provider": "{{provider}}(으)로 로그인", + "magicLinkHint": "비밀번호가 없으신가요? 이메일을 입력하시면 일회용 로그인 링크를 보내드립니다.", + "magicLinkEmailLabel": "이메일 주소", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "로그인 링크 보내기", + "magicLinkSent": "해당 이메일에 대한 계정이 있는 경우 로그인 링크가 전송되었습니다. 받은편지함을 확인하세요.", + "magicLinkUnavailable": "이 서버에서는 이메일 로그인을 사용할 수 없습니다.", + "magicLinkNetworkError": "서버에 연결할 수 없습니다: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "이메일 주소", + "magic_hint": "비밀번호가 없으신가요? 이메일을 입력하시면 일회용 로그인 링크를 보내드립니다.", + "magic_unavailable": "이 서버에서는 이메일 로그인을 사용할 수 없습니다.", + "passwords_match": "Passwords match", + "sign_in": "로그인" + }, + "storage": { + "title": "저장소", + "calculating": "계산 중...", + "used": "{{percentage}}% 사용 중 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "이 파일 형식은 미리보기를 지원하지 않습니다.", + "download_file": "파일 다운로드", + "zoom_in": "확대", + "zoom_out": "축소", + "zoom_reset": "줌 초기화" + }, + "language_selector": { + "title": "환영합니다!", + "subtitle": "계속하려면 언어를 선택하세요", + "continue": "계속", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ko": "한국어", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "아직 즐겨찾기가 없습니다", + "empty_hint": "파일이나 폴더에 별표를 눌러 즐겨찾기에 추가하세요", + "add": "즐겨찾기 추가", + "remove": "즐겨찾기 해제", + "added_title": "즐겨찾기에 추가됨", + "added_msg": "즐겨찾기에 추가되었습니다", + "removed_title": "즐겨찾기에서 삭제됨", + "removed_msg": "즐겨찾기에서 삭제되었습니다" + }, + "recent": { + "title": "최근", + "clear": "최근 항목 지우기", + "accessed": "접근일", + "empty_state": "최근 파일이 없습니다", + "empty_hint": "열어본 파일이 여기에 표시됩니다", + "loadMore": "더 불러오기" + }, + "notifications": { + "file_renamed": "파일 이름이 변경되었습니다", + "file_renamed_to": "파일 이름이 «{{name}}»(으)로 변경되었습니다", + "folder_renamed": "폴더 이름이 변경되었습니다", + "folder_renamed_to": "폴더 이름이 «{{name}}»(으)로 변경되었습니다", + "file_uploaded": "파일이 업로드되었습니다", + "file_deleted": "파일이 휴지통으로 이동되었습니다", + "folder_deleted": "폴더가 휴지통으로 이동되었습니다", + "item_deleted_permanently": "항목이 영구적으로 삭제되었습니다", + "trash_emptied": "휴지통이 성공적으로 비워졌습니다", + "title": "알림", + "empty": "알림이 없습니다", + "link_created": "링크 생성됨", + "share_success": "공유 링크가 성공적으로 생성되었습니다", + "upload_files_section_title": "여기서는 업로드할 수 없습니다", + "upload_files_section_body": "파일을 업로드하려면 파일 섹션으로 이동하세요" + }, + "batch": { + "one_selected": "1개 선택됨", + "n_selected": "{{count}}개 선택됨", + "confirm_delete": "{{count}}개 항목을 휴지통으로 이동하시겠습니까?", + "move_title": "{{count}}개 항목 이동", + "add_favorites": "즐겨찾기에 추가", + "move_copy": "이동 또는 복사" + }, + "admin": { + "page_title": "관리자 패널", + "back_to_app": "OxiCloud로 돌아가기", + "loading": "로딩 중…", + "access_denied": "접근 거부", + "access_denied_desc": "관리자 권한이 필요합니다.", + "sign_in": "로그인", + "tab_dashboard": "대시보드", + "tab_users": "사용자", + "tab_oidc": "SSO / OIDC", + "total_users": "전체 사용자", + "active_users": "활성 사용자", + "admins": "관리자", + "version": "버전", + "storage_overview": "스토리지 개요", + "used": "사용됨", + "total_quota": "총 할당량", + "usage_pct": "사용률", + "users_over_80": "할당량 80% 초과", + "users_over_quota": "할당량 초과", + "system": "시스템", + "auth_label": "인증", + "oidc_label": "OIDC", + "quotas_label": "할당량", + "enabled": "활성화됨", + "disabled": "비활성화됨", + "active": "활성", + "off": "꺼짐", + "allow_registration": "공개 자가 등록 허용", + "registration_warning": "공개 등록이 비활성화되어 있습니다. 관리자만 사용자를 만들 수 있습니다.", + "user_management": "사용자 관리", + "create_user": "사용자 생성", + "col_user": "사용자", + "col_role": "역할", + "col_auth": "인증", + "col_status": "상태", + "col_storage": "스토리지", + "col_last_login": "마지막 로그인", + "col_actions": "작업", + "loading_users": "사용자 로딩 중…", + "failed_load_users": "로드 실패", + "no_users_found": "사용자 없음", + "showing_users": "{{from}}-{{to}} / {{total}} 표시", + "prev": "이전", + "next": "다음", + "inactive": "비활성", + "you_badge": "(나)", + "local": "로컬", + "never": "없음", + "just_now": "방금", + "minutes_ago": "{{n}}분 전", + "hours_ago": "{{n}}시간 전", + "days_ago": "{{n}}일 전", + "edit_quota_title": "할당량 편집", + "reset_password_title": "비밀번호 재설정", + "toggle_role_title": "역할 전환", + "deactivate_title": "비활성화", + "activate_title": "활성화", + "delete_title": "삭제", + "sso_title": "싱글 사인온 (OIDC / SSO)", + "enable_sso": "SSO 인증 활성화", + "provider_name": "제공자 이름", + "issuer_url": "발급자 URL", + "issuer_url_hint": "OpenID Connect 발급자 URL", + "auto_discover": "자동 검색", + "discovering": "검색 중…", + "client_id": "클라이언트 ID", + "client_secret": "클라이언트 시크릿", + "client_secret_placeholder": "현재 값 유지하려면 비워두세요", + "secret_configured": "클라이언트 시크릿 구성됨", + "callback_url": "콜백 URL", + "callback_url_hint": "(IdP에 등록)", + "advanced_settings": "고급 설정", + "scopes": "스코프", + "auto_provision": "첫 로그인 시 자동 프로비저닝", + "admin_groups": "관리자 그룹", + "admin_groups_hint": "쉼표로 구분된 OIDC 그룹 이름", + "disable_password": "비밀번호 로그인 비활성화 (OIDC만)", + "password_warning": "모든 비밀번호 로그인이 차단됩니다!", + "test_btn": "테스트", + "save_btn": "저장", + "saving": "저장 중…", + "settings_saved": "설정 저장됨 — OIDC: {{status}}", + "quota_modal_title": "스토리지 할당량 업데이트", + "quota_user_label": "사용자:", + "new_quota": "새 할당량", + "quota_unlimited_hint": "무제한은 0", + "cancel": "취소", + "create_user_title": "새 사용자 생성", + "username_label": "사용자 이름", + "username_placeholder": "username", + "username_hint": "3–32자", + "password_label": "비밀번호", + "password_placeholder": "최소 8자", + "email_label": "이메일", + "email_optional": "(선택사항)", + "email_placeholder": "user@example.com (비어있으면 자동 생성)", + "role_label": "역할", + "role_user": "사용자", + "role_admin": "관리자", + "quota_label": "할당량", + "creating": "생성 중…", + "reset_pw_title": "비밀번호 재설정", + "new_password_label": "새 비밀번호", + "resetting": "재설정 중…", + "reset_btn": "재설정", + "confirm_role_change": "역할을 {{role}}(으)로 변경?", + "confirm_deactivate": "이 사용자를 비활성화하시겠습니까?", + "confirm_activate": "이 사용자를 활성화하시겠습니까?", + "confirm_delete_user": "사용자 \"{{name}}\" 삭제? 되돌릴 수 없습니다!", + "confirm_action": "작업 확인", + "confirm_yes": "확인", + "confirm_no": "취소", + "error_username_short": "사용자 이름 최소 3자", + "error_password_short": "비밀번호 최소 8자", + "error_generic": "실패", + "error_network": "네트워크 오류: {{message}}", + "error_create_user": "사용자 생성 실패", + "tab_storage": "저장소", + "storage_title": "저장소 구성", + "storage_current_backend": "현재 백엔드", + "storage_total_blobs": "총 블롭 수", + "storage_total_size": "총 크기", + "storage_dedup_ratio": "중복 제거 비율", + "storage_backend": "백엔드", + "storage_local": "로컬", + "storage_s3": "S3 호환", + "storage_provider_preset": "공급자 프리셋", + "storage_preset_custom": "사용자 지정", + "storage_endpoint_url": "엔드포인트 URL", + "storage_endpoint_hint": "AWS S3의 경우 비워두세요", + "storage_bucket": "버킷", + "storage_region": "지역", + "storage_access_key": "액세스 키", + "storage_secret_key": "시크릿 키", + "storage_secret_configured": "키 구성됨", + "storage_key_placeholder": "새 키 입력", + "storage_path_style": "경로 스타일 강제", + "storage_path_style_hint": "MinIO 및 일부 S3 호환 서비스에 필요", + "storage_test_connection": "연결 테스트", + "storage_test_success": "연결 성공", + "storage_test_failure": "연결 실패", + "storage_save": "구성 저장", + "storage_saved": "구성이 저장되었습니다", + "storage_migration": "데이터 마이그레이션", + "storage_migration_coming_soon": "마이그레이션 도구 곧 출시", + "migration_status_label": "마이그레이션 상태", + "migration_start": "마이그레이션 시작", + "migration_pause": "일시 중지", + "migration_resume": "재개", + "migration_verify": "확인", + "migration_complete": "완료", + "migration_started": "마이그레이션 시작됨", + "migration_paused_msg": "마이그레이션 일시 중지됨", + "migration_resumed_msg": "마이그레이션 재개됨", + "migration_completed_msg": "마이그레이션이 성공적으로 완료되었습니다", + "migration_verifying": "확인 중...", + "migration_verify_passed": "확인 통과", + "migration_verify_failed": "확인 실패", + "migration_failed_blobs": "실패한 블롭", + "testing": "테스트 중...", + "smtp_disabled": "비활성화됨 (호스트 미설정)", + "smtp_enabled": "활성화됨", + "smtp_enabled_label": "상태", + "smtp_intro": "SMTP는 환경 변수(OXICLOUD_SMTP_*)로만 구성됩니다. 아래 값들은 실행 중인 서버에서 읽어옵니다 — 변경하려면 환경을 수정하고 OxiCloud를 다시 시작하세요.", + "smtp_not_configured": "이 서버에는 SMTP가 구성되어 있지 않습니다.", + "smtp_send_failed": "전송 실패.", + "smtp_send_test": "테스트 이메일 보내기", + "smtp_sending": "보내는 중…", + "smtp_sent": "테스트 이메일을 보냈습니다.", + "smtp_server_code": "서버 응답", + "smtp_test_intro": "아래 수신자에게 미리 정의된 진단 메시지를 보내고 SMTP 서버의 응답을 표시합니다. 이를 통해 릴레이 로그와 대조하여 확인할 수 있습니다.", + "smtp_test_missing_to": "수신자 주소를 입력하세요.", + "smtp_test_title": "테스트 이메일 보내기", + "smtp_test_to": "수신자 주소", + "smtp_title": "발신 이메일 (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "관리자", + "confirm_role": "역할을 {{role}}(으)로 변경?", + "dashboard": "대시보드", + "email": "이메일", + "mig_complete": "완료", + "mig_pause": "일시 중지", + "mig_resume": "재개", + "mig_verify_failed": "확인 실패", + "mig_verify_passed": "확인 통과", + "mig_verifying": "확인 중...", + "oidc_auto_provision": "첫 로그인 시 자동 프로비저닝", + "oidc_callback": "콜백 URL", + "oidc_client_id": "클라이언트 ID", + "oidc_disable_pw": "비밀번호 로그인 비활성화 (OIDC만)", + "oidc_issuer": "발급자 URL", + "oidc_scopes": "스코프", + "password": "비밀번호", + "quotas": "할당량", + "reset_pw_for": "새 비밀번호:", + "role": "역할", + "smtp_fail": "전송 실패.", + "smtp_send": "보내기", + "smtp_test": "테스트 이메일 보내기", + "smtp_user_state": "인증", + "status": "상태", + "storage": "스토리지", + "storage_endpoint": "엔드포인트 URL", + "storage_tab": "스토리지", + "time_min_ago": "{{n}}분 전", + "title": "관리자", + "user": "사용자", + "username": "사용자 이름", + "users": "사용자" + }, + "profile": { + "page_title": "프로필", + "back_to_app": "OxiCloud로 돌아가기", + "loading": "로딩 중…", + "not_authenticated": "인증되지 않음", + "not_authenticated_desc": "프로필을 보려면 로그인하세요.", + "sign_in": "로그인", + "role_admin": "관리자", + "role_user": "사용자", + "account_details": "계정 정보", + "username": "사용자 이름", + "email": "이메일", + "role": "역할", + "last_login": "마지막 로그인", + "storage": "스토리지", + "used": "사용됨", + "quota": "할당량", + "usage": "사용률", + "unlimited": "무제한", + "app_passwords": "앱 비밀번호", + "app_pw_desc": "WebDAV, CalDAV, CardDAV 클라이언트용 비밀번호를 생성합니다. 각 비밀번호는 한 번만 표시됩니다.", + "app_pw_label_placeholder": "라벨 (예: Thunderbird, macOS)", + "generate": "생성", + "generating": "생성 중…", + "new_password_for": "새 비밀번호:", + "copy_warning": "지금 이 비밀번호를 복사하세요. 다시 볼 수 없습니다.", + "copy_to_clipboard": "클립보드에 복사", + "col_label": "라벨", + "col_created": "생성일", + "col_last_used": "마지막 사용", + "col_status": "상태", + "active": "활성", + "revoked": "취소됨", + "revoke_title": "취소", + "no_app_passwords": "앱 비밀번호가 아직 없습니다.", + "client_sessions": "클라이언트 세션", + "client_sessions_desc": "Nextcloud 호환 클라이언트 연결 시 자동 생성됩니다.", + "col_client": "클라이언트", + "never": "없음", + "just_now": "방금", + "minutes_ago": "{{n}}분 전", + "hours_ago": "{{n}}시간 전", + "days_ago": "{{n}}일 전", + "edit_profile": "프로필 편집", + "edit_oidc_managed": "정보(이름, 성, 프로필 사진 등)를 변경하려면 ID 공급자에서 업데이트하세요. 변경 사항은 다음 로그인 시 반영됩니다.", + "username_claim_hint": "2~64자, 영문자 / 숫자 / 점 / 하이픈 / 밑줄. 선택한 후에는 사용자 이름을 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", + "username_already_claimed": "사용자 이름이 설정되어 있어 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", + "given_name": "이름", + "family_name": "성", + "notify_on_share": "다른 사람이 나에게 공유할 때 이메일로 알림 받기", + "notify_on_share_hint": "선택을 해제해도 공유 항목은 계정에 계속 표시되지만, 이메일 알림은 받지 않습니다.", + "save_profile": "변경 사항 저장", + "profile_saved": "프로필이 업데이트되었습니다", + "profile_no_changes": "저장할 변경 사항이 없습니다.", + "profile_save_failed": "저장 실패", + "username_taken_error": "이미 사용 중인 사용자 이름입니다.", + "username_immutable_error": "사용자 이름이 이미 설정되어 있어 여기서 변경할 수 없습니다. 이름을 변경하려면 관리자에게 문의하세요.", + "change_password": "비밀번호 변경", + "current_password": "현재 비밀번호", + "new_password": "새 비밀번호", + "min_8_chars": "최소 8자", + "confirm_password": "새 비밀번호 확인", + "update_password": "비밀번호 업데이트", + "updating": "업데이트 중…", + "password_updated": "비밀번호가 성공적으로 업데이트되었습니다", + "passwords_no_match": "비밀번호가 일치하지 않습니다", + "password_too_short": "비밀번호는 최소 8자여야 합니다", + "password_change_failed": "비밀번호 변경 실패", + "error_network": "네트워크 오류: {{message}}", + "error_label_required": "라벨을 입력하세요", + "error_create_pw": "앱 비밀번호 생성 실패", + "confirm_revoke": "앱 비밀번호 \"{{label}}\"을(를) 취소하시겠습니까? 이 비밀번호를 사용하는 클라이언트가 작동하지 않게 됩니다.", + "error_revoke": "취소 실패", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "비밀번호가 일치하지 않습니다" + }, + "upload": { + "uploading": "업로드 중...", + "files": "파일", + "complete": "{{count}} / {{total}} 업로드됨" + }, + "storage_quota_exceeded": "저장 공간 할당량 초과", + "sharedwithme": { + "pageTitle": "나와 공유됨", + "pageDescription": "다른 사용자가 나와 공유한 파일 및 폴더", + "emptyStateTitle": "아직 공유된 항목이 없습니다", + "emptyStateDesc": "다른 사용자가 공유한 항목이 여기에 표시됩니다", + "loadMore": "더 불러오기", + "sharedBy": "공유한 사람", + "colName": "이름", + "colType": "유형", + "colSharedBy": "공유한 사람", + "colDate": "공유 날짜", + "colPermissions": "권한" + }, + "groupby": { + "none": "없음", + "title": "그룹화 기준", + "owner": "소유자", + "shareDate": "공유 날짜", + "type": "유형", + "type.folders": "폴더", + "accessedAt": "접근 날짜", + "modifiedAt": "수정 날짜", + "createdAt": "생성 날짜", + "size": "크기", + "favoriteDate": "즐겨찾기 날짜", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "새 항목", + "folders": "폴더" + }, + "dateBucket": { + "today": "오늘", + "last7days": "최근 7일", + "last30days": "최근 30일", + "unknown": "알 수 없음" + }, + "groups": { + "title": "그룹 관리", + "create_button": "그룹 생성", + "create_dialog_title": "새 그룹", + "edit_dialog_title": "그룹 이름 변경", + "name_label": "이름", + "name_placeholder": "engineering", + "description_label": "설명(선택사항)", + "members_section": "구성원", + "add_member_placeholder": "사용자 또는 그룹 추가…", + "no_members": "아직 구성원이 없습니다.", + "remove_member": "제거", + "delete_group": "그룹 삭제", + "delete_confirm": "\"{name}\" 그룹을 삭제하시겠습니까? 이 그룹을 참조하는 모든 권한이 해제됩니다.", + "empty_state": "아직 그룹이 없습니다.", + "load_more": "더 보기", + "back_to_list": "뒤로", + "loading": "로딩 중…", + "virtual_badge": "시스템", + "member_count_zero": "구성원 없음", + "member_count_one": "구성원 1명", + "member_count_other": "구성원 {count}명", + "delete_confirm_label": "확인을 위해 그룹 이름을 입력하세요:", + "delete_confirm_mismatch": "확인을 위해 그룹 이름을 정확히 입력하세요.", + "virtual_internal_name": "내부", + "members_loading": "구성원 로딩 중…", + "members_empty": "구성원 없음", + "virtual_internal_explanation": "이 서버의 모든 내부 사용자", + "create": "그룹 생성", + "empty": "아직 그룹이 없습니다.", + "members": "구성원" + }, + "myshares": { + "copyLink": "링크 복사", + "deleteLink": "링크 삭제", + "notifyByEmail": "이메일로 알림", + "notifyFailed": "알림을 보낼 수 없습니다.", + "notifyGroupMembers": "그룹 구성원에게 알림", + "notifyRateLimited": "이 수신자에게 알림이 너무 많습니다 — 나중에 다시 시도하세요.", + "removeAccess": "액세스 제거", + "resendInvitation": "초대 이메일 다시 보내기", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "오디오", + "code": "코드", + "text": "텍스트" + }, + "common": { + "add": "추가", + "cancel": "취소", + "clear": "Clear", + "close": "닫기", + "confirm": "확인", + "copy": "복사", + "create": "만들기", + "delete": "삭제", + "download": "다운로드", + "load_more": "더 불러오기", + "loading": "로딩 중…", + "next": "다음", + "no": "없음", + "previous": "이전", + "remove": "Remove", + "rename": "이름 변경", + "save": "저장", + "search": "검색", + "yes": "있음" + }, + "device": { + "continue": "계속", + "unknown": "알 수 없음" + }, + "expiryBucket": { + "expired": "만료됨", + "noExpiry": "만료 없음", + "today": "오늘", + "tomorrow": "내일" + }, + "nextcloud": { + "error_title": "오류", + "sign_in_with": "{{provider}}(으)로 로그인" + }, + "search": { + "size_label": "크기", + "title": "검색", + "type": { + "audio": "오디오" + }, + "type_label": "유형" + }, + "sizeBucket": { + "folders": "폴더" + }, + "view": { + "grid": "그리드 보기", + "list": "목록 보기" + } } diff --git a/frontend/static/locales/nl.json b/frontend/static/locales/nl.json index 468c93fd..f52a5322 100644 --- a/frontend/static/locales/nl.json +++ b/frontend/static/locales/nl.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Deze aanmeldlink is niet meer geldig", - "expired_body": "De link is mogelijk verlopen of al gebruikt. We kunnen je een nieuwe sturen — die komt binnen enkele seconden in je inbox.", - "resend_to": "Stuur een nieuwe link naar {{email}}", - "generic_unavailable": "Deze aanmeldlink is niet meer geldig. Hij is mogelijk al gebruikt of verlopen. Vraag een nieuwe link aan via de aanmeldpagina.", - "service_unavailable": "Aanmelden via magic link is niet ingeschakeld op deze server.", - "internal_error": "Er is iets misgegaan bij het aanmelden. Probeer het opnieuw.", - "resend_failure": "Er is iets misgegaan bij het versturen van de link. Probeer het opnieuw.", - "cross_browser_title": "Doorgaan met aanmelden op dit apparaat?", - "cross_browser_body": "Je hebt deze aanmeldlink geopend in een andere browser of op een ander apparaat dan waar je hem hebt aangevraagd.", - "cross_browser_warning": "Als jij deze link hebt aangevraagd, kun je veilig doorgaan. Zo niet, sluit deze pagina — op Doorgaan klikken zou iemand anders bij je account aanmelden.", - "cross_browser_continue": "Doorgaan en aanmelden", - "resend_confirmation_title": "Controleer je inbox", - "resend_confirmation_body": "Als de aanmeldlink bij een actief account hoorde, is er zojuist een nieuwe link verstuurd. Controleer je inbox.", - "return_link": "Terug naar OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", - "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen het door op de onderstaande link te klikken:\n{{link}}\n\nDe link werkt eenmalig en verloopt over {{ttl_hours}} uur.\nAls je deze uitnodiging niet verwacht, kun je dit bericht negeren.\n\n— OxiCloud" - }, - "login": { - "subject": "Aanmelden bij OxiCloud", - "body": "Hallo,\n\nGebruik de onderstaande link om je aan te melden bij OxiCloud. De link werkt eenmalig en verloopt over {{ttl_minutes}} minuten. Open hem op hetzelfde apparaat waarop je hem hebt aangevraagd.\n\n{{link}}\n\nAls je deze aanmeldlink niet hebt aangevraagd, kun je dit bericht negeren — er is geen verdere actie nodig.\n\n— OxiCloud" - }, - "kind_file": "bestand", - "kind_folder": "map", - "english_fallback_divider": "--- Engelse versie hieronder ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Deze aanmeldlink is niet meer geldig", + "expired_body": "De link is mogelijk verlopen of al gebruikt. We kunnen je een nieuwe sturen — die komt binnen enkele seconden in je inbox.", + "resend_to": "Stuur een nieuwe link naar {{email}}", + "generic_unavailable": "Deze aanmeldlink is niet meer geldig. Hij is mogelijk al gebruikt of verlopen. Vraag een nieuwe link aan via de aanmeldpagina.", + "service_unavailable": "Aanmelden via magic link is niet ingeschakeld op deze server.", + "internal_error": "Er is iets misgegaan bij het aanmelden. Probeer het opnieuw.", + "resend_failure": "Er is iets misgegaan bij het versturen van de link. Probeer het opnieuw.", + "cross_browser_title": "Doorgaan met aanmelden op dit apparaat?", + "cross_browser_body": "Je hebt deze aanmeldlink geopend in een andere browser of op een ander apparaat dan waar je hem hebt aangevraagd.", + "cross_browser_warning": "Als jij deze link hebt aangevraagd, kun je veilig doorgaan. Zo niet, sluit deze pagina — op Doorgaan klikken zou iemand anders bij je account aanmelden.", + "cross_browser_continue": "Doorgaan en aanmelden", + "resend_confirmation_title": "Controleer je inbox", + "resend_confirmation_body": "Als de aanmeldlink bij een actief account hoorde, is er zojuist een nieuwe link verstuurd. Controleer je inbox.", + "return_link": "Terug naar OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", + "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen het door op de onderstaande link te klikken:\n{{link}}\n\nDe link werkt eenmalig en verloopt over {{ttl_hours}} uur.\nAls je deze uitnodiging niet verwacht, kun je dit bericht negeren.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", - "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen OxiCloud om je nieuwe gedeelde item te bekijken:\n{{login_link}}\n\nMisschien heb je nog meer nieuwe gedeelde items van {{inviter}} — meld je aan om al je gedeelde items te zien.\n\n— OxiCloud\n\nJe ontvangt dit bericht omdat je een OxiCloud-account hebt en je voorkeur voor deelmeldingen aanstaat. Je kunt het uitzetten in je profiel (Stuur me een e-mail wanneer iemand iets met mij deelt)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalistisch cloudopslagsysteem" - }, - "nav": { - "files": "Bestanden", - "shared": "Gedeeld", - "recent": "Recente", - "favorites": "Favorieten", - "photos": "Foto's", - "music": "Muziek", - "trash": "Prullenbak", - "sharedwithme": "Gedeeld met mij" - }, - "photos": { - "empty_state": "Nog geen foto's", - "empty_hint": "Upload afbeeldingen of video's om ze hier te zien", - "items_selected": "geselecteerd", - "view_daily": "Dag", - "view_monthly": "Maand", - "view_yearly": "Jaar" - }, - "music": { - "create_playlist": "Afspeellijst Maken", - "playlists": "Afspeellijsten", - "no_playlists": "Nog geen afspeellijsten", - "select_playlist": "Selecteer een afspeellijst", - "select_hint": "Kies een afspeellijst uit de zijbalk of maak een nieuwe", - "add_tracks": "Tracks Toevoegen", - "no_tracks": "Geen tracks in deze afspeellijst", - "unknown_artist": "Onbekende Artiest", - "unknown_title": "Onbekend", - "confirm_delete": "Deze afspeellijst verwijderen?", - "playlist_name": "Naam afspeellijst", - "create": "Maken", - "delete": "Verwijderen", - "share": "Delen", - "edit": "Bewerken", - "play_all": "Alles Afspelen", - "shuffle": "Shuffle", - "repeat": "Herhalen", - "repeat_one": "Een Herhalen", - "queue": "Wachtrij", - "queue_empty": "Wachtrij is leeg", - "not_playing": "Niet afspelend", - "play": "Afspelen", - "pause": "Pauzeren", - "previous": "Vorige", - "next": "Volgende", - "volume": "Volume", - "mute": "Dempen", - "unmute": "Geluid aan", - "title": "Titel", - "artist": "Artiest", - "album": "Album", - "tracks": "tracks", - "add": "Toevoegen", - "added": "Toegevoegd!", - "added_to_playlist": "toegevoegd aan playlist", - "add_to_playlist": "Aan playlist toevoegen", - "load_error": "Fout bij laden van playlists", - "add_error": "Kon tracks niet toevoegen aan playlist", - "no_playlists_yet": "Nog geen playlists. Maak er eerst een!", - "selected_files": "Geselecteerd:", - "error": "Fout", - "search_audio": "Audiobestanden zoeken…", - "no_audio_files": "Geen audiobestanden gevonden", - "selected": "geselecteerd", - "loading": "Laden…", - "search_error": "Kan audiobestanden niet laden", - "adding": "Toevoegen…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Zoek bestanden...", - "new_folder": "Nieuwe map", - "upload": "Uploaden", - "upload_files": "Bestanden uploaden", - "upload_folder": "Map uploaden", - "upload.uploading": "Uploaden...", - "upload.complete": "{count} / {total} geüpload", - "upload.files": "bestanden", - "rename": "Hernoemen", - "move": "Verplaatsen naar...", - "move_to": "Verplaatsen naar", - "delete": "Verwijderen", - "download": "Downloaden", - "view": "Bekijken", - "cancel": "Annuleren", - "confirm": "Bevestigen", - "share": "Delen", - "favorite": "Toevoegen aan favorieten", - "unfavorite": "Verwijderen uit favorieten", - "copy": "Kopiëren", - "notify": "Melden", - "send": "Verzenden", - "clear_recent": "Recente wissen", - "logout": "Uitloggen", - "create": "Maken", - "search_btn": "Zoeken", - "close": "Sluiten", - "delete_permanently": "Permanent verwijderen", - "empty_trash": "Prullenbak legen", - "open_parent_folder": "Naar bovenliggende map", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Weergave", - "about": "Over OxiCloud", - "about_description": "Cloudopslagplatform gebouwd met Rust & Clean Architecture. Snel, veilig en privé.", - "admin_panel": "Beheerpaneel", - "profile": "Mijn profiel", - "role_user": "Gebruiker", - "theme": { - "light": "Licht", - "dark": "Donker", - "auto": "Zoals systeem" + "login": { + "subject": "Aanmelden bij OxiCloud", + "body": "Hallo,\n\nGebruik de onderstaande link om je aan te melden bij OxiCloud. De link werkt eenmalig en verloopt over {{ttl_minutes}} minuten. Open hem op hetzelfde apparaat waarop je hem hebt aangevraagd.\n\n{{link}}\n\nAls je deze aanmeldlink niet hebt aangevraagd, kun je dit bericht negeren — er is geen verdere actie nodig.\n\n— OxiCloud" }, - "manage_groups": "Groepen beheren" + "kind_file": "bestand", + "kind_folder": "map", + "english_fallback_divider": "--- Engelse versie hieronder ---" + } }, - "share": { - "dialogTitle": "Deellink", - "linkLabel": "Deellink:", - "copyLink": "Kopiëren", - "permissions": "Rechten:", - "permissionRead": "Lezen", - "permissionWrite": "Schrijven", - "permissionReshare": "Opnieuw delen", - "password": "Wachtwoordbeveiliging:", - "generatePassword": "Genereren", - "expiration": "Verloopdatum:", - "update": "Delen bijwerken", - "remove": "Delen verwijderen", - "notifyTitle": "Notificatie verzenden", - "notifyEmailLabel": "E-mailadres:", - "notifyMessageLabel": "Bericht (optioneel):", - "notifySend": "Notificatie verzenden", - "shareWithOthers": "Met anderen delen", - "sharePublicly": "Openbaar delen", - "shareSettings": "Deelinstellingen", - "shareCopied": "Link gekopieerd naar klembord", - "shareCreated": "Deellink succesvol aangemaakt", - "shareUpdated": "Deelinstellingen bijgewerkt", - "shareRemoved": "Delen verwijderd", - "inviteByEmail": "Uitnodigen via e-mail — uitnodiging wordt verzonden", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Deellink", - "share_linkLabel": "Deellink:", - "share_copyLink": "Kopiëren", - "share_permissions": "Rechten:", - "share_permissionRead": "Lezen", - "share_permissionWrite": "Schrijven", - "share_permissionReshare": "Opnieuw delen", - "share_password": "Wachtwoordbeveiliging:", - "share_generatePassword": "Genereren", - "share_expiration": "Verloopdatum:", - "share_update": "Share bijwerken", - "share_remove": "Share verwijderen", - "share_notifyTitle": "Notificatie verzenden", - "share_notifyEmailLabel": "E-mailadres:", - "share_notifyMessageLabel": "Bericht (optioneel):", - "share_notifySend": "Notificatie verzenden", - "shared": { - "backToFiles": "Terug naar Bestanden", - "pageTitle": "Gedeelde items", - "pageDescription": "Beheer je gedeelde bestanden en mappen", - "filterType": "Type:", - "filterAll": "Alles", - "filterFiles": "Bestanden", - "filterFolders": "Mappen", - "sortBy": "Sorteren op:", - "sortByName": "Naam", - "sortByDate": "Datum gedeeld", - "sortByExpiration": "Verloop", - "search": "Zoeken", - "colName": "Naam", - "colType": "Type", - "colDateShared": "Gedeeld op", - "colExpiration": "Verloop", - "colPermissions": "Rechten", - "colPassword": "Wachtwoord", - "colActions": "Acties", - "emptyStateTitle": "Nog geen gedeelde items", - "emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", - "goToFiles": "Ga naar Bestanden", - "typeFile": "Bestand", - "typeFolder": "Map", - "noExpiration": "Geen verloop", - "hasPassword": "Ja", - "noPassword": "Nee", - "editShare": "Delen bewerken", - "notifyShare": "Iemand informeren", - "copyLink": "Link kopiëren", - "removeShare": "Delen verwijderen", - "linkCopied": "Link gekopieerd naar klembord!", - "linkCopyFailed": "Kopiëren van link mislukt", - "itemUpdated": "Deelinstellingen bijgewerkt", - "itemRemoved": "Delen verwijderd", - "invalidEmail": "Voer een geldig e-mailadres in", - "notificationSent": "Notificatie verzonden", - "notificationFailed": "Notificatie verzenden mislukt", - "shared_backToFiles": "Terug naar Bestanden", - "shared_pageTitle": "Gedeelde items", - "shared_pageDescription": "Beheer je gedeelde bestanden en mappen", - "shared_filterType": "Type:", - "shared_filterAll": "Alles", - "shared_filterFiles": "Bestanden", - "shared_filterFolders": "Mappen", - "shared_sortBy": "Sorteren op:", - "shared_sortByName": "Naam", - "shared_sortByDate": "Datum gedeeld", - "shared_sortByExpiration": "Verloop", - "shared_search": "Zoeken", - "shared_colName": "Naam", - "shared_colType": "Type", - "shared_colDateShared": "Gedeeld op", - "shared_colExpiration": "Verloop", - "shared_colPermissions": "Rechten", - "shared_colPassword": "Wachtwoord", - "shared_colActions": "Acties", - "shared_emptyStateTitle": "Nog geen gedeelde items", - "shared_emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", - "shared_goToFiles": "Ga naar Bestanden", - "shared_typeFile": "Bestand", - "shared_typeFolder": "Map", - "shared_noExpiration": "Geen verloop", - "shared_hasPassword": "Ja", - "shared_noPassword": "Nee", - "shared_editShare": "Delen bewerken", - "shared_notifyShare": "Iemand informeren", - "shared_copyLink": "Link kopiëren", - "shared_removeShare": "Delen verwijderen", - "shared_linkCopied": "Link gekopieerd naar klembord!", - "shared_linkCopyFailed": "Kopiëren van link mislukt", - "shared_itemUpdated": "Deelinstellingen bijgewerkt", - "shared_itemRemoved": "Delen verwijderd", - "shared_invalidEmail": "Voer een geldig e-mailadres in", - "shared_notificationSent": "Notificatie verzonden", - "shared_notificationFailed": "Notificatie verzenden mislukt" - }, - "files": { - "name": "Naam", - "type": "Type", - "size": "Grootte", - "modified": "Gewijzigd", - "no_files": "Geen bestanden in deze map", - "empty_hint": "Upload bestanden of maak mappen aan om te beginnen", - "loading": "Bestanden laden…", - "view_grid": "Rasterweergave", - "view_list": "Lijstweergave", - "file_types": { - "document": "Document", - "image": "Afbeelding", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Tekst", - "folder": "Map", - "spreadsheet": "Spreadsheet", - "presentation": "Presentatie", - "archive": "Archief", - "installer": "Installatiebestand", - "code": "Code" - }, - "owner": "Eigenaar" - }, - "dialogs": { - "rename_folder": "Map hernoemen", - "rename_file": "Bestand hernoemen", - "new_name": "Nieuwe naam", - "new_folder_title": "Nieuwe map", - "folder_name": "Mapnaam", - "folder_placeholder": "Mijn map", - "rename_title": "Hernoemen", - "move_file": "Bestand verplaatsen", - "move_folder": "Map verplaatsen", - "select_destination": "Selecteer doelmap:", - "root": "Hoofdmap", - "delete_confirmation": "Weet je zeker dat je wilt verwijderen", - "and_contents": "en alle inhoud", - "no_undo": "Deze actie kan niet ongedaan gemaakt worden", - "confirm_title": "Actie bevestigen", - "confirm_delete": "Verplaatsen naar prullenbak", - "confirm_delete_file": "Weet je zeker dat je het bestand \"{{name}}\" naar de prullenbak wilt verplaatsen?", - "confirm_delete_folder": "Weet je zeker dat je de map \"{{name}}\" en alle inhoud naar de prullenbak wilt verplaatsen?", - "confirm_permanent_delete": "Permanent verwijderen", - "confirm_permanent_delete_msg": "Weet je zeker dat je dit item permanent wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden.", - "confirm_empty_trash": "Prullenbak legen", - "confirm_delete_share": "Deellink verwijderen", - "confirm_delete_share_msg": "Weet je zeker dat je deze deellink wilt verwijderen?", - "share_file": "Bestand delen", - "share_folder": "Map delen", - "existing_shares": "Bestaande delingen", - "share_options": "Deelopties", - "password": "Wachtwoord", - "expiration": "Verloop", - "permissions": "Rechten", - "generated_link": "Gegenereerde link", - "notify": "Notificatie verzenden", - "recipient": "Ontvanger", - "message": "Bericht", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Verplaatsen naar de thuismap" - }, - "dropzone": { - "drag_files": "Sleep bestanden hierheen of klik om te selecteren", - "drop_files": "Laat bestanden vallen om te uploaden" - }, - "permissions": { - "read": "Lezen", - "write": "Schrijven", - "reshare": "Opnieuw delen" - }, - "errors": { - "file_not_found": "Bestand niet gevonden", - "folder_not_found": "Map niet gevonden", - "delete_error": "Fout bij verwijderen", - "upload_error": "Fout bij uploaden van bestand", - "rename_error": "Fout bij hernoemen", - "move_error": "Fout bij verplaatsen", - "empty_name": "Naam mag niet leeg zijn", - "name_exists": "Een bestand of map met deze naam bestaat al", - "generic_error": "Er is een fout opgetreden", - "group_name_invalid": "De groepsnaam moet voldoen aan het e-mailprefix-formaat (letters, cijfers, punt, streepje, underscore; 1–64 tekens).", - "group_cycle": "Dit lid zou een circulaire groepsverwijzing veroorzaken.", - "group_depth_exceeded": "Deze nestdiepte overschrijdt het maximum (8).", - "group_virtual_immutable": "De groep 'Internal' wordt door het systeem beheerd en kan niet worden gewijzigd.", - "group_not_found": "Groep niet gevonden.", - "group_name_taken": "Er bestaat al een groep met deze naam." - }, - "breadcrumb": { - "home": "Start" - }, - "trash": { - "empty_trash": "Prullenbak legen", - "empty_state": "Prullenbak is leeg", - "original_location": "Oorspronkelijke locatie", - "deleted_date": "Verwijderdatum", - "remaining": "Resterend", - "actions": "Acties", - "restore": "Herstellen", - "delete_permanently": "Permanent verwijderen", - "empty_confirm": "Weet je zeker dat je de prullenbak wilt legen? Dit verwijdert alle items permanent.", - "groupby": { - "remaining_days": "Resterende dagen", - "trashed_time": "Verwijderd op" - } - }, - "daysRemaining": { - "expired": "Verlopen", - "today": "Vandaag", - "tomorrow": "Morgen", - "inDays": "{{count}} dagen" - }, - "expiryChip": { - "never": "Verloopt nooit", - "expired": "Verlopen", - "today": "Verloopt vandaag", - "tomorrow": "Verloopt morgen", - "inDays": "Verloopt over {{count}} dagen", - "onDate": "Verloopt op {{date}}" - }, - "auth": { - "login_title": "Inloggen", - "username": "Gebruikersnaam", - "username_placeholder": "Voer je gebruikersnaam in", - "login_identifier": "Gebruikersnaam of e-mail", - "login_identifier_placeholder": "Voer uw gebruikersnaam of e-mailadres in", - "password": "Wachtwoord", - "password_placeholder": "Voer je wachtwoord in", - "login_button": "Inloggen", - "no_account": "Nog geen account?", - "register": "Aanmelden", - "admin_setup": "Eerste keer?", - "setup": "Administrator instellen", - "register_title": "Account aanmaken", - "email": "E-mailadres", - "email_placeholder": "Voer je e-mailadres in", - "confirm_password": "Bevestig wachtwoord", - "confirm_password_placeholder": "Bevestig je wachtwoord", - "register_button": "Account aanmaken", - "have_account": "Heb je al een account?", - "login": "Inloggen", - "setup_title": "Eerste setup", - "setup_step1": "Admin", - "setup_step2": "Systeem", - "setup_step3": "Voltooid", - "admin_username": "Admin gebruikersnaam", - "admin_email": "Admin e-mailadres", - "admin_password": "Admin wachtwoord", - "create_admin": "Administrator aanmaken", - "back_to_login": "Al ingesteld?", - "admin_success": "Administrator account succesvol aangemaakt! Je kunt nu inloggen.", - "account_success": "Account succesvol aangemaakt! Je kunt nu inloggen.", - "passwords_mismatch": "Wachtwoorden komen niet overeen", - "admin_create_error": "Fout bij het aanmaken van het administratoraccount", - "or": "of", - "sso_login": "Inloggen met SSO", - "sso_login_provider": "Inloggen met {{provider}}", - "magicLinkHint": "Geen wachtwoord? Voer uw e-mailadres in en we sturen u een eenmalige aanmeldlink.", - "magicLinkEmailLabel": "E-mailadres", - "magicLinkEmailPlaceholder": "jij@voorbeeld.nl", - "magicLinkSubmit": "Aanmeldlink versturen", - "magicLinkSent": "Als er een account bestaat voor dat e-mailadres, is een aanmeldlink verzonden. Controleer uw inbox.", - "magicLinkUnavailable": "Aanmelden per e-mail is niet beschikbaar op deze server.", - "magicLinkNetworkError": "Kan de server niet bereiken: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Opslag", - "calculating": "Bezig met berekenen...", - "used": "{{percentage}}% gebruikt ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Dit bestandstype kan niet bekeken worden.", - "download_file": "Bestand downloaden", - "zoom_in": "Inzoomen", - "zoom_out": "Uitzoomen", - "zoom_reset": "Zoom terugzetten" - }, - "language_selector": { - "title": "Welkom!", - "subtitle": "Selecteer je taal om door te gaan", - "continue": "Doorgaan", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Nog geen favorieten", - "empty_hint": "Markeer bestanden of mappen om ze aan je favorieten toe te voegen", - "add": "Toevoegen aan favorieten", - "remove": "Verwijderen uit favorieten", - "added_title": "Toegevoegd aan favorieten", - "added_msg": "toegevoegd aan favorieten", - "removed_title": "Verwijderd uit favorieten", - "removed_msg": "verwijderd uit favorieten" - }, - "recent": { - "title": "Recent", - "clear": "Recente wissen", - "accessed": "Geopend", - "empty_state": "Geen recente bestanden", - "empty_hint": "Bestanden die je opent verschijnen hier", - "loadMore": "Meer laden" - }, - "notifications": { - "file_renamed": "Bestand hernoemd", - "file_renamed_to": "Bestand hernoemd naar \"{{name}}\"", - "folder_renamed": "Map hernoemd", - "folder_renamed_to": "Map hernoemd naar \"{{name}}\"", - "file_uploaded": "Bestand geüpload", - "file_deleted": "Bestand verplaatst naar prullenbak", - "folder_deleted": "Map verplaatst naar prullenbak", - "item_deleted_permanently": "Item permanent verwijderd", - "trash_emptied": "Prullenbak succesvol geleegd", - "title": "Notificaties", - "empty": "Geen notificaties", - "link_created": "Link aangemaakt", - "share_success": "Deellink succesvol aangemaakt", - "upload_files_section_title": "Uploaden hier niet beschikbaar", - "upload_files_section_body": "Ga naar de sectie Bestanden om bestanden te uploaden" - }, - "batch": { - "one_selected": "1 item geselecteerd", - "n_selected": "{{count}} items geselecteerd", - "confirm_delete": "Weet je zeker dat je {{count}} items naar de prullenbak wilt verplaatsen?", - "move_title": "Verplaats {{count}} item(s)", - "add_favorites": "Toevoegen aan favorieten", - "move_copy": "Verplaatsen of kopiëren" - }, - "admin": { - "page_title": "Beheerderspaneel", - "back_to_app": "Terug naar OxiCloud", - "loading": "Laden…", - "access_denied": "Toegang geweigerd", - "access_denied_desc": "Beheerdersrechten vereist.", - "sign_in": "Inloggen", - "tab_dashboard": "Dashboard", - "tab_users": "Gebruikers", - "tab_oidc": "SSO / OIDC", - "total_users": "Totaal gebruikers", - "active_users": "Actieve gebruikers", - "admins": "Beheerders", - "version": "Versie", - "storage_overview": "Opslagoverzicht", - "used": "Gebruikt", - "total_quota": "Totaal quotum", - "usage_pct": "Gebruik %", - "users_over_80": "Gebruikers >80% quotum", - "users_over_quota": "Gebruikers boven quotum", - "system": "Systeem", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quota", - "enabled": "Ingeschakeld", - "disabled": "Uitgeschakeld", - "active": "Actief", - "off": "Uit", - "allow_registration": "Openbare zelfregistratie toestaan", - "registration_warning": "Openbare registratie is uitgeschakeld. Alleen beheerders kunnen gebruikers aanmaken.", - "user_management": "Gebruikersbeheer", - "create_user": "Gebruiker aanmaken", - "col_user": "Gebruiker", - "col_role": "Rol", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Opslag", - "col_last_login": "Laatste login", - "col_actions": "Acties", - "loading_users": "Gebruikers laden…", - "failed_load_users": "Laden mislukt", - "no_users_found": "Geen gebruikers gevonden", - "showing_users": "Toont {{from}}-{{to}} van {{total}}", - "prev": "Vorige", - "next": "Volgende", - "inactive": "Inactief", - "you_badge": "(jij)", - "local": "Lokaal", - "never": "Nooit", - "just_now": "Zojuist", - "minutes_ago": "{{n}}min geleden", - "hours_ago": "{{n}}u geleden", - "days_ago": "{{n}}d geleden", - "edit_quota_title": "Quotum bewerken", - "reset_password_title": "Wachtwoord resetten", - "toggle_role_title": "Rol wisselen", - "deactivate_title": "Deactiveren", - "activate_title": "Activeren", - "delete_title": "Verwijderen", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "SSO-authenticatie inschakelen", - "provider_name": "Providernaam", - "issuer_url": "Uitgever-URL", - "issuer_url_hint": "OpenID Connect uitgever-URL", - "auto_discover": "Auto-ontdekking", - "discovering": "Ontdekken…", - "client_id": "Client-ID", - "client_secret": "Client-secret", - "client_secret_placeholder": "Laat leeg om huidige waarde te behouden", - "secret_configured": "Een client-secret is al geconfigureerd", - "callback_url": "Callback-URL", - "callback_url_hint": "(registreer bij uw IdP)", - "advanced_settings": "Geavanceerde instellingen", - "scopes": "Scopes", - "auto_provision": "Gebruikers automatisch aanmaken bij eerste login", - "admin_groups": "Beheergroepen", - "admin_groups_hint": "Kommagescheiden OIDC-groepsnamen", - "disable_password": "Wachtwoord-login uitschakelen (alleen OIDC)", - "password_warning": "Dit voorkomt ALLE logins op basis van wachtwoord!", - "test_btn": "Testen", - "save_btn": "Opslaan", - "saving": "Opslaan…", - "settings_saved": "Instellingen opgeslagen — OIDC is nu {{status}}", - "quota_modal_title": "Opslagquotum bijwerken", - "quota_user_label": "Gebruiker:", - "new_quota": "Nieuw quotum", - "quota_unlimited_hint": "0 voor onbeperkt", - "cancel": "Annuleren", - "create_user_title": "Nieuwe gebruiker aanmaken", - "username_label": "Gebruikersnaam", - "username_placeholder": "jandevries", - "username_hint": "3–32 tekens", - "password_label": "Wachtwoord", - "password_placeholder": "Min 8 tekens", - "email_label": "E-mail", - "email_optional": "(optioneel)", - "email_placeholder": "gebruiker@voorbeeld.nl (automatisch indien leeg)", - "role_label": "Rol", - "role_user": "Gebruiker", - "role_admin": "Beheerder", - "quota_label": "Quotum", - "creating": "Aanmaken…", - "reset_pw_title": "Wachtwoord resetten", - "new_password_label": "Nieuw wachtwoord", - "resetting": "Resetten…", - "reset_btn": "Resetten", - "confirm_role_change": "Rol wijzigen naar {{role}}?", - "confirm_deactivate": "Weet u zeker dat u deze gebruiker wilt deactiveren?", - "confirm_activate": "Weet u zeker dat u deze gebruiker wilt activeren?", - "confirm_delete_user": "Gebruiker \"{{name}}\" VERWIJDEREN? Kan niet ongedaan worden gemaakt!", - "confirm_action": "Actie bevestigen", - "confirm_yes": "Bevestigen", - "confirm_no": "Annuleren", - "error_username_short": "Gebruikersnaam moet minimaal 3 tekens bevatten", - "error_password_short": "Wachtwoord moet minimaal 8 tekens bevatten", - "error_generic": "Mislukt", - "error_network": "Netwerkfout: {{message}}", - "error_create_user": "Kan gebruiker niet aanmaken", - "tab_storage": "Opslag", - "storage_title": "Opslagconfiguratie", - "storage_current_backend": "Huidig backend", - "storage_total_blobs": "Totaal blobs", - "storage_total_size": "Totale grootte", - "storage_dedup_ratio": "Deduplicatieverhouding", - "storage_backend": "Backend", - "storage_local": "Lokaal", - "storage_s3": "S3-compatibel", - "storage_provider_preset": "Providerinstelling", - "storage_preset_custom": "Aangepast", - "storage_endpoint_url": "Eindpunt-URL", - "storage_endpoint_hint": "Leeg laten voor AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Regio", - "storage_access_key": "Toegangssleutel", - "storage_secret_key": "Geheime sleutel", - "storage_secret_configured": "Sleutel geconfigureerd", - "storage_key_placeholder": "Nieuwe sleutel invoeren", - "storage_path_style": "Padstijl forceren", - "storage_path_style_hint": "Vereist voor MinIO en sommige S3-compatibele diensten", - "storage_test_connection": "Verbinding testen", - "storage_test_success": "Verbinding geslaagd", - "storage_test_failure": "Verbinding mislukt", - "storage_save": "Configuratie opslaan", - "storage_saved": "Configuratie opgeslagen", - "storage_migration": "Gegevensmigratie", - "storage_migration_coming_soon": "Migratietools binnenkort beschikbaar", - "migration_status_label": "Migratiestatus", - "migration_start": "Migratie starten", - "migration_pause": "Pauzeren", - "migration_resume": "Hervatten", - "migration_verify": "Verifiëren", - "migration_complete": "Voltooien", - "migration_started": "Migratie gestart", - "migration_paused_msg": "Migratie gepauzeerd", - "migration_resumed_msg": "Migratie hervat", - "migration_completed_msg": "Migratie succesvol voltooid", - "migration_verifying": "Bezig met verifiëren...", - "migration_verify_passed": "Verificatie geslaagd", - "migration_verify_failed": "Verificatie mislukt", - "migration_failed_blobs": "Mislukte blobs", - "testing": "Bezig met testen...", - "smtp_disabled": "Uitgeschakeld (host niet ingesteld)", - "smtp_enabled": "Ingeschakeld", - "smtp_enabled_label": "Status", - "smtp_intro": "SMTP wordt uitsluitend geconfigureerd via omgevingsvariabelen (OXICLOUD_SMTP_*). De onderstaande waarden worden gelezen uit de actieve server — om ze te wijzigen, bewerk de omgeving en herstart OxiCloud.", - "smtp_not_configured": "SMTP is niet geconfigureerd op deze server.", - "smtp_send_failed": "Verzenden mislukt.", - "smtp_send_test": "Test-e-mail verzenden", - "smtp_sending": "Bezig met verzenden…", - "smtp_sent": "Test-e-mail verzonden.", - "smtp_server_code": "Serverantwoord", - "smtp_test_intro": "Verzendt een vooraf gedefinieerd diagnostisch bericht naar de onderstaande ontvanger en rapporteert het antwoord van de SMTP-server, zodat je het kunt correleren met je relay-logboeken.", - "smtp_test_missing_to": "Voer een ontvangeradres in.", - "smtp_test_title": "Test-e-mail verzenden", - "smtp_test_to": "Ontvangeradres", - "smtp_title": "Uitgaande e-mail (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profiel", - "back_to_app": "Terug naar OxiCloud", - "loading": "Laden…", - "not_authenticated": "Niet geauthenticeerd", - "not_authenticated_desc": "Log in om uw profiel te bekijken.", - "sign_in": "Inloggen", - "role_admin": "Beheerder", - "role_user": "Gebruiker", - "account_details": "Accountgegevens", - "username": "Gebruikersnaam", - "email": "E-mail", - "role": "Rol", - "last_login": "Laatste login", - "storage": "Opslag", - "used": "Gebruikt", - "quota": "Quotum", - "usage": "Gebruik", - "unlimited": "Onbeperkt", - "app_passwords": "App-wachtwoorden", - "app_pw_desc": "Genereer wachtwoorden voor WebDAV-, CalDAV- en CardDAV-clients. Elk wachtwoord wordt slechts één keer getoond.", - "app_pw_label_placeholder": "Label (bijv. Thunderbird, macOS)", - "generate": "Genereren", - "generating": "Genereren…", - "new_password_for": "Nieuw wachtwoord voor", - "copy_warning": "Kopieer dit wachtwoord nu. U kunt het niet opnieuw bekijken.", - "copy_to_clipboard": "Kopiëren naar klembord", - "col_label": "Label", - "col_created": "Aangemaakt", - "col_last_used": "Laatst gebruikt", - "col_status": "Status", - "active": "Actief", - "revoked": "Ingetrokken", - "revoke_title": "Intrekken", - "no_app_passwords": "Nog geen app-wachtwoorden.", - "client_sessions": "Clientsessies", - "client_sessions_desc": "Automatisch gegenereerd bij het verbinden van een Nextcloud-compatibele client.", - "col_client": "Client", - "never": "Nooit", - "just_now": "Zojuist", - "minutes_ago": "{{n}} min geleden", - "hours_ago": "{{n}}u geleden", - "days_ago": "{{n}} dagen geleden", - "edit_profile": "Profiel bewerken", - "edit_oidc_managed": "Om uw gegevens (naam, voornaam, profielfoto, …) te wijzigen, werk ze bij bij uw identity provider. De wijzigingen verschijnen bij uw volgende aanmelding.", - "username_claim_hint": "2–64 tekens, letters / cijfers / punt / streepje / underscore. Eenmaal gekozen kan de gebruikersnaam niet meer worden gewijzigd (DAV/NextCloud-clients zijn ervan afhankelijk).", - "username_already_claimed": "Gebruikersnaam ingesteld en niet wijzigbaar (DAV/NextCloud-clients zijn ervan afhankelijk).", - "given_name": "Voornaam", - "family_name": "Achternaam", - "notify_on_share": "Stuur me een e-mail wanneer iemand iets met mij deelt", - "notify_on_share_hint": "Wanneer uitgevinkt, verschijnen gedeelde items nog steeds in je account — je krijgt er alleen geen e-mail over.", - "save_profile": "Wijzigingen opslaan", - "profile_saved": "Profiel bijgewerkt", - "profile_no_changes": "Geen wijzigingen om op te slaan.", - "profile_save_failed": "Opslaan mislukt", - "username_taken_error": "Die gebruikersnaam is al in gebruik.", - "username_immutable_error": "Uw gebruikersnaam is al ingesteld en kan hier niet worden gewijzigd. Neem contact op met een beheerder als u wilt hernoemen.", - "change_password": "Wachtwoord wijzigen", - "current_password": "Huidig wachtwoord", - "new_password": "Nieuw wachtwoord", - "min_8_chars": "Minimaal 8 tekens", - "confirm_password": "Bevestig nieuw wachtwoord", - "update_password": "Wachtwoord bijwerken", - "updating": "Bijwerken…", - "password_updated": "Wachtwoord succesvol bijgewerkt", - "passwords_no_match": "Wachtwoorden komen niet overeen", - "password_too_short": "Wachtwoord moet minimaal 8 tekens bevatten", - "password_change_failed": "Wachtwoord wijzigen mislukt", - "error_network": "Netwerkfout: {{message}}", - "error_label_required": "Voer een label in", - "error_create_pw": "App-wachtwoord aanmaken mislukt", - "confirm_revoke": "App-wachtwoord \"{{label}}\" intrekken? Clients die dit wachtwoord gebruiken zullen stoppen.", - "error_revoke": "Intrekken mislukt", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Bezig met uploaden...", - "files": "bestanden", - "complete": "{{count}} / {{total}} geüpload" - }, - "storage_quota_exceeded": "Opslagquotum overschreden", - "sharedwithme": { - "pageTitle": "Gedeeld met mij", - "pageDescription": "Bestanden en mappen die andere gebruikers met u hebben gedeeld", - "emptyStateTitle": "Er is nog niets met u gedeeld", - "emptyStateDesc": "Items die andere gebruikers met u delen, verschijnen hier", - "loadMore": "Meer laden", - "sharedBy": "Gedeeld door", - "colName": "Naam", - "colType": "Type", - "colSharedBy": "Gedeeld door", - "colDate": "Datum gedeeld", - "colPermissions": "Machtigingen" - }, - "groupby": { - "none": "Geen", - "title": "Groeperen op", - "owner": "Eigenaar", - "shareDate": "Deeldatum", - "type": "Type", - "type.folders": "Mappen", - "accessedAt": "Toegangsdatum", - "modifiedAt": "Wijzigingsdatum", - "createdAt": "Aanmaakdatum", - "size": "Grootte", - "favoriteDate": "Favoritendatum", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nieuw" - }, - "dateBucket": { - "today": "Vandaag", - "last7days": "Afgelopen 7 dagen", - "last30days": "Afgelopen 30 dagen" - }, - "groups": { - "title": "Groepen beheren", - "create_button": "Groep maken", - "create_dialog_title": "Nieuwe groep", - "edit_dialog_title": "Groep hernoemen", - "name_label": "Naam", - "name_placeholder": "engineering", - "description_label": "Beschrijving (optioneel)", - "members_section": "Leden", - "add_member_placeholder": "Een gebruiker of groep toevoegen…", - "no_members": "Nog geen leden.", - "remove_member": "Verwijderen", - "delete_group": "Groep verwijderen", - "delete_confirm": "De groep \"{name}\" verwijderen? Aan deze groep gekoppelde rechten worden ingetrokken.", - "empty_state": "Nog geen groepen.", - "load_more": "Meer laden", - "back_to_list": "Terug", - "loading": "Bezig met laden…", - "virtual_badge": "Systeem", - "member_count_zero": "Geen leden", - "member_count_one": "1 lid", - "member_count_other": "{count} leden", - "delete_confirm_label": "Typ de groepsnaam ter bevestiging:", - "delete_confirm_mismatch": "Typ de groepsnaam exact om te bevestigen.", - "virtual_internal_name": "Intern", - "members_loading": "Leden laden…", - "members_empty": "Geen leden", - "virtual_internal_explanation": "Iedere interne gebruiker op deze server" - }, - "myshares": { - "copyLink": "Link kopiëren", - "deleteLink": "Link verwijderen", - "notifyByEmail": "Per e-mail notificeren", - "notifyFailed": "Notificatie kon niet worden verzonden.", - "notifyGroupMembers": "Groepsleden notificeren", - "notifyRateLimited": "Te veel notificaties voor deze ontvanger — probeer het later opnieuw.", - "removeAccess": "Toegang verwijderen", - "resendInvitation": "Uitnodigingsmail opnieuw verzenden" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", + "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen OxiCloud om je nieuwe gedeelde item te bekijken:\n{{login_link}}\n\nMisschien heb je nog meer nieuwe gedeelde items van {{inviter}} — meld je aan om al je gedeelde items te zien.\n\n— OxiCloud\n\nJe ontvangt dit bericht omdat je een OxiCloud-account hebt en je voorkeur voor deelmeldingen aanstaat. Je kunt het uitzetten in je profiel (Stuur me een e-mail wanneer iemand iets met mij deelt)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalistisch cloudopslagsysteem" + }, + "nav": { + "files": "Bestanden", + "shared": "Gedeeld", + "recent": "Recente", + "favorites": "Favorieten", + "photos": "Foto's", + "music": "Muziek", + "trash": "Prullenbak", + "sharedwithme": "Gedeeld met mij", + "profile": "Profiel", + "shared_with_me": "Gedeeld met mij" + }, + "photos": { + "empty_state": "Nog geen foto's", + "empty_hint": "Upload afbeeldingen of video's om ze hier te zien", + "items_selected": "geselecteerd", + "view_daily": "Dag", + "view_monthly": "Maand", + "view_yearly": "Jaar", + "group_by": "Groeperen op" + }, + "music": { + "create_playlist": "Afspeellijst Maken", + "playlists": "Afspeellijsten", + "no_playlists": "Nog geen afspeellijsten", + "select_playlist": "Selecteer een afspeellijst", + "select_hint": "Kies een afspeellijst uit de zijbalk of maak een nieuwe", + "add_tracks": "Tracks Toevoegen", + "no_tracks": "Geen tracks in deze afspeellijst", + "unknown_artist": "Onbekende Artiest", + "unknown_title": "Onbekend", + "confirm_delete": "Deze afspeellijst verwijderen?", + "playlist_name": "Naam afspeellijst", + "create": "Maken", + "delete": "Verwijderen", + "share": "Delen", + "edit": "Bewerken", + "play_all": "Alles Afspelen", + "shuffle": "Shuffle", + "repeat": "Herhalen", + "repeat_one": "Een Herhalen", + "queue": "Wachtrij", + "queue_empty": "Wachtrij is leeg", + "not_playing": "Niet afspelend", + "play": "Afspelen", + "pause": "Pauzeren", + "previous": "Vorige", + "next": "Volgende", + "volume": "Volume", + "mute": "Dempen", + "unmute": "Geluid aan", + "title": "Titel", + "artist": "Artiest", + "album": "Album", + "tracks": "tracks", + "add": "Toevoegen", + "added": "Toegevoegd!", + "added_to_playlist": "toegevoegd aan playlist", + "add_to_playlist": "Aan playlist toevoegen", + "load_error": "Fout bij laden van playlists", + "add_error": "Kon tracks niet toevoegen aan playlist", + "no_playlists_yet": "Nog geen playlists. Maak er eerst een!", + "selected_files": "Geselecteerd:", + "error": "Fout", + "search_audio": "Audiobestanden zoeken…", + "no_audio_files": "Geen audiobestanden gevonden", + "selected": "geselecteerd", + "loading": "Laden…", + "search_error": "Kan audiobestanden niet laden", + "adding": "Toevoegen…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "Vorige" + }, + "actions": { + "search": "Zoek bestanden...", + "new_folder": "Nieuwe map", + "upload": "Uploaden", + "upload_files": "Bestanden uploaden", + "upload_folder": "Map uploaden", + "upload.uploading": "Uploaden...", + "upload.complete": "{count} / {total} geüpload", + "upload.files": "bestanden", + "rename": "Hernoemen", + "move": "Verplaatsen naar...", + "move_to": "Verplaatsen naar", + "delete": "Verwijderen", + "download": "Downloaden", + "view": "Bekijken", + "cancel": "Annuleren", + "confirm": "Bevestigen", + "share": "Delen", + "favorite": "Toevoegen aan favorieten", + "unfavorite": "Verwijderen uit favorieten", + "copy": "Kopiëren", + "notify": "Melden", + "send": "Verzenden", + "clear_recent": "Recente wissen", + "logout": "Uitloggen", + "create": "Maken", + "search_btn": "Zoeken", + "close": "Sluiten", + "delete_permanently": "Permanent verwijderen", + "empty_trash": "Prullenbak legen", + "open_parent_folder": "Naar bovenliggende map", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Weergave", + "about": "Over OxiCloud", + "about_description": "Cloudopslagplatform gebouwd met Rust & Clean Architecture. Snel, veilig en privé.", + "admin_panel": "Beheerpaneel", + "profile": "Mijn profiel", + "role_user": "Gebruiker", + "theme": { + "light": "Licht", + "dark": "Donker", + "auto": "Zoals systeem" + }, + "manage_groups": "Groepen beheren", + "admin": "Admin" + }, + "share": { + "dialogTitle": "Deellink", + "linkLabel": "Deellink:", + "copyLink": "Kopiëren", + "permissions": "Rechten:", + "permissionRead": "Lezen", + "permissionWrite": "Schrijven", + "permissionReshare": "Opnieuw delen", + "password": "Wachtwoordbeveiliging:", + "generatePassword": "Genereren", + "expiration": "Verloopdatum:", + "update": "Delen bijwerken", + "remove": "Delen verwijderen", + "notifyTitle": "Notificatie verzenden", + "notifyEmailLabel": "E-mailadres:", + "notifyMessageLabel": "Bericht (optioneel):", + "notifySend": "Notificatie verzenden", + "shareWithOthers": "Met anderen delen", + "sharePublicly": "Openbaar delen", + "shareSettings": "Deelinstellingen", + "shareCopied": "Link gekopieerd naar klembord", + "shareCreated": "Deellink succesvol aangemaakt", + "shareUpdated": "Deelinstellingen bijgewerkt", + "shareRemoved": "Delen verwijderd", + "inviteByEmail": "Uitnodigen via e-mail — uitnodiging wordt verzonden", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Kopiëren", + "copy_failed": "Could not copy link", + "download": "Downloaden", + "files": "Bestanden", + "folders": "Mappen", + "link_name": "Link name (optional)", + "notifyByEmail": "Per e-mail notificeren", + "revoke": "Remove", + "role_label": "Rol" + }, + "share_dialogTitle": "Deellink", + "share_linkLabel": "Deellink:", + "share_copyLink": "Kopiëren", + "share_permissions": "Rechten:", + "share_permissionRead": "Lezen", + "share_permissionWrite": "Schrijven", + "share_permissionReshare": "Opnieuw delen", + "share_password": "Wachtwoordbeveiliging:", + "share_generatePassword": "Genereren", + "share_expiration": "Verloopdatum:", + "share_update": "Share bijwerken", + "share_remove": "Share verwijderen", + "share_notifyTitle": "Notificatie verzenden", + "share_notifyEmailLabel": "E-mailadres:", + "share_notifyMessageLabel": "Bericht (optioneel):", + "share_notifySend": "Notificatie verzenden", + "shared": { + "backToFiles": "Terug naar Bestanden", + "pageTitle": "Gedeelde items", + "pageDescription": "Beheer je gedeelde bestanden en mappen", + "filterType": "Type:", + "filterAll": "Alles", + "filterFiles": "Bestanden", + "filterFolders": "Mappen", + "sortBy": "Sorteren op:", + "sortByName": "Naam", + "sortByDate": "Datum gedeeld", + "sortByExpiration": "Verloop", + "search": "Zoeken", + "colName": "Naam", + "colType": "Type", + "colDateShared": "Gedeeld op", + "colExpiration": "Verloop", + "colPermissions": "Rechten", + "colPassword": "Wachtwoord", + "colActions": "Acties", + "emptyStateTitle": "Nog geen gedeelde items", + "emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", + "goToFiles": "Ga naar Bestanden", + "typeFile": "Bestand", + "typeFolder": "Map", + "noExpiration": "Geen verloop", + "hasPassword": "Ja", + "noPassword": "Nee", + "editShare": "Delen bewerken", + "notifyShare": "Iemand informeren", + "copyLink": "Link kopiëren", + "removeShare": "Delen verwijderen", + "linkCopied": "Link gekopieerd naar klembord!", + "linkCopyFailed": "Kopiëren van link mislukt", + "itemUpdated": "Deelinstellingen bijgewerkt", + "itemRemoved": "Delen verwijderd", + "invalidEmail": "Voer een geldig e-mailadres in", + "notificationSent": "Notificatie verzonden", + "notificationFailed": "Notificatie verzenden mislukt", + "shared_backToFiles": "Terug naar Bestanden", + "shared_pageTitle": "Gedeelde items", + "shared_pageDescription": "Beheer je gedeelde bestanden en mappen", + "shared_filterType": "Type:", + "shared_filterAll": "Alles", + "shared_filterFiles": "Bestanden", + "shared_filterFolders": "Mappen", + "shared_sortBy": "Sorteren op:", + "shared_sortByName": "Naam", + "shared_sortByDate": "Datum gedeeld", + "shared_sortByExpiration": "Verloop", + "shared_search": "Zoeken", + "shared_colName": "Naam", + "shared_colType": "Type", + "shared_colDateShared": "Gedeeld op", + "shared_colExpiration": "Verloop", + "shared_colPermissions": "Rechten", + "shared_colPassword": "Wachtwoord", + "shared_colActions": "Acties", + "shared_emptyStateTitle": "Nog geen gedeelde items", + "shared_emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", + "shared_goToFiles": "Ga naar Bestanden", + "shared_typeFile": "Bestand", + "shared_typeFolder": "Map", + "shared_noExpiration": "Geen verloop", + "shared_hasPassword": "Ja", + "shared_noPassword": "Nee", + "shared_editShare": "Delen bewerken", + "shared_notifyShare": "Iemand informeren", + "shared_copyLink": "Link kopiëren", + "shared_removeShare": "Delen verwijderen", + "shared_linkCopied": "Link gekopieerd naar klembord!", + "shared_linkCopyFailed": "Kopiëren van link mislukt", + "shared_itemUpdated": "Deelinstellingen bijgewerkt", + "shared_itemRemoved": "Delen verwijderd", + "shared_invalidEmail": "Voer een geldig e-mailadres in", + "shared_notificationSent": "Notificatie verzonden", + "shared_notificationFailed": "Notificatie verzenden mislukt" + }, + "files": { + "name": "Naam", + "type": "Type", + "size": "Grootte", + "modified": "Gewijzigd", + "no_files": "Geen bestanden in deze map", + "empty_hint": "Upload bestanden of maak mappen aan om te beginnen", + "loading": "Bestanden laden…", + "view_grid": "Rasterweergave", + "view_list": "Lijstweergave", + "file_types": { + "document": "Document", + "image": "Afbeelding", + "video": "Video", + "audio": "Audio", + "pdf": "PDF", + "text": "Tekst", + "folder": "Map", + "spreadsheet": "Spreadsheet", + "presentation": "Presentatie", + "archive": "Archief", + "installer": "Installatiebestand", + "code": "Code" + }, + "owner": "Eigenaar", + "add_favorites": "Toevoegen aan favorieten", + "added_favorites": "Toegevoegd aan favorieten", + "col_name": "Naam", + "col_owner": "Eigenaar", + "col_size": "Grootte", + "col_type": "Type", + "copy": "Kopiëren", + "edit": "Bewerken", + "file": "Bestand", + "folder": "Map", + "new_folder": "Nieuwe map", + "share": "Delen", + "view": "Bekijken" + }, + "dialogs": { + "rename_folder": "Map hernoemen", + "rename_file": "Bestand hernoemen", + "new_name": "Nieuwe naam", + "new_folder_title": "Nieuwe map", + "folder_name": "Mapnaam", + "folder_placeholder": "Mijn map", + "rename_title": "Hernoemen", + "move_file": "Bestand verplaatsen", + "move_folder": "Map verplaatsen", + "select_destination": "Selecteer doelmap:", + "root": "Hoofdmap", + "delete_confirmation": "Weet je zeker dat je wilt verwijderen", + "and_contents": "en alle inhoud", + "no_undo": "Deze actie kan niet ongedaan gemaakt worden", + "confirm_title": "Actie bevestigen", + "confirm_delete": "Verplaatsen naar prullenbak", + "confirm_delete_file": "Weet je zeker dat je het bestand \"{{name}}\" naar de prullenbak wilt verplaatsen?", + "confirm_delete_folder": "Weet je zeker dat je de map \"{{name}}\" en alle inhoud naar de prullenbak wilt verplaatsen?", + "confirm_permanent_delete": "Permanent verwijderen", + "confirm_permanent_delete_msg": "Weet je zeker dat je dit item permanent wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden.", + "confirm_empty_trash": "Prullenbak legen", + "confirm_delete_share": "Deellink verwijderen", + "confirm_delete_share_msg": "Weet je zeker dat je deze deellink wilt verwijderen?", + "share_file": "Bestand delen", + "share_folder": "Map delen", + "existing_shares": "Bestaande delingen", + "share_options": "Deelopties", + "password": "Wachtwoord", + "expiration": "Verloop", + "permissions": "Rechten", + "generated_link": "Gegenereerde link", + "notify": "Notificatie verzenden", + "recipient": "Ontvanger", + "message": "Bericht", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Verplaatsen naar de thuismap" + }, + "dropzone": { + "drag_files": "Sleep bestanden hierheen of klik om te selecteren", + "drop_files": "Laat bestanden vallen om te uploaden" + }, + "permissions": { + "read": "Lezen", + "write": "Schrijven", + "reshare": "Opnieuw delen" + }, + "errors": { + "file_not_found": "Bestand niet gevonden", + "folder_not_found": "Map niet gevonden", + "delete_error": "Fout bij verwijderen", + "upload_error": "Fout bij uploaden van bestand", + "rename_error": "Fout bij hernoemen", + "move_error": "Fout bij verplaatsen", + "empty_name": "Naam mag niet leeg zijn", + "name_exists": "Een bestand of map met deze naam bestaat al", + "generic_error": "Er is een fout opgetreden", + "group_name_invalid": "De groepsnaam moet voldoen aan het e-mailprefix-formaat (letters, cijfers, punt, streepje, underscore; 1–64 tekens).", + "group_cycle": "Dit lid zou een circulaire groepsverwijzing veroorzaken.", + "group_depth_exceeded": "Deze nestdiepte overschrijdt het maximum (8).", + "group_virtual_immutable": "De groep 'Internal' wordt door het systeem beheerd en kan niet worden gewijzigd.", + "group_not_found": "Groep niet gevonden.", + "group_name_taken": "Er bestaat al een groep met deze naam." + }, + "breadcrumb": { + "home": "Start" + }, + "trash": { + "empty_trash": "Prullenbak legen", + "empty_state": "Prullenbak is leeg", + "original_location": "Oorspronkelijke locatie", + "deleted_date": "Verwijderdatum", + "remaining": "Resterend", + "actions": "Acties", + "restore": "Herstellen", + "delete_permanently": "Permanent verwijderen", + "empty_confirm": "Weet je zeker dat je de prullenbak wilt legen? Dit verwijdert alle items permanent.", + "groupby": { + "remaining_days": "Resterende dagen", + "trashed_time": "Verwijderd op" + }, + "delete": "Permanent verwijderen", + "empty_action": "Prullenbak legen" + }, + "daysRemaining": { + "expired": "Verlopen", + "today": "Vandaag", + "tomorrow": "Morgen", + "inDays": "{{count}} dagen" + }, + "expiryChip": { + "never": "Verloopt nooit", + "expired": "Verlopen", + "today": "Verloopt vandaag", + "tomorrow": "Verloopt morgen", + "inDays": "Verloopt over {{count}} dagen", + "onDate": "Verloopt op {{date}}" + }, + "auth": { + "login_title": "Inloggen", + "username": "Gebruikersnaam", + "username_placeholder": "Voer je gebruikersnaam in", + "login_identifier": "Gebruikersnaam of e-mail", + "login_identifier_placeholder": "Voer uw gebruikersnaam of e-mailadres in", + "password": "Wachtwoord", + "password_placeholder": "Voer je wachtwoord in", + "login_button": "Inloggen", + "no_account": "Nog geen account?", + "register": "Aanmelden", + "admin_setup": "Eerste keer?", + "setup": "Administrator instellen", + "register_title": "Account aanmaken", + "email": "E-mailadres", + "email_placeholder": "Voer je e-mailadres in", + "confirm_password": "Bevestig wachtwoord", + "confirm_password_placeholder": "Bevestig je wachtwoord", + "register_button": "Account aanmaken", + "have_account": "Heb je al een account?", + "login": "Inloggen", + "setup_title": "Eerste setup", + "setup_step1": "Admin", + "setup_step2": "Systeem", + "setup_step3": "Voltooid", + "admin_username": "Admin gebruikersnaam", + "admin_email": "Admin e-mailadres", + "admin_password": "Admin wachtwoord", + "create_admin": "Administrator aanmaken", + "back_to_login": "Al ingesteld?", + "admin_success": "Administrator account succesvol aangemaakt! Je kunt nu inloggen.", + "account_success": "Account succesvol aangemaakt! Je kunt nu inloggen.", + "passwords_mismatch": "Wachtwoorden komen niet overeen", + "admin_create_error": "Fout bij het aanmaken van het administratoraccount", + "or": "of", + "sso_login": "Inloggen met SSO", + "sso_login_provider": "Inloggen met {{provider}}", + "magicLinkHint": "Geen wachtwoord? Voer uw e-mailadres in en we sturen u een eenmalige aanmeldlink.", + "magicLinkEmailLabel": "E-mailadres", + "magicLinkEmailPlaceholder": "jij@voorbeeld.nl", + "magicLinkSubmit": "Aanmeldlink versturen", + "magicLinkSent": "Als er een account bestaat voor dat e-mailadres, is een aanmeldlink verzonden. Controleer uw inbox.", + "magicLinkUnavailable": "Aanmelden per e-mail is niet beschikbaar op deze server.", + "magicLinkNetworkError": "Kan de server niet bereiken: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "E-mailadres", + "magic_hint": "Geen wachtwoord? Voer uw e-mailadres in en we sturen u een eenmalige aanmeldlink.", + "magic_unavailable": "Aanmelden per e-mail is niet beschikbaar op deze server.", + "passwords_match": "Passwords match", + "sign_in": "Inloggen" + }, + "storage": { + "title": "Opslag", + "calculating": "Bezig met berekenen...", + "used": "{{percentage}}% gebruikt ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Dit bestandstype kan niet bekeken worden.", + "download_file": "Bestand downloaden", + "zoom_in": "Inzoomen", + "zoom_out": "Uitzoomen", + "zoom_reset": "Zoom terugzetten" + }, + "language_selector": { + "title": "Welkom!", + "subtitle": "Selecteer je taal om door te gaan", + "continue": "Doorgaan", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Nog geen favorieten", + "empty_hint": "Markeer bestanden of mappen om ze aan je favorieten toe te voegen", + "add": "Toevoegen aan favorieten", + "remove": "Verwijderen uit favorieten", + "added_title": "Toegevoegd aan favorieten", + "added_msg": "toegevoegd aan favorieten", + "removed_title": "Verwijderd uit favorieten", + "removed_msg": "verwijderd uit favorieten" + }, + "recent": { + "title": "Recent", + "clear": "Recente wissen", + "accessed": "Geopend", + "empty_state": "Geen recente bestanden", + "empty_hint": "Bestanden die je opent verschijnen hier", + "loadMore": "Meer laden" + }, + "notifications": { + "file_renamed": "Bestand hernoemd", + "file_renamed_to": "Bestand hernoemd naar \"{{name}}\"", + "folder_renamed": "Map hernoemd", + "folder_renamed_to": "Map hernoemd naar \"{{name}}\"", + "file_uploaded": "Bestand geüpload", + "file_deleted": "Bestand verplaatst naar prullenbak", + "folder_deleted": "Map verplaatst naar prullenbak", + "item_deleted_permanently": "Item permanent verwijderd", + "trash_emptied": "Prullenbak succesvol geleegd", + "title": "Notificaties", + "empty": "Geen notificaties", + "link_created": "Link aangemaakt", + "share_success": "Deellink succesvol aangemaakt", + "upload_files_section_title": "Uploaden hier niet beschikbaar", + "upload_files_section_body": "Ga naar de sectie Bestanden om bestanden te uploaden" + }, + "batch": { + "one_selected": "1 item geselecteerd", + "n_selected": "{{count}} items geselecteerd", + "confirm_delete": "Weet je zeker dat je {{count}} items naar de prullenbak wilt verplaatsen?", + "move_title": "Verplaats {{count}} item(s)", + "add_favorites": "Toevoegen aan favorieten", + "move_copy": "Verplaatsen of kopiëren" + }, + "admin": { + "page_title": "Beheerderspaneel", + "back_to_app": "Terug naar OxiCloud", + "loading": "Laden…", + "access_denied": "Toegang geweigerd", + "access_denied_desc": "Beheerdersrechten vereist.", + "sign_in": "Inloggen", + "tab_dashboard": "Dashboard", + "tab_users": "Gebruikers", + "tab_oidc": "SSO / OIDC", + "total_users": "Totaal gebruikers", + "active_users": "Actieve gebruikers", + "admins": "Beheerders", + "version": "Versie", + "storage_overview": "Opslagoverzicht", + "used": "Gebruikt", + "total_quota": "Totaal quotum", + "usage_pct": "Gebruik %", + "users_over_80": "Gebruikers >80% quotum", + "users_over_quota": "Gebruikers boven quotum", + "system": "Systeem", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Quota", + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "active": "Actief", + "off": "Uit", + "allow_registration": "Openbare zelfregistratie toestaan", + "registration_warning": "Openbare registratie is uitgeschakeld. Alleen beheerders kunnen gebruikers aanmaken.", + "user_management": "Gebruikersbeheer", + "create_user": "Gebruiker aanmaken", + "col_user": "Gebruiker", + "col_role": "Rol", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Opslag", + "col_last_login": "Laatste login", + "col_actions": "Acties", + "loading_users": "Gebruikers laden…", + "failed_load_users": "Laden mislukt", + "no_users_found": "Geen gebruikers gevonden", + "showing_users": "Toont {{from}}-{{to}} van {{total}}", + "prev": "Vorige", + "next": "Volgende", + "inactive": "Inactief", + "you_badge": "(jij)", + "local": "Lokaal", + "never": "Nooit", + "just_now": "Zojuist", + "minutes_ago": "{{n}}min geleden", + "hours_ago": "{{n}}u geleden", + "days_ago": "{{n}}d geleden", + "edit_quota_title": "Quotum bewerken", + "reset_password_title": "Wachtwoord resetten", + "toggle_role_title": "Rol wisselen", + "deactivate_title": "Deactiveren", + "activate_title": "Activeren", + "delete_title": "Verwijderen", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "SSO-authenticatie inschakelen", + "provider_name": "Providernaam", + "issuer_url": "Uitgever-URL", + "issuer_url_hint": "OpenID Connect uitgever-URL", + "auto_discover": "Auto-ontdekking", + "discovering": "Ontdekken…", + "client_id": "Client-ID", + "client_secret": "Client-secret", + "client_secret_placeholder": "Laat leeg om huidige waarde te behouden", + "secret_configured": "Een client-secret is al geconfigureerd", + "callback_url": "Callback-URL", + "callback_url_hint": "(registreer bij uw IdP)", + "advanced_settings": "Geavanceerde instellingen", + "scopes": "Scopes", + "auto_provision": "Gebruikers automatisch aanmaken bij eerste login", + "admin_groups": "Beheergroepen", + "admin_groups_hint": "Kommagescheiden OIDC-groepsnamen", + "disable_password": "Wachtwoord-login uitschakelen (alleen OIDC)", + "password_warning": "Dit voorkomt ALLE logins op basis van wachtwoord!", + "test_btn": "Testen", + "save_btn": "Opslaan", + "saving": "Opslaan…", + "settings_saved": "Instellingen opgeslagen — OIDC is nu {{status}}", + "quota_modal_title": "Opslagquotum bijwerken", + "quota_user_label": "Gebruiker:", + "new_quota": "Nieuw quotum", + "quota_unlimited_hint": "0 voor onbeperkt", + "cancel": "Annuleren", + "create_user_title": "Nieuwe gebruiker aanmaken", + "username_label": "Gebruikersnaam", + "username_placeholder": "jandevries", + "username_hint": "3–32 tekens", + "password_label": "Wachtwoord", + "password_placeholder": "Min 8 tekens", + "email_label": "E-mail", + "email_optional": "(optioneel)", + "email_placeholder": "gebruiker@voorbeeld.nl (automatisch indien leeg)", + "role_label": "Rol", + "role_user": "Gebruiker", + "role_admin": "Beheerder", + "quota_label": "Quotum", + "creating": "Aanmaken…", + "reset_pw_title": "Wachtwoord resetten", + "new_password_label": "Nieuw wachtwoord", + "resetting": "Resetten…", + "reset_btn": "Resetten", + "confirm_role_change": "Rol wijzigen naar {{role}}?", + "confirm_deactivate": "Weet u zeker dat u deze gebruiker wilt deactiveren?", + "confirm_activate": "Weet u zeker dat u deze gebruiker wilt activeren?", + "confirm_delete_user": "Gebruiker \"{{name}}\" VERWIJDEREN? Kan niet ongedaan worden gemaakt!", + "confirm_action": "Actie bevestigen", + "confirm_yes": "Bevestigen", + "confirm_no": "Annuleren", + "error_username_short": "Gebruikersnaam moet minimaal 3 tekens bevatten", + "error_password_short": "Wachtwoord moet minimaal 8 tekens bevatten", + "error_generic": "Mislukt", + "error_network": "Netwerkfout: {{message}}", + "error_create_user": "Kan gebruiker niet aanmaken", + "tab_storage": "Opslag", + "storage_title": "Opslagconfiguratie", + "storage_current_backend": "Huidig backend", + "storage_total_blobs": "Totaal blobs", + "storage_total_size": "Totale grootte", + "storage_dedup_ratio": "Deduplicatieverhouding", + "storage_backend": "Backend", + "storage_local": "Lokaal", + "storage_s3": "S3-compatibel", + "storage_provider_preset": "Providerinstelling", + "storage_preset_custom": "Aangepast", + "storage_endpoint_url": "Eindpunt-URL", + "storage_endpoint_hint": "Leeg laten voor AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Regio", + "storage_access_key": "Toegangssleutel", + "storage_secret_key": "Geheime sleutel", + "storage_secret_configured": "Sleutel geconfigureerd", + "storage_key_placeholder": "Nieuwe sleutel invoeren", + "storage_path_style": "Padstijl forceren", + "storage_path_style_hint": "Vereist voor MinIO en sommige S3-compatibele diensten", + "storage_test_connection": "Verbinding testen", + "storage_test_success": "Verbinding geslaagd", + "storage_test_failure": "Verbinding mislukt", + "storage_save": "Configuratie opslaan", + "storage_saved": "Configuratie opgeslagen", + "storage_migration": "Gegevensmigratie", + "storage_migration_coming_soon": "Migratietools binnenkort beschikbaar", + "migration_status_label": "Migratiestatus", + "migration_start": "Migratie starten", + "migration_pause": "Pauzeren", + "migration_resume": "Hervatten", + "migration_verify": "Verifiëren", + "migration_complete": "Voltooien", + "migration_started": "Migratie gestart", + "migration_paused_msg": "Migratie gepauzeerd", + "migration_resumed_msg": "Migratie hervat", + "migration_completed_msg": "Migratie succesvol voltooid", + "migration_verifying": "Bezig met verifiëren...", + "migration_verify_passed": "Verificatie geslaagd", + "migration_verify_failed": "Verificatie mislukt", + "migration_failed_blobs": "Mislukte blobs", + "testing": "Bezig met testen...", + "smtp_disabled": "Uitgeschakeld (host niet ingesteld)", + "smtp_enabled": "Ingeschakeld", + "smtp_enabled_label": "Status", + "smtp_intro": "SMTP wordt uitsluitend geconfigureerd via omgevingsvariabelen (OXICLOUD_SMTP_*). De onderstaande waarden worden gelezen uit de actieve server — om ze te wijzigen, bewerk de omgeving en herstart OxiCloud.", + "smtp_not_configured": "SMTP is niet geconfigureerd op deze server.", + "smtp_send_failed": "Verzenden mislukt.", + "smtp_send_test": "Test-e-mail verzenden", + "smtp_sending": "Bezig met verzenden…", + "smtp_sent": "Test-e-mail verzonden.", + "smtp_server_code": "Serverantwoord", + "smtp_test_intro": "Verzendt een vooraf gedefinieerd diagnostisch bericht naar de onderstaande ontvanger en rapporteert het antwoord van de SMTP-server, zodat je het kunt correleren met je relay-logboeken.", + "smtp_test_missing_to": "Voer een ontvangeradres in.", + "smtp_test_title": "Test-e-mail verzenden", + "smtp_test_to": "Ontvangeradres", + "smtp_title": "Uitgaande e-mail (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Beheerders", + "confirm_role": "Rol wijzigen naar {{role}}?", + "dashboard": "Dashboard", + "email": "E-mail", + "mig_complete": "Voltooien", + "mig_pause": "Pauzeren", + "mig_resume": "Hervatten", + "mig_verify_failed": "Verificatie mislukt", + "mig_verify_passed": "Verificatie geslaagd", + "mig_verifying": "Bezig met verifiëren...", + "oidc_auto_provision": "Gebruikers automatisch aanmaken bij eerste login", + "oidc_callback": "Callback-URL", + "oidc_client_id": "Client-ID", + "oidc_disable_pw": "Wachtwoord-login uitschakelen (alleen OIDC)", + "oidc_issuer": "Uitgever-URL", + "oidc_scopes": "Scopes", + "password": "Wachtwoord", + "quotas": "Quota", + "reset_pw_for": "Nieuw wachtwoord voor", + "role": "Rol", + "smtp_fail": "Verzenden mislukt.", + "smtp_send": "Verzenden", + "smtp_test": "Test-e-mail verzenden", + "smtp_user_state": "Auth", + "status": "Status", + "storage": "Opslag", + "storage_endpoint": "Eindpunt-URL", + "storage_tab": "Opslag", + "time_min_ago": "{{n}} min geleden", + "title": "Beheerder", + "user": "Gebruiker", + "username": "Gebruikersnaam", + "users": "Gebruikers" + }, + "profile": { + "page_title": "Profiel", + "back_to_app": "Terug naar OxiCloud", + "loading": "Laden…", + "not_authenticated": "Niet geauthenticeerd", + "not_authenticated_desc": "Log in om uw profiel te bekijken.", + "sign_in": "Inloggen", + "role_admin": "Beheerder", + "role_user": "Gebruiker", + "account_details": "Accountgegevens", + "username": "Gebruikersnaam", + "email": "E-mail", + "role": "Rol", + "last_login": "Laatste login", + "storage": "Opslag", + "used": "Gebruikt", + "quota": "Quotum", + "usage": "Gebruik", + "unlimited": "Onbeperkt", + "app_passwords": "App-wachtwoorden", + "app_pw_desc": "Genereer wachtwoorden voor WebDAV-, CalDAV- en CardDAV-clients. Elk wachtwoord wordt slechts één keer getoond.", + "app_pw_label_placeholder": "Label (bijv. Thunderbird, macOS)", + "generate": "Genereren", + "generating": "Genereren…", + "new_password_for": "Nieuw wachtwoord voor", + "copy_warning": "Kopieer dit wachtwoord nu. U kunt het niet opnieuw bekijken.", + "copy_to_clipboard": "Kopiëren naar klembord", + "col_label": "Label", + "col_created": "Aangemaakt", + "col_last_used": "Laatst gebruikt", + "col_status": "Status", + "active": "Actief", + "revoked": "Ingetrokken", + "revoke_title": "Intrekken", + "no_app_passwords": "Nog geen app-wachtwoorden.", + "client_sessions": "Clientsessies", + "client_sessions_desc": "Automatisch gegenereerd bij het verbinden van een Nextcloud-compatibele client.", + "col_client": "Client", + "never": "Nooit", + "just_now": "Zojuist", + "minutes_ago": "{{n}} min geleden", + "hours_ago": "{{n}}u geleden", + "days_ago": "{{n}} dagen geleden", + "edit_profile": "Profiel bewerken", + "edit_oidc_managed": "Om uw gegevens (naam, voornaam, profielfoto, …) te wijzigen, werk ze bij bij uw identity provider. De wijzigingen verschijnen bij uw volgende aanmelding.", + "username_claim_hint": "2–64 tekens, letters / cijfers / punt / streepje / underscore. Eenmaal gekozen kan de gebruikersnaam niet meer worden gewijzigd (DAV/NextCloud-clients zijn ervan afhankelijk).", + "username_already_claimed": "Gebruikersnaam ingesteld en niet wijzigbaar (DAV/NextCloud-clients zijn ervan afhankelijk).", + "given_name": "Voornaam", + "family_name": "Achternaam", + "notify_on_share": "Stuur me een e-mail wanneer iemand iets met mij deelt", + "notify_on_share_hint": "Wanneer uitgevinkt, verschijnen gedeelde items nog steeds in je account — je krijgt er alleen geen e-mail over.", + "save_profile": "Wijzigingen opslaan", + "profile_saved": "Profiel bijgewerkt", + "profile_no_changes": "Geen wijzigingen om op te slaan.", + "profile_save_failed": "Opslaan mislukt", + "username_taken_error": "Die gebruikersnaam is al in gebruik.", + "username_immutable_error": "Uw gebruikersnaam is al ingesteld en kan hier niet worden gewijzigd. Neem contact op met een beheerder als u wilt hernoemen.", + "change_password": "Wachtwoord wijzigen", + "current_password": "Huidig wachtwoord", + "new_password": "Nieuw wachtwoord", + "min_8_chars": "Minimaal 8 tekens", + "confirm_password": "Bevestig nieuw wachtwoord", + "update_password": "Wachtwoord bijwerken", + "updating": "Bijwerken…", + "password_updated": "Wachtwoord succesvol bijgewerkt", + "passwords_no_match": "Wachtwoorden komen niet overeen", + "password_too_short": "Wachtwoord moet minimaal 8 tekens bevatten", + "password_change_failed": "Wachtwoord wijzigen mislukt", + "error_network": "Netwerkfout: {{message}}", + "error_label_required": "Voer een label in", + "error_create_pw": "App-wachtwoord aanmaken mislukt", + "confirm_revoke": "App-wachtwoord \"{{label}}\" intrekken? Clients die dit wachtwoord gebruiken zullen stoppen.", + "error_revoke": "Intrekken mislukt", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Wachtwoorden komen niet overeen" + }, + "upload": { + "uploading": "Bezig met uploaden...", + "files": "bestanden", + "complete": "{{count}} / {{total}} geüpload" + }, + "storage_quota_exceeded": "Opslagquotum overschreden", + "sharedwithme": { + "pageTitle": "Gedeeld met mij", + "pageDescription": "Bestanden en mappen die andere gebruikers met u hebben gedeeld", + "emptyStateTitle": "Er is nog niets met u gedeeld", + "emptyStateDesc": "Items die andere gebruikers met u delen, verschijnen hier", + "loadMore": "Meer laden", + "sharedBy": "Gedeeld door", + "colName": "Naam", + "colType": "Type", + "colSharedBy": "Gedeeld door", + "colDate": "Datum gedeeld", + "colPermissions": "Machtigingen" + }, + "groupby": { + "none": "Geen", + "title": "Groeperen op", + "owner": "Eigenaar", + "shareDate": "Deeldatum", + "type": "Type", + "type.folders": "Mappen", + "accessedAt": "Toegangsdatum", + "modifiedAt": "Wijzigingsdatum", + "createdAt": "Aanmaakdatum", + "size": "Grootte", + "favoriteDate": "Favoritendatum", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nieuw", + "folders": "Mappen" + }, + "dateBucket": { + "today": "Vandaag", + "last7days": "Afgelopen 7 dagen", + "last30days": "Afgelopen 30 dagen", + "unknown": "Onbekend" + }, + "groups": { + "title": "Groepen beheren", + "create_button": "Groep maken", + "create_dialog_title": "Nieuwe groep", + "edit_dialog_title": "Groep hernoemen", + "name_label": "Naam", + "name_placeholder": "engineering", + "description_label": "Beschrijving (optioneel)", + "members_section": "Leden", + "add_member_placeholder": "Een gebruiker of groep toevoegen…", + "no_members": "Nog geen leden.", + "remove_member": "Verwijderen", + "delete_group": "Groep verwijderen", + "delete_confirm": "De groep \"{name}\" verwijderen? Aan deze groep gekoppelde rechten worden ingetrokken.", + "empty_state": "Nog geen groepen.", + "load_more": "Meer laden", + "back_to_list": "Terug", + "loading": "Bezig met laden…", + "virtual_badge": "Systeem", + "member_count_zero": "Geen leden", + "member_count_one": "1 lid", + "member_count_other": "{count} leden", + "delete_confirm_label": "Typ de groepsnaam ter bevestiging:", + "delete_confirm_mismatch": "Typ de groepsnaam exact om te bevestigen.", + "virtual_internal_name": "Intern", + "members_loading": "Leden laden…", + "members_empty": "Geen leden", + "virtual_internal_explanation": "Iedere interne gebruiker op deze server", + "create": "Groep maken", + "empty": "Nog geen groepen.", + "members": "Leden" + }, + "myshares": { + "copyLink": "Link kopiëren", + "deleteLink": "Link verwijderen", + "notifyByEmail": "Per e-mail notificeren", + "notifyFailed": "Notificatie kon niet worden verzonden.", + "notifyGroupMembers": "Groepsleden notificeren", + "notifyRateLimited": "Te veel notificaties voor deze ontvanger — probeer het later opnieuw.", + "removeAccess": "Toegang verwijderen", + "resendInvitation": "Uitnodigingsmail opnieuw verzenden", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Audio", + "code": "Code", + "text": "Tekst" + }, + "common": { + "add": "Toevoegen", + "cancel": "Annuleren", + "clear": "Clear", + "close": "Sluiten", + "confirm": "Bevestigen", + "copy": "Kopiëren", + "create": "Maken", + "delete": "Verwijderen", + "download": "Downloaden", + "load_more": "Meer laden", + "loading": "Laden…", + "next": "Volgende", + "no": "Nee", + "previous": "Vorige", + "remove": "Remove", + "rename": "Hernoemen", + "save": "Opslaan", + "search": "Zoeken", + "yes": "Ja" + }, + "device": { + "continue": "Doorgaan", + "unknown": "Onbekend" + }, + "expiryBucket": { + "expired": "Verlopen", + "noExpiry": "Geen verloop", + "today": "Vandaag", + "tomorrow": "Morgen" + }, + "nextcloud": { + "error_title": "Fout", + "sign_in_with": "Inloggen met {{provider}}" + }, + "search": { + "size_label": "Grootte", + "title": "Zoeken", + "type": { + "audio": "Audio" + }, + "type_label": "Type" + }, + "sizeBucket": { + "folders": "Mappen" + }, + "view": { + "grid": "Rasterweergave", + "list": "Lijstweergave" + } } diff --git a/frontend/static/locales/pl.json b/frontend/static/locales/pl.json index 320e34b8..87d240ff 100644 --- a/frontend/static/locales/pl.json +++ b/frontend/static/locales/pl.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Ten link logowania nie jest już ważny", - "expired_body": "Link mógł wygasnąć lub został już użyty. Możemy wysłać Ci nowy — dotrze do Twojej skrzynki odbiorczej w ciągu kilku sekund.", - "resend_to": "Wyślij nowy link do {{email}}", - "generic_unavailable": "Ten link logowania nie jest już ważny. Mógł zostać już użyty lub wygasł. Poproś o nowy link na stronie logowania.", - "service_unavailable": "Logowanie magic link nie jest włączone na tym serwerze.", - "internal_error": "Coś poszło nie tak podczas logowania. Spróbuj ponownie.", - "resend_failure": "Coś poszło nie tak podczas wysyłania linku. Spróbuj ponownie.", - "cross_browser_title": "Kontynuować logowanie na tym urządzeniu?", - "cross_browser_body": "Otworzyłeś ten link logowania w innej przeglądarce lub urządzeniu niż to, z którego został zażądany.", - "cross_browser_warning": "Jeśli to Ty zażądałeś tego linku, możesz bezpiecznie kontynuować. W przeciwnym razie zamknij tę stronę — kliknięcie Kontynuuj zaloguje kogoś innego na Twoje konto.", - "cross_browser_continue": "Kontynuuj i zaloguj się", - "resend_confirmation_title": "Sprawdź swoją skrzynkę", - "resend_confirmation_body": "Jeśli link logowania należał do aktywnego konta, nowy link właśnie został wysłany. Sprawdź swoją skrzynkę.", - "return_link": "Powrót do OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", - "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz, klikając poniższy link:\n{{link}}\n\nLink działa raz i wygasa za {{ttl_hours}} godzin.\nJeśli nie spodziewałeś się tego zaproszenia, możesz zignorować tę wiadomość.\n\n— OxiCloud" - }, - "login": { - "subject": "Zaloguj się do OxiCloud", - "body": "Cześć,\n\nUżyj poniższego linku, aby zalogować się do OxiCloud. Link działa raz i wygasa za {{ttl_minutes}} minut. Otwórz go na tym samym urządzeniu, z którego został zażądany.\n\n{{link}}\n\nJeśli nie żądałeś tego linku logowania, możesz zignorować tę wiadomość — nie jest wymagane żadne dalsze działanie.\n\n— OxiCloud" - }, - "kind_file": "plik", - "kind_folder": "folder", - "english_fallback_divider": "--- Wersja angielska poniżej ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Ten link logowania nie jest już ważny", + "expired_body": "Link mógł wygasnąć lub został już użyty. Możemy wysłać Ci nowy — dotrze do Twojej skrzynki odbiorczej w ciągu kilku sekund.", + "resend_to": "Wyślij nowy link do {{email}}", + "generic_unavailable": "Ten link logowania nie jest już ważny. Mógł zostać już użyty lub wygasł. Poproś o nowy link na stronie logowania.", + "service_unavailable": "Logowanie magic link nie jest włączone na tym serwerze.", + "internal_error": "Coś poszło nie tak podczas logowania. Spróbuj ponownie.", + "resend_failure": "Coś poszło nie tak podczas wysyłania linku. Spróbuj ponownie.", + "cross_browser_title": "Kontynuować logowanie na tym urządzeniu?", + "cross_browser_body": "Otworzyłeś ten link logowania w innej przeglądarce lub urządzeniu niż to, z którego został zażądany.", + "cross_browser_warning": "Jeśli to Ty zażądałeś tego linku, możesz bezpiecznie kontynuować. W przeciwnym razie zamknij tę stronę — kliknięcie Kontynuuj zaloguje kogoś innego na Twoje konto.", + "cross_browser_continue": "Kontynuuj i zaloguj się", + "resend_confirmation_title": "Sprawdź swoją skrzynkę", + "resend_confirmation_body": "Jeśli link logowania należał do aktywnego konta, nowy link właśnie został wysłany. Sprawdź swoją skrzynkę.", + "return_link": "Powrót do OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", + "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz, klikając poniższy link:\n{{link}}\n\nLink działa raz i wygasa za {{ttl_hours}} godzin.\nJeśli nie spodziewałeś się tego zaproszenia, możesz zignorować tę wiadomość.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", - "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz OxiCloud, aby zobaczyć nowe udostępnienie:\n{{login_link}}\n\nMożesz mieć dodatkowe nowe udostępnienia od {{inviter}} — zaloguj się, aby zobaczyć wszystkie udostępnione Ci elementy.\n\n— OxiCloud\n\nOtrzymujesz tę wiadomość, ponieważ masz konto OxiCloud i preferencja powiadomień o udostępnieniach jest włączona. Możesz ją wyłączyć w swoim profilu (Wyślij mi e-mail, gdy ktoś coś mi udostępni)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalistyczny cloud storage" - }, - "nav": { - "files": "Pliki", - "shared": "Udostępnione", - "recent": "Ostatnie", - "favorites": "Ulubione", - "photos": "Zdjęcia", - "music": "Muzyka", - "trash": "Kosz", - "sharedwithme": "Udostępnione dla mnie" - }, - "photos": { - "empty_state": "Brak zdjęć", - "empty_hint": "Prześlij obrazy lub filmy, aby zobaczyć je tutaj", - "items_selected": "wybrane", - "view_daily": "Dzień", - "view_monthly": "Miesiąc", - "view_yearly": "Rok" - }, - "music": { - "create_playlist": "Utwórz playlistę", - "playlists": "Playlisty", - "no_playlists": "Brak playlist", - "empty_hint": "Utwórz pierwszą playlistę, aby zacząć organizować swoją muzykę", - "select_playlist": "Wybierz playlistę", - "select_hint": "Wybierz playlistę z paska bocznego lub utwórz nową", - "add_tracks": "Dodaj utwory", - "add_to_playlist": "Dodaj do playlisty", - "add": "Dodaj", - "added": "Dodano!", - "added_to_playlist": "dodano do playlisty", - "load_error": "Błąd ładowania playlist", - "add_error": "Nie można dodać utworów do playlisty", - "no_playlists_yet": "Brak playlist. Utwórz pierwszą!", - "selected_files": "Wybrane:", - "no_tracks": "Brak utworów na tej playliście", - "unknown_artist": "Nieznany wykonawca", - "unknown_title": "Nieznany", - "confirm_delete": "Usunąć tę playlistę?", - "playlist_name": "Nazwa playlisty", - "create": "Utwórz", - "delete": "Usuń", - "share": "Udostępnij", - "edit": "Edytuj", - "play_all": "Odtwórz wszystkie", - "shuffle": "Losowo", - "repeat": "Powtarzaj", - "repeat_one": "Powtarzaj jeden", - "queue": "Kolejka", - "queue_empty": "Kolejka jest pusta", - "not_playing": "Nic nie jest odtwarzane", - "play": "Odtwórz", - "pause": "Pauza", - "previous": "Poprzedni", - "next": "Następny", - "volume": "Głośność", - "mute": "Wycisz", - "unmute": "Włącz dźwięk", - "title": "Tytuł", - "artist": "Wykonawca", - "album": "Album", - "tracks": "utwory", - "share_with_user": "ID użytkownika lub e-mail", - "playback_error": "Odtwarzanie nie powiodło się", - "error": "Błąd", - "remove": "Usuń", - "track_removed": "Utwór usunięty", - "manage_shares": "Zarządzaj udostępnieniami", - "no_shares": "Brak udostępnień", - "remove_share": "Usuń udostępnienie", - "can_write": "Może edytować", - "read_only": "Tylko do odczytu", - "public": "Publiczny", - "private": "Prywatny", - "toggle_public": "Widoczność", - "make_public": "Ustaw jako publiczny", - "make_private": "Ustaw jako prywatny", - "set_cover": "Ustaw okładkę", - "cover_updated": "Okładka zaktualizowana", - "search_audio": "Szukaj plików audio…", - "no_audio_files": "Nie znaleziono plików audio", - "selected": "wybrane", - "loading": "Ładowanie…", - "search_error": "Nie można załadować plików audio", - "adding": "Dodawanie…" - }, - "actions": { - "search": "Szukaj plików...", - "new_folder": "Nowy folder", - "upload": "Prześlij", - "upload_files": "Prześlij pliki", - "upload_folder": "Prześlij folder", - "upload.uploading": "Przesyłanie...", - "upload.complete": "{count} / {total} przesłano", - "upload.files": "plików", - "rename": "Zmień nazwę", - "move": "Przenieś do...", - "move_to": "Przenieś do", - "delete": "Usuń", - "download": "Pobierz", - "view": "Pokaż", - "cancel": "Anuluj", - "confirm": "Potwierdź", - "share": "Udostępnij", - "favorite": "Dodaj do ulubionych", - "unfavorite": "Usuń z ulubionych", - "copy": "Kopiuj", - "notify": "Powiadom", - "send": "Wyślij", - "clear_recent": "Wyczyść ostatnie", - "logout": "Wyloguj się", - "create": "Utwórz", - "search_btn": "Szukaj", - "close": "Zamknij", - "delete_permanently": "Usuń trwale", - "empty_trash": "Opróżnij kosz", - "open_parent_folder": "Przejdź do folderu nadrzędnego", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Wygląd", - "about": "O OxiCloud", - "about_description": "Platforma pamięci masowej w chmurze zbudowana w oparciu o Rust & Clean Architecture. Szybka, bezpieczna i prywatna.", - "admin_panel": "Panel administratora", - "profile": "Mój profil", - "role_user": "Użytkownik", - "theme": { - "light": "Jasny", - "dark": "Ciemny", - "auto": "Jak system" + "login": { + "subject": "Zaloguj się do OxiCloud", + "body": "Cześć,\n\nUżyj poniższego linku, aby zalogować się do OxiCloud. Link działa raz i wygasa za {{ttl_minutes}} minut. Otwórz go na tym samym urządzeniu, z którego został zażądany.\n\n{{link}}\n\nJeśli nie żądałeś tego linku logowania, możesz zignorować tę wiadomość — nie jest wymagane żadne dalsze działanie.\n\n— OxiCloud" }, - "manage_groups": "Zarządzaj grupami" + "kind_file": "plik", + "kind_folder": "folder", + "english_fallback_divider": "--- Wersja angielska poniżej ---" + } }, - "share": { - "dialogTitle": "Link udostępniania", - "linkLabel": "Link udostępniania:", - "copyLink": "Kopiuj", - "permissions": "Uprawnienia:", - "permissionRead": "Odczyt", - "permissionWrite": "Zapis", - "permissionReshare": "Dalsze udostępnianie", - "password": "Ochrona hasłem:", - "generatePassword": "Wygeneruj", - "expiration": "Data wygaśnięcia:", - "update": "Zaktualizuj udostępnienie", - "remove": "Usuń udostępnienie", - "notifyTitle": "Wyślij powiadomienie", - "notifyEmailLabel": "Adres e-mail:", - "notifyMessageLabel": "Wiadomość (opcjonalnie):", - "notifySend": "Wyślij powiadomienie", - "shareWithOthers": "Udostępnij innym", - "sharePublicly": "Udostępnij publicznie", - "shareSettings": "Ustawienia udostępniania", - "shareCopied": "Link skopiowany do schowka", - "shareCreated": "Link udostępniania utworzony pomyślnie", - "shareUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", - "shareRemoved": "Udostępnienie usunięte pomyślnie", - "inviteByEmail": "Zaproś przez e-mail — zaproszenie zostanie wysłane", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link udostępniania", - "share_linkLabel": "Link udostępniania:", - "share_copyLink": "Kopiuj", - "share_permissions": "Uprawnienia:", - "share_permissionRead": "Odczyt", - "share_permissionWrite": "Zapis", - "share_permissionReshare": "Dalsze udostępnianie", - "share_password": "Ochrona hasłem:", - "share_generatePassword": "Wygeneruj", - "share_expiration": "Data wygaśnięcia:", - "share_update": "Zaktualizuj udostępnienie", - "share_remove": "Usuń udostępnienie", - "share_notifyTitle": "Wyślij powiadomienie", - "share_notifyEmailLabel": "Adres e-mail:", - "share_notifyMessageLabel": "Wiadomość (opcjonalnie):", - "share_notifySend": "Wyślij powiadomienie", - "shared": { - "backToFiles": "Powrót do plików", - "pageTitle": "Udostępnione zasoby", - "pageDescription": "Zarządzaj udostępnionymi plikami i folderami", - "filterType": "Typ:", - "filterAll": "Wszystkie", - "filterFiles": "Pliki", - "filterFolders": "Foldery", - "sortBy": "Sortuj według:", - "sortByName": "Nazwa", - "sortByDate": "Data udostępnienia", - "sortByExpiration": "Wygaśnięcie", - "search": "Szukaj", - "colName": "Nazwa", - "colType": "Typ", - "colDateShared": "Data udostępnienia", - "colExpiration": "Wygaśnięcie", - "colPermissions": "Uprawnienia", - "colPassword": "Hasło", - "colActions": "Akcje", - "emptyStateTitle": "Brak udostępnionych zasobów", - "emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", - "goToFiles": "Przejdź do plików", - "typeFile": "Plik", - "typeFolder": "Folder", - "noExpiration": "Bez wygaśnięcia", - "hasPassword": "Tak", - "noPassword": "Nie", - "editShare": "Edytuj udostępnienie", - "notifyShare": "Powiadom kogoś", - "copyLink": "Kopiuj link", - "removeShare": "Usuń udostępnienie", - "linkCopied": "Link skopiowany do schowka!", - "linkCopyFailed": "Nie udało się skopiować linku", - "itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", - "itemRemoved": "Udostępnienie usunięte pomyślnie", - "invalidEmail": "Wprowadź prawidłowy adres e-mail", - "notificationSent": "Powiadomienie wysłane pomyślnie", - "notificationFailed": "Nie udało się wysłać powiadomienia", - "shared_backToFiles": "Powrót do plików", - "shared_pageTitle": "Udostępnione zasoby", - "shared_pageDescription": "Zarządzaj udostępnionymi plikami i folderami", - "shared_filterType": "Typ:", - "shared_filterAll": "Wszystkie", - "shared_filterFiles": "Pliki", - "shared_filterFolders": "Foldery", - "shared_sortBy": "Sortuj według:", - "shared_sortByName": "Nazwa", - "shared_sortByDate": "Data udostępnienia", - "shared_sortByExpiration": "Wygaśnięcie", - "shared_search": "Szukaj", - "shared_colName": "Nazwa", - "shared_colType": "Typ", - "shared_colDateShared": "Data udostępnienia", - "shared_colExpiration": "Wygaśnięcie", - "shared_colPermissions": "Uprawnienia", - "shared_colPassword": "Hasło", - "shared_colActions": "Akcje", - "shared_emptyStateTitle": "Brak udostępnionych zasobów", - "shared_emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", - "shared_goToFiles": "Przejdź do plików", - "shared_typeFile": "Plik", - "shared_typeFolder": "Folder", - "shared_noExpiration": "Bez wygaśnięcia", - "shared_hasPassword": "Tak", - "shared_noPassword": "Nie", - "shared_editShare": "Edytuj udostępnienie", - "shared_notifyShare": "Powiadom kogoś", - "shared_copyLink": "Kopiuj link", - "shared_removeShare": "Usuń udostępnienie", - "shared_linkCopied": "Link skopiowany do schowka!", - "shared_linkCopyFailed": "Nie udało się skopiować linku", - "shared_itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", - "shared_itemRemoved": "Udostępnienie usunięte pomyślnie", - "shared_invalidEmail": "Wprowadź prawidłowy adres e-mail", - "shared_notificationSent": "Powiadomienie wysłane pomyślnie", - "shared_notificationFailed": "Nie udało się wysłać powiadomienia" - }, - "files": { - "name": "Nazwa", - "type": "Typ", - "size": "Rozmiar", - "modified": "Zmodyfikowano", - "no_files": "Brak plików w tym folderze", - "empty_hint": "Prześlij pliki lub utwórz foldery, aby rozpocząć", - "loading": "Ładowanie plików…", - "view_grid": "Widok siatki", - "view_list": "Widok listy", - "file_types": { - "document": "Dokument", - "image": "Obraz", - "video": "Wideo", - "audio": "Audio", - "pdf": "PDF", - "text": "Tekst", - "folder": "Folder", - "spreadsheet": "Arkusz kalkulacyjny", - "presentation": "Prezentacja", - "archive": "Archiwum", - "installer": "Instalator", - "code": "Kod" - }, - "owner": "Właściciel" - }, - "dialogs": { - "rename_folder": "Zmień nazwę folderu", - "rename_file": "Zmień nazwę pliku", - "new_name": "Nowa nazwa", - "new_folder_title": "Nowy folder", - "folder_name": "Nazwa folderu", - "folder_placeholder": "Mój folder", - "rename_title": "Zmień nazwę", - "move_file": "Przenieś plik", - "move_folder": "Przenieś folder", - "select_destination": "Wybierz folder docelowy:", - "select_this_folder": "Wybierz ten folder", - "go_to_parent": ".. (folder nadrzędny)", - "no_subfolders": "Brak podfolderów", - "root": "Główny", - "delete_confirmation": "Czy na pewno chcesz usunąć", - "and_contents": "i całą jego zawartość", - "no_undo": "Tej akcji nie można cofnąć", - "confirm_title": "Potwierdź akcję", - "confirm_delete": "Przenieś do kosza", - "confirm_delete_file": "Czy na pewno chcesz przenieść plik \"{{name}}\" do kosza?", - "confirm_delete_folder": "Czy na pewno chcesz przenieść folder \"{{name}}\" i całą jego zawartość do kosza?", - "confirm_permanent_delete": "Usuń trwale", - "confirm_permanent_delete_msg": "Czy na pewno chcesz trwale usunąć ten element? Tej akcji nie można cofnąć.", - "confirm_empty_trash": "Opróżnij kosz", - "confirm_delete_share": "Usuń link udostępniania", - "confirm_delete_share_msg": "Czy na pewno chcesz usunąć ten link udostępniania?", - "share_file": "Udostępnij plik", - "share_folder": "Udostępnij folder", - "existing_shares": "Istniejące udostępnienia", - "share_options": "Opcje udostępniania", - "password": "Hasło", - "expiration": "Wygaśnięcie", - "permissions": "Uprawnienia", - "generated_link": "Wygenerowany link", - "notify": "Wyślij powiadomienie", - "recipient": "Odbiorca", - "message": "Wiadomość", - "move_to_home": "Przenieś do folderu domowego" - }, - "dropzone": { - "drag_files": "Przeciągnij pliki tutaj lub kliknij, aby wybrać", - "drop_files": "Upuść pliki, aby przesłać" - }, - "permissions": { - "read": "Odczyt", - "write": "Zapis", - "reshare": "Dalsze udostępnianie" - }, - "errors": { - "file_not_found": "Plik nie został znaleziony", - "folder_not_found": "Folder nie został znaleziony", - "delete_error": "Błąd podczas usuwania", - "upload_error": "Błąd podczas przesyłania pliku", - "rename_error": "Błąd podczas zmiany nazwy", - "move_error": "Błąd podczas przenoszenia", - "empty_name": "Nazwa nie może być pusta", - "name_exists": "Plik lub folder o tej nazwie już istnieje", - "generic_error": "Wystąpił błąd", - "group_name_invalid": "Nazwa grupy musi spełniać format prefiksu e-mail (litery, cyfry, kropka, myślnik, podkreślnik; 1–64 znaków).", - "group_cycle": "Ten członek utworzyłby cykliczne odwołanie między grupami.", - "group_depth_exceeded": "Ta głębokość zagnieżdżenia przekracza maksymalną dozwoloną (8).", - "group_virtual_immutable": "Grupa „Internal” jest zarządzana przez system i nie może być modyfikowana.", - "group_not_found": "Grupa nie znaleziona.", - "group_name_taken": "Grupa o tej nazwie już istnieje." - }, - "breadcrumb": { - "home": "Strona główna" - }, - "trash": { - "empty_trash": "Opróżnij kosz", - "empty_state": "Kosz jest pusty", - "original_location": "Pierwotna lokalizacja", - "deleted_date": "Data usunięcia", - "remaining": "Pozostało", - "actions": "Akcje", - "restore": "Przywróć", - "delete_permanently": "Usuń trwale", - "empty_confirm": "Czy na pewno chcesz opróżnić kosz? Wszystkie elementy zostaną trwale usunięte.", - "groupby": { - "remaining_days": "Pozostałe dni", - "trashed_time": "Czas usunięcia" - } - }, - "daysRemaining": { - "expired": "Wygasł", - "today": "Dziś", - "tomorrow": "Jutro", - "inDays": "{{count}} dni" - }, - "expiryChip": { - "never": "Nigdy nie wygasa", - "expired": "Wygasł", - "today": "Wygasa dziś", - "tomorrow": "Wygasa jutro", - "inDays": "Wygasa za {{count}} dni", - "onDate": "Wygasa {{date}}" - }, - "auth": { - "login_title": "Zaloguj się", - "username": "Nazwa użytkownika", - "username_placeholder": "Wprowadź nazwę użytkownika", - "login_identifier": "Nazwa użytkownika lub e-mail", - "login_identifier_placeholder": "Wpisz nazwę użytkownika lub e-mail", - "password": "Hasło", - "password_placeholder": "Wprowadź hasło", - "login_button": "Zaloguj się", - "no_account": "Nie masz konta?", - "register": "Zarejestruj się", - "admin_setup": "Pierwszy raz?", - "setup": "Konfiguracja administratora", - "register_title": "Utwórz konto", - "email": "E-mail", - "email_placeholder": "Wprowadź adres e-mail", - "confirm_password": "Potwierdź hasło", - "confirm_password_placeholder": "Potwierdź hasło", - "register_button": "Utwórz konto", - "have_account": "Masz już konto?", - "login": "Zaloguj się", - "setup_title": "Konfiguracja początkowa", - "setup_step1": "Administrator", - "setup_step2": "System", - "setup_step3": "Gotowe", - "admin_username": "Nazwa administratora", - "admin_email": "E-mail administratora", - "admin_password": "Hasło administratora", - "create_admin": "Utwórz administratora", - "back_to_login": "Już skonfigurowane?", - "admin_success": "Konto administratora zostało utworzone! Możesz się teraz zalogować.", - "account_success": "Konto utworzone pomyślnie! Możesz się teraz zalogować.", - "passwords_mismatch": "Hasła nie są zgodne", - "admin_create_error": "Błąd podczas tworzenia konta administratora", - "or": "lub", - "sso_login": "Zaloguj się przez SSO", - "sso_login_provider": "Zaloguj się przez {{provider}}", - "magicLinkHint": "Brak hasła? Wpisz swój adres e-mail, a wyślemy Ci jednorazowy link do logowania.", - "magicLinkEmailLabel": "Adres e-mail", - "magicLinkEmailPlaceholder": "ty@przyklad.pl", - "magicLinkSubmit": "Wyślij link do logowania", - "magicLinkSent": "Jeśli konto dla tego adresu istnieje, link do logowania został wysłany. Sprawdź swoją skrzynkę odbiorczą.", - "magicLinkUnavailable": "Logowanie e-mailem nie jest dostępne na tym serwerze.", - "magicLinkNetworkError": "Nie udało się połączyć z serwerem: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Pamięć masowa", - "calculating": "Obliczanie...", - "used": "{{percentage}}% wykorzystane ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Tego typu pliku nie można wyświetlić.", - "download_file": "Pobierz plik", - "zoom_in": "Przybliż", - "zoom_out": "Oddal", - "zoom_reset": "Resetuj powiększenie" - }, - "language_selector": { - "title": "Witaj!", - "subtitle": "Wybierz język, aby kontynuować", - "continue": "Kontynuuj", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Brak ulubionych", - "empty_hint": "Oznacz pliki lub foldery gwiazdką, aby dodać je do ulubionych", - "add": "Dodaj do ulubionych", - "remove": "Usuń z ulubionych", - "added_title": "Dodano do ulubionych", - "added_msg": "dodano do ulubionych", - "removed_title": "Usunięto z ulubionych", - "removed_msg": "usunięto z ulubionych" - }, - "recent": { - "title": "Ostatnie", - "clear": "Wyczyść ostatnie", - "accessed": "Otwarte", - "empty_state": "Brak ostatnich plików", - "empty_hint": "Otwarte pliki pojawią się tutaj", - "loadMore": "Załaduj więcej" - }, - "notifications": { - "file_renamed": "Zmieniono nazwę pliku", - "file_renamed_to": "Nazwa pliku zmieniona na \"{{name}}\"", - "folder_renamed": "Zmieniono nazwę folderu", - "folder_renamed_to": "Nazwa folderu zmieniona na \"{{name}}\"", - "file_uploaded": "Plik przesłany", - "file_deleted": "Plik przeniesiony do kosza", - "folder_deleted": "Folder przeniesiony do kosza", - "item_deleted_permanently": "Element trwale usunięty", - "trash_emptied": "Kosz został opróżniony", - "title": "Powiadomienia", - "empty": "Brak powiadomień", - "link_created": "Link utworzony", - "share_success": "Link udostępniania utworzony pomyślnie", - "upload_files_section_title": "Przesyłanie niedostępne tutaj", - "upload_files_section_body": "Przejdź do sekcji Pliki, aby przesłać pliki" - }, - "batch": { - "one_selected": "Wybrano 1 element", - "n_selected": "Wybrano {{count}} elementów", - "confirm_delete": "Czy na pewno chcesz przenieść {{count}} elementów do kosza?", - "move_title": "Przenieś {{count}} element(ów)", - "add_favorites": "Dodaj do ulubionych", - "move_copy": "Przenieś lub kopiuj" - }, - "admin": { - "page_title": "Panel administratora", - "back_to_app": "Powrót do OxiCloud", - "loading": "Ładowanie…", - "access_denied": "Dostęp zabroniony", - "access_denied_desc": "Aby uzyskać dostęp do tego panelu, wymagane są uprawnienia administratora.", - "sign_in": "Zaloguj się", - "tab_dashboard": "Panel", - "tab_users": "Użytkownicy", - "tab_oidc": "SSO / OIDC", - "total_users": "Wszyscy użytkownicy", - "active_users": "Aktywni użytkownicy", - "admins": "Administratorzy", - "version": "Wersja", - "storage_overview": "Przegląd pamięci masowej", - "used": "Wykorzystane", - "total_quota": "Łączny przydział", - "usage_pct": "Wykorzystanie %", - "users_over_80": "Użytkownicy >80% przydziału", - "users_over_quota": "Użytkownicy powyżej przydziału", - "system": "System", - "auth_label": "Uwierzytelnianie", - "oidc_label": "OIDC", - "quotas_label": "Przydziały", - "enabled": "Włączone", - "disabled": "Wyłączone", - "active": "Aktywne", - "off": "Wyłączone", - "allow_registration": "Zezwalaj na publiczną samodzielną rejestrację", - "registration_warning": "Publiczna rejestracja jest wyłączona. Tylko administratorzy mogą tworzyć nowych użytkowników.", - "user_management": "Zarządzanie użytkownikami", - "create_user": "Utwórz użytkownika", - "col_user": "Użytkownik", - "col_role": "Rola", - "col_auth": "Uwierzytelnianie", - "col_status": "Status", - "col_storage": "Pamięć masowa", - "col_last_login": "Ostatnie logowanie", - "col_actions": "Akcje", - "loading_users": "Ładowanie użytkowników…", - "failed_load_users": "Nie udało się załadować użytkowników", - "no_users_found": "Nie znaleziono użytkowników", - "showing_users": "Wyświetlanie {{from}}-{{to}} z {{total}}", - "prev": "Poprzednia", - "next": "Następna", - "inactive": "Nieaktywny", - "you_badge": "(ty)", - "local": "Lokalny", - "never": "Nigdy", - "just_now": "Przed chwilą", - "minutes_ago": "{{n}} min temu", - "hours_ago": "{{n}} godz. temu", - "days_ago": "{{n}} dni temu", - "edit_quota_title": "Edytuj przydział", - "reset_password_title": "Resetuj hasło", - "toggle_role_title": "Przełącz rolę", - "deactivate_title": "Dezaktywuj", - "activate_title": "Aktywuj", - "delete_title": "Usuń", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "Włącz uwierzytelnianie SSO", - "provider_name": "Nazwa dostawcy", - "issuer_url": "URL wystawcy", - "issuer_url_hint": "URL wystawcy OpenID Connect Twojego dostawcy tożsamości", - "auto_discover": "Automatyczne wykrywanie", - "discovering": "Wykrywanie…", - "client_id": "ID klienta", - "client_secret": "Sekret klienta", - "client_secret_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", - "secret_configured": "Sekret klienta jest już skonfigurowany", - "callback_url": "URL zwrotny", - "callback_url_hint": "(zarejestruj w swoim IdP)", - "advanced_settings": "Ustawienia zaawansowane", - "scopes": "Zakresy", - "auto_provision": "Automatycznie twórz użytkowników przy pierwszym logowaniu", - "admin_groups": "Grupy administratorów", - "admin_groups_hint": "Lista nazw grup OIDC oddzielonych przecinkami, mapowanych na rolę administratora", - "disable_password": "Wyłącz logowanie hasłem (tylko OIDC)", - "password_warning": "To uniemożliwi WSZYSTKIE logowania hasłem!", - "test_btn": "Testuj", - "save_btn": "Zapisz", - "saving": "Zapisywanie…", - "settings_saved": "Ustawienia zapisane — OIDC jest teraz {{status}}", - "quota_modal_title": "Aktualizuj przydział pamięci masowej", - "quota_user_label": "Użytkownik:", - "new_quota": "Nowy przydział", - "quota_unlimited_hint": "Ustaw 0 dla nieograniczonego", - "cancel": "Anuluj", - "create_user_title": "Utwórz nowego użytkownika", - "username_label": "Nazwa użytkownika", - "username_placeholder": "jankowalski", - "username_hint": "3–32 znaków", - "password_label": "Hasło", - "password_placeholder": "Min. 8 znaków", - "email_label": "E-mail", - "email_optional": "(opcjonalnie)", - "email_placeholder": "uzytkownik@example.com (generowany automatycznie, jeśli puste)", - "role_label": "Rola", - "role_user": "Użytkownik", - "role_admin": "Administrator", - "quota_label": "Przydział", - "creating": "Tworzenie…", - "reset_pw_title": "Resetuj hasło", - "new_password_label": "Nowe hasło", - "resetting": "Resetowanie…", - "reset_btn": "Resetuj", - "confirm_role_change": "Zmienić rolę na {{role}}?", - "confirm_deactivate": "Czy na pewno chcesz dezaktywować tego użytkownika?", - "confirm_activate": "Czy na pewno chcesz aktywować tego użytkownika?", - "confirm_delete_user": "USUNĄĆ użytkownika \"{{name}}\"? Tej akcji nie można cofnąć!", - "confirm_action": "Potwierdź akcję", - "confirm_yes": "Potwierdź", - "confirm_no": "Anuluj", - "error_username_short": "Nazwa użytkownika musi mieć co najmniej 3 znaki", - "error_password_short": "Hasło musi mieć co najmniej 8 znaków", - "error_generic": "Niepowodzenie", - "error_network": "Błąd sieci: {{message}}", - "error_create_user": "Nie udało się utworzyć użytkownika", - "tab_storage": "Pamięć masowa", - "storage_title": "Backend pamięci masowej", - "storage_current_backend": "Aktywny backend", - "storage_total_blobs": "Liczba blobów", - "storage_total_size": "Łączny rozmiar", - "storage_dedup_ratio": "Współczynnik deduplikacji", - "storage_backend": "Typ backendu", - "storage_local": "Lokalny system plików", - "storage_s3": "Kompatybilny z S3", - "storage_provider_preset": "Ustawienia dostawcy", - "storage_preset_custom": "Niestandardowy", - "storage_endpoint_url": "URL endpointu", - "storage_endpoint_hint": "Pozostaw puste dla domyślnego Amazon S3", - "storage_bucket": "Bucket", - "storage_region": "Region", - "storage_access_key": "Access Key ID", - "storage_secret_key": "Secret Access Key", - "storage_secret_configured": "Klucz tajny jest już skonfigurowany", - "storage_key_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", - "storage_path_style": "Wymuś styl ścieżki", - "storage_path_style_hint": "Wymagane dla MinIO i niektórych dostawców kompatybilnych z S3", - "storage_test_connection": "Testuj połączenie", - "storage_test_success": "Połączenie udane", - "storage_test_failure": "Połączenie nieudane", - "storage_save": "Zapisz", - "storage_saved": "Ustawienia pamięci masowej zapisane pomyślnie", - "storage_migration": "Migracja backendu", - "storage_migration_coming_soon": "Migracja backendu będzie dostępna w przyszłej aktualizacji.", - "migration_status_label": "Status:", - "migration_start": "Rozpocznij migrację", - "migration_pause": "Wstrzymaj", - "migration_resume": "Wznów", - "migration_verify": "Sprawdź integralność", - "migration_complete": "Sfinalizuj", - "migration_started": "Migracja rozpoczęta", - "migration_paused_msg": "Migracja wstrzymana", - "migration_resumed_msg": "Migracja wznowiona", - "migration_completed_msg": "Migracja sfinalizowana. Uruchom ponownie serwer, aby użyć nowego backendu.", - "migration_verifying": "Weryfikowanie…", - "migration_verify_passed": "Weryfikacja zaliczona", - "migration_verify_failed": "Weryfikacja nieudana", - "migration_failed_blobs": "nieudane bloby", - "testing": "Testowanie…", - "smtp_disabled": "Wyłączone (host nieustawiony)", - "smtp_enabled": "Włączone", - "smtp_enabled_label": "Status", - "smtp_intro": "SMTP jest konfigurowany wyłącznie przez zmienne środowiskowe (OXICLOUD_SMTP_*). Poniższe wartości są odczytywane z działającego serwera — aby je zmienić, zmodyfikuj środowisko i uruchom ponownie OxiCloud.", - "smtp_not_configured": "SMTP nie jest skonfigurowany na tym serwerze.", - "smtp_send_failed": "Wysłanie nie powiodło się.", - "smtp_send_test": "Wyślij e-mail testowy", - "smtp_sending": "Wysyłanie…", - "smtp_sent": "E-mail testowy wysłany.", - "smtp_server_code": "Odpowiedź serwera", - "smtp_test_intro": "Wysyła wstępnie zdefiniowaną wiadomość diagnostyczną do podanego poniżej odbiorcy i raportuje odpowiedź serwera SMTP, abyś mógł skorelować ją z logami swojego przekaźnika.", - "smtp_test_missing_to": "Wprowadź adres odbiorcy.", - "smtp_test_title": "Wyślij e-mail testowy", - "smtp_test_to": "Adres odbiorcy", - "smtp_title": "Poczta wychodząca (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profil", - "back_to_app": "Powrót do OxiCloud", - "loading": "Ładowanie…", - "not_authenticated": "Nieuwierzytelniony", - "not_authenticated_desc": "Zaloguj się, aby zobaczyć swój profil.", - "sign_in": "Zaloguj się", - "role_admin": "Administrator", - "role_user": "Użytkownik", - "account_details": "Szczegóły konta", - "username": "Nazwa użytkownika", - "email": "E-mail", - "role": "Rola", - "last_login": "Ostatnie logowanie", - "storage": "Pamięć masowa", - "used": "Wykorzystane", - "quota": "Przydział", - "usage": "Wykorzystanie", - "unlimited": "Nieograniczone", - "app_passwords": "Hasła aplikacji", - "app_pw_desc": "Generuj hasła dla klientów WebDAV, CalDAV i CardDAV. Każde hasło jest wyświetlane tylko raz.", - "app_pw_label_placeholder": "Etykieta (np. Thunderbird, macOS)", - "generate": "Wygeneruj", - "generating": "Generowanie…", - "new_password_for": "Nowe hasło dla", - "copy_warning": "Skopiuj to hasło teraz. Nie zobaczysz go ponownie.", - "copy_to_clipboard": "Kopiuj do schowka", - "col_label": "Etykieta", - "col_created": "Utworzone", - "col_last_used": "Ostatnio używane", - "col_status": "Status", - "active": "Aktywne", - "revoked": "Unieważnione", - "revoke_title": "Unieważnij", - "no_app_passwords": "Brak haseł aplikacji.", - "client_sessions": "Sesje klientów", - "client_sessions_desc": "Generowane automatycznie po połączeniu z klientem kompatybilnym z Nextcloud.", - "col_client": "Klient", - "never": "Nigdy", - "just_now": "Przed chwilą", - "minutes_ago": "{{n}} min temu", - "hours_ago": "{{n}} godz. temu", - "days_ago": "{{n}} dni temu", - "edit_profile": "Edytuj profil", - "edit_oidc_managed": "Aby zmienić swoje dane (nazwisko, imię, zdjęcie profilowe, …), zaktualizuj je u swojego dostawcy tożsamości. Twoje zmiany pojawią się przy następnym logowaniu.", - "username_claim_hint": "2–64 znaki, litery / cyfry / kropka / myślnik / podkreślenie. Po wybraniu nazwy użytkownika nie można jej zmienić (klienty DAV/NextCloud są od niej zależne).", - "username_already_claimed": "Nazwa użytkownika jest ustawiona i nie może być zmieniona (klienty DAV/NextCloud są od niej zależne).", - "given_name": "Imię", - "family_name": "Nazwisko", - "notify_on_share": "Wyślij mi e-mail, gdy ktoś coś mi udostępni", - "notify_on_share_hint": "Gdy odznaczone, udostępnienia nadal pojawiają się na Twoim koncie — po prostu nie otrzymasz o nich e-maila.", - "save_profile": "Zapisz zmiany", - "profile_saved": "Profil zaktualizowany", - "profile_no_changes": "Brak zmian do zapisania.", - "profile_save_failed": "Zapis nie powiódł się", - "username_taken_error": "Ta nazwa użytkownika jest już zajęta.", - "username_immutable_error": "Twoja nazwa użytkownika jest już ustawiona i nie można jej tutaj zmienić. Skontaktuj się z administratorem, jeśli chcesz ją zmienić.", - "change_password": "Zmień hasło", - "current_password": "Bieżące hasło", - "new_password": "Nowe hasło", - "min_8_chars": "Co najmniej 8 znaków", - "confirm_password": "Potwierdź nowe hasło", - "update_password": "Aktualizuj hasło", - "updating": "Aktualizowanie…", - "password_updated": "Hasło zaktualizowane pomyślnie", - "passwords_no_match": "Hasła nie są zgodne", - "password_too_short": "Hasło musi mieć co najmniej 8 znaków", - "password_change_failed": "Nie udało się zmienić hasła", - "error_network": "Błąd sieci: {{message}}", - "error_label_required": "Wprowadź etykietę", - "error_create_pw": "Nie udało się utworzyć hasła aplikacji", - "confirm_revoke": "Unieważnić hasło aplikacji \"{{label}}\"? Klienci używający tego hasła przestaną działać.", - "error_revoke": "Nie udało się unieważnić hasła aplikacji", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Przesyłanie...", - "files": "plików", - "complete": "{{count}} / {{total}} przesłano" - }, - "storage_quota_exceeded": "Przekroczono limit pamięci masowej", - "sharedwithme": { - "pageTitle": "Udostępnione dla mnie", - "pageDescription": "Pliki i foldery, które inni użytkownicy udostępnili Ci", - "emptyStateTitle": "Nic nie zostało Ci jeszcze udostępnione", - "emptyStateDesc": "Elementy udostępnione Ci przez innych użytkowników pojawią się tutaj", - "loadMore": "Załaduj więcej", - "sharedBy": "Udostępnione przez", - "colName": "Nazwa", - "colType": "Typ", - "colSharedBy": "Udostępnione przez", - "colDate": "Data udostępnienia", - "colPermissions": "Uprawnienia" - }, - "groupby": { - "none": "Brak", - "title": "Grupuj według", - "owner": "Właściciel", - "shareDate": "Data udostępnienia", - "type": "Typ", - "type.folders": "Foldery", - "accessedAt": "Data dostępu", - "modifiedAt": "Data modyfikacji", - "createdAt": "Data utworzenia", - "size": "Rozmiar", - "favoriteDate": "Data dodania do ulubionych", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nowe" - }, - "dateBucket": { - "today": "Dzisiaj", - "last7days": "Ostatnie 7 dni", - "last30days": "Ostatnie 30 dni" - }, - "groups": { - "title": "Zarządzaj grupami", - "create_button": "Utwórz grupę", - "create_dialog_title": "Nowa grupa", - "edit_dialog_title": "Zmień nazwę grupy", - "name_label": "Nazwa", - "name_placeholder": "inzynieria", - "description_label": "Opis (opcjonalny)", - "members_section": "Członkowie", - "add_member_placeholder": "Dodaj użytkownika lub grupę…", - "no_members": "Brak członków.", - "remove_member": "Usuń", - "delete_group": "Usuń grupę", - "delete_confirm": "Usunąć grupę „{name}\"? Uprawnienia odwołujące się do tej grupy zostaną cofnięte.", - "empty_state": "Brak grup.", - "load_more": "Załaduj więcej", - "back_to_list": "Wstecz", - "loading": "Ładowanie…", - "virtual_badge": "System", - "member_count_zero": "Brak członków", - "member_count_one": "1 członek", - "member_count_other": "{count} członków", - "delete_confirm_label": "Wpisz nazwę grupy, aby potwierdzić:", - "delete_confirm_mismatch": "Wpisz nazwę grupy dokładnie, aby potwierdzić.", - "virtual_internal_name": "Wewnętrzni", - "members_loading": "Ładowanie członków…", - "members_empty": "Brak członków", - "virtual_internal_explanation": "Każdy użytkownik wewnętrzny na tym serwerze" - }, - "myshares": { - "copyLink": "Skopiuj link", - "deleteLink": "Usuń link", - "notifyByEmail": "Powiadom e-mailem", - "notifyFailed": "Nie udało się wysłać powiadomienia.", - "notifyGroupMembers": "Powiadom członków grupy", - "notifyRateLimited": "Zbyt wiele powiadomień dla tego odbiorcy — spróbuj ponownie później.", - "removeAccess": "Usuń dostęp", - "resendInvitation": "Wyślij ponownie e-mail z zaproszeniem" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", + "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz OxiCloud, aby zobaczyć nowe udostępnienie:\n{{login_link}}\n\nMożesz mieć dodatkowe nowe udostępnienia od {{inviter}} — zaloguj się, aby zobaczyć wszystkie udostępnione Ci elementy.\n\n— OxiCloud\n\nOtrzymujesz tę wiadomość, ponieważ masz konto OxiCloud i preferencja powiadomień o udostępnieniach jest włączona. Możesz ją wyłączyć w swoim profilu (Wyślij mi e-mail, gdy ktoś coś mi udostępni)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Minimalistyczny cloud storage" + }, + "nav": { + "files": "Pliki", + "shared": "Udostępnione", + "recent": "Ostatnie", + "favorites": "Ulubione", + "photos": "Zdjęcia", + "music": "Muzyka", + "trash": "Kosz", + "sharedwithme": "Udostępnione dla mnie", + "profile": "Profil", + "shared_with_me": "Udostępnione dla mnie" + }, + "photos": { + "empty_state": "Brak zdjęć", + "empty_hint": "Prześlij obrazy lub filmy, aby zobaczyć je tutaj", + "items_selected": "wybrane", + "view_daily": "Dzień", + "view_monthly": "Miesiąc", + "view_yearly": "Rok", + "group_by": "Grupuj według" + }, + "music": { + "create_playlist": "Utwórz playlistę", + "playlists": "Playlisty", + "no_playlists": "Brak playlist", + "empty_hint": "Utwórz pierwszą playlistę, aby zacząć organizować swoją muzykę", + "select_playlist": "Wybierz playlistę", + "select_hint": "Wybierz playlistę z paska bocznego lub utwórz nową", + "add_tracks": "Dodaj utwory", + "add_to_playlist": "Dodaj do playlisty", + "add": "Dodaj", + "added": "Dodano!", + "added_to_playlist": "dodano do playlisty", + "load_error": "Błąd ładowania playlist", + "add_error": "Nie można dodać utworów do playlisty", + "no_playlists_yet": "Brak playlist. Utwórz pierwszą!", + "selected_files": "Wybrane:", + "no_tracks": "Brak utworów na tej playliście", + "unknown_artist": "Nieznany wykonawca", + "unknown_title": "Nieznany", + "confirm_delete": "Usunąć tę playlistę?", + "playlist_name": "Nazwa playlisty", + "create": "Utwórz", + "delete": "Usuń", + "share": "Udostępnij", + "edit": "Edytuj", + "play_all": "Odtwórz wszystkie", + "shuffle": "Losowo", + "repeat": "Powtarzaj", + "repeat_one": "Powtarzaj jeden", + "queue": "Kolejka", + "queue_empty": "Kolejka jest pusta", + "not_playing": "Nic nie jest odtwarzane", + "play": "Odtwórz", + "pause": "Pauza", + "previous": "Poprzedni", + "next": "Następny", + "volume": "Głośność", + "mute": "Wycisz", + "unmute": "Włącz dźwięk", + "title": "Tytuł", + "artist": "Wykonawca", + "album": "Album", + "tracks": "utwory", + "share_with_user": "ID użytkownika lub e-mail", + "playback_error": "Odtwarzanie nie powiodło się", + "error": "Błąd", + "remove": "Usuń", + "track_removed": "Utwór usunięty", + "manage_shares": "Zarządzaj udostępnieniami", + "no_shares": "Brak udostępnień", + "remove_share": "Usuń udostępnienie", + "can_write": "Może edytować", + "read_only": "Tylko do odczytu", + "public": "Publiczny", + "private": "Prywatny", + "toggle_public": "Widoczność", + "make_public": "Ustaw jako publiczny", + "make_private": "Ustaw jako prywatny", + "set_cover": "Ustaw okładkę", + "cover_updated": "Okładka zaktualizowana", + "search_audio": "Szukaj plików audio…", + "no_audio_files": "Nie znaleziono plików audio", + "selected": "wybrane", + "loading": "Ładowanie…", + "search_error": "Nie można załadować plików audio", + "adding": "Dodawanie…", + "prev": "Poprzedni" + }, + "actions": { + "search": "Szukaj plików...", + "new_folder": "Nowy folder", + "upload": "Prześlij", + "upload_files": "Prześlij pliki", + "upload_folder": "Prześlij folder", + "upload.uploading": "Przesyłanie...", + "upload.complete": "{count} / {total} przesłano", + "upload.files": "plików", + "rename": "Zmień nazwę", + "move": "Przenieś do...", + "move_to": "Przenieś do", + "delete": "Usuń", + "download": "Pobierz", + "view": "Pokaż", + "cancel": "Anuluj", + "confirm": "Potwierdź", + "share": "Udostępnij", + "favorite": "Dodaj do ulubionych", + "unfavorite": "Usuń z ulubionych", + "copy": "Kopiuj", + "notify": "Powiadom", + "send": "Wyślij", + "clear_recent": "Wyczyść ostatnie", + "logout": "Wyloguj się", + "create": "Utwórz", + "search_btn": "Szukaj", + "close": "Zamknij", + "delete_permanently": "Usuń trwale", + "empty_trash": "Opróżnij kosz", + "open_parent_folder": "Przejdź do folderu nadrzędnego", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Wygląd", + "about": "O OxiCloud", + "about_description": "Platforma pamięci masowej w chmurze zbudowana w oparciu o Rust & Clean Architecture. Szybka, bezpieczna i prywatna.", + "admin_panel": "Panel administratora", + "profile": "Mój profil", + "role_user": "Użytkownik", + "theme": { + "light": "Jasny", + "dark": "Ciemny", + "auto": "Jak system" + }, + "manage_groups": "Zarządzaj grupami", + "admin": "Administrator" + }, + "share": { + "dialogTitle": "Link udostępniania", + "linkLabel": "Link udostępniania:", + "copyLink": "Kopiuj", + "permissions": "Uprawnienia:", + "permissionRead": "Odczyt", + "permissionWrite": "Zapis", + "permissionReshare": "Dalsze udostępnianie", + "password": "Ochrona hasłem:", + "generatePassword": "Wygeneruj", + "expiration": "Data wygaśnięcia:", + "update": "Zaktualizuj udostępnienie", + "remove": "Usuń udostępnienie", + "notifyTitle": "Wyślij powiadomienie", + "notifyEmailLabel": "Adres e-mail:", + "notifyMessageLabel": "Wiadomość (opcjonalnie):", + "notifySend": "Wyślij powiadomienie", + "shareWithOthers": "Udostępnij innym", + "sharePublicly": "Udostępnij publicznie", + "shareSettings": "Ustawienia udostępniania", + "shareCopied": "Link skopiowany do schowka", + "shareCreated": "Link udostępniania utworzony pomyślnie", + "shareUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", + "shareRemoved": "Udostępnienie usunięte pomyślnie", + "inviteByEmail": "Zaproś przez e-mail — zaproszenie zostanie wysłane", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Kopiuj", + "copy_failed": "Could not copy link", + "download": "Pobierz", + "files": "Pliki", + "folders": "Foldery", + "link_name": "Link name (optional)", + "notifyByEmail": "Powiadom e-mailem", + "revoke": "Usuń", + "role_label": "Rola" + }, + "share_dialogTitle": "Link udostępniania", + "share_linkLabel": "Link udostępniania:", + "share_copyLink": "Kopiuj", + "share_permissions": "Uprawnienia:", + "share_permissionRead": "Odczyt", + "share_permissionWrite": "Zapis", + "share_permissionReshare": "Dalsze udostępnianie", + "share_password": "Ochrona hasłem:", + "share_generatePassword": "Wygeneruj", + "share_expiration": "Data wygaśnięcia:", + "share_update": "Zaktualizuj udostępnienie", + "share_remove": "Usuń udostępnienie", + "share_notifyTitle": "Wyślij powiadomienie", + "share_notifyEmailLabel": "Adres e-mail:", + "share_notifyMessageLabel": "Wiadomość (opcjonalnie):", + "share_notifySend": "Wyślij powiadomienie", + "shared": { + "backToFiles": "Powrót do plików", + "pageTitle": "Udostępnione zasoby", + "pageDescription": "Zarządzaj udostępnionymi plikami i folderami", + "filterType": "Typ:", + "filterAll": "Wszystkie", + "filterFiles": "Pliki", + "filterFolders": "Foldery", + "sortBy": "Sortuj według:", + "sortByName": "Nazwa", + "sortByDate": "Data udostępnienia", + "sortByExpiration": "Wygaśnięcie", + "search": "Szukaj", + "colName": "Nazwa", + "colType": "Typ", + "colDateShared": "Data udostępnienia", + "colExpiration": "Wygaśnięcie", + "colPermissions": "Uprawnienia", + "colPassword": "Hasło", + "colActions": "Akcje", + "emptyStateTitle": "Brak udostępnionych zasobów", + "emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", + "goToFiles": "Przejdź do plików", + "typeFile": "Plik", + "typeFolder": "Folder", + "noExpiration": "Bez wygaśnięcia", + "hasPassword": "Tak", + "noPassword": "Nie", + "editShare": "Edytuj udostępnienie", + "notifyShare": "Powiadom kogoś", + "copyLink": "Kopiuj link", + "removeShare": "Usuń udostępnienie", + "linkCopied": "Link skopiowany do schowka!", + "linkCopyFailed": "Nie udało się skopiować linku", + "itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", + "itemRemoved": "Udostępnienie usunięte pomyślnie", + "invalidEmail": "Wprowadź prawidłowy adres e-mail", + "notificationSent": "Powiadomienie wysłane pomyślnie", + "notificationFailed": "Nie udało się wysłać powiadomienia", + "shared_backToFiles": "Powrót do plików", + "shared_pageTitle": "Udostępnione zasoby", + "shared_pageDescription": "Zarządzaj udostępnionymi plikami i folderami", + "shared_filterType": "Typ:", + "shared_filterAll": "Wszystkie", + "shared_filterFiles": "Pliki", + "shared_filterFolders": "Foldery", + "shared_sortBy": "Sortuj według:", + "shared_sortByName": "Nazwa", + "shared_sortByDate": "Data udostępnienia", + "shared_sortByExpiration": "Wygaśnięcie", + "shared_search": "Szukaj", + "shared_colName": "Nazwa", + "shared_colType": "Typ", + "shared_colDateShared": "Data udostępnienia", + "shared_colExpiration": "Wygaśnięcie", + "shared_colPermissions": "Uprawnienia", + "shared_colPassword": "Hasło", + "shared_colActions": "Akcje", + "shared_emptyStateTitle": "Brak udostępnionych zasobów", + "shared_emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", + "shared_goToFiles": "Przejdź do plików", + "shared_typeFile": "Plik", + "shared_typeFolder": "Folder", + "shared_noExpiration": "Bez wygaśnięcia", + "shared_hasPassword": "Tak", + "shared_noPassword": "Nie", + "shared_editShare": "Edytuj udostępnienie", + "shared_notifyShare": "Powiadom kogoś", + "shared_copyLink": "Kopiuj link", + "shared_removeShare": "Usuń udostępnienie", + "shared_linkCopied": "Link skopiowany do schowka!", + "shared_linkCopyFailed": "Nie udało się skopiować linku", + "shared_itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", + "shared_itemRemoved": "Udostępnienie usunięte pomyślnie", + "shared_invalidEmail": "Wprowadź prawidłowy adres e-mail", + "shared_notificationSent": "Powiadomienie wysłane pomyślnie", + "shared_notificationFailed": "Nie udało się wysłać powiadomienia" + }, + "files": { + "name": "Nazwa", + "type": "Typ", + "size": "Rozmiar", + "modified": "Zmodyfikowano", + "no_files": "Brak plików w tym folderze", + "empty_hint": "Prześlij pliki lub utwórz foldery, aby rozpocząć", + "loading": "Ładowanie plików…", + "view_grid": "Widok siatki", + "view_list": "Widok listy", + "file_types": { + "document": "Dokument", + "image": "Obraz", + "video": "Wideo", + "audio": "Audio", + "pdf": "PDF", + "text": "Tekst", + "folder": "Folder", + "spreadsheet": "Arkusz kalkulacyjny", + "presentation": "Prezentacja", + "archive": "Archiwum", + "installer": "Instalator", + "code": "Kod" + }, + "owner": "Właściciel", + "add_favorites": "Dodaj do ulubionych", + "added_favorites": "Dodano do ulubionych", + "col_name": "Nazwa", + "col_owner": "Właściciel", + "col_size": "Rozmiar", + "col_type": "Typ", + "copy": "Kopiuj", + "edit": "Edytuj", + "file": "Plik", + "folder": "Folder", + "new_folder": "Nowy folder", + "share": "Udostępnij", + "view": "Pokaż" + }, + "dialogs": { + "rename_folder": "Zmień nazwę folderu", + "rename_file": "Zmień nazwę pliku", + "new_name": "Nowa nazwa", + "new_folder_title": "Nowy folder", + "folder_name": "Nazwa folderu", + "folder_placeholder": "Mój folder", + "rename_title": "Zmień nazwę", + "move_file": "Przenieś plik", + "move_folder": "Przenieś folder", + "select_destination": "Wybierz folder docelowy:", + "select_this_folder": "Wybierz ten folder", + "go_to_parent": ".. (folder nadrzędny)", + "no_subfolders": "Brak podfolderów", + "root": "Główny", + "delete_confirmation": "Czy na pewno chcesz usunąć", + "and_contents": "i całą jego zawartość", + "no_undo": "Tej akcji nie można cofnąć", + "confirm_title": "Potwierdź akcję", + "confirm_delete": "Przenieś do kosza", + "confirm_delete_file": "Czy na pewno chcesz przenieść plik \"{{name}}\" do kosza?", + "confirm_delete_folder": "Czy na pewno chcesz przenieść folder \"{{name}}\" i całą jego zawartość do kosza?", + "confirm_permanent_delete": "Usuń trwale", + "confirm_permanent_delete_msg": "Czy na pewno chcesz trwale usunąć ten element? Tej akcji nie można cofnąć.", + "confirm_empty_trash": "Opróżnij kosz", + "confirm_delete_share": "Usuń link udostępniania", + "confirm_delete_share_msg": "Czy na pewno chcesz usunąć ten link udostępniania?", + "share_file": "Udostępnij plik", + "share_folder": "Udostępnij folder", + "existing_shares": "Istniejące udostępnienia", + "share_options": "Opcje udostępniania", + "password": "Hasło", + "expiration": "Wygaśnięcie", + "permissions": "Uprawnienia", + "generated_link": "Wygenerowany link", + "notify": "Wyślij powiadomienie", + "recipient": "Odbiorca", + "message": "Wiadomość", + "move_to_home": "Przenieś do folderu domowego" + }, + "dropzone": { + "drag_files": "Przeciągnij pliki tutaj lub kliknij, aby wybrać", + "drop_files": "Upuść pliki, aby przesłać" + }, + "permissions": { + "read": "Odczyt", + "write": "Zapis", + "reshare": "Dalsze udostępnianie" + }, + "errors": { + "file_not_found": "Plik nie został znaleziony", + "folder_not_found": "Folder nie został znaleziony", + "delete_error": "Błąd podczas usuwania", + "upload_error": "Błąd podczas przesyłania pliku", + "rename_error": "Błąd podczas zmiany nazwy", + "move_error": "Błąd podczas przenoszenia", + "empty_name": "Nazwa nie może być pusta", + "name_exists": "Plik lub folder o tej nazwie już istnieje", + "generic_error": "Wystąpił błąd", + "group_name_invalid": "Nazwa grupy musi spełniać format prefiksu e-mail (litery, cyfry, kropka, myślnik, podkreślnik; 1–64 znaków).", + "group_cycle": "Ten członek utworzyłby cykliczne odwołanie między grupami.", + "group_depth_exceeded": "Ta głębokość zagnieżdżenia przekracza maksymalną dozwoloną (8).", + "group_virtual_immutable": "Grupa „Internal” jest zarządzana przez system i nie może być modyfikowana.", + "group_not_found": "Grupa nie znaleziona.", + "group_name_taken": "Grupa o tej nazwie już istnieje." + }, + "breadcrumb": { + "home": "Strona główna" + }, + "trash": { + "empty_trash": "Opróżnij kosz", + "empty_state": "Kosz jest pusty", + "original_location": "Pierwotna lokalizacja", + "deleted_date": "Data usunięcia", + "remaining": "Pozostało", + "actions": "Akcje", + "restore": "Przywróć", + "delete_permanently": "Usuń trwale", + "empty_confirm": "Czy na pewno chcesz opróżnić kosz? Wszystkie elementy zostaną trwale usunięte.", + "groupby": { + "remaining_days": "Pozostałe dni", + "trashed_time": "Czas usunięcia" + }, + "delete": "Usuń trwale", + "empty_action": "Opróżnij kosz" + }, + "daysRemaining": { + "expired": "Wygasł", + "today": "Dziś", + "tomorrow": "Jutro", + "inDays": "{{count}} dni" + }, + "expiryChip": { + "never": "Nigdy nie wygasa", + "expired": "Wygasł", + "today": "Wygasa dziś", + "tomorrow": "Wygasa jutro", + "inDays": "Wygasa za {{count}} dni", + "onDate": "Wygasa {{date}}" + }, + "auth": { + "login_title": "Zaloguj się", + "username": "Nazwa użytkownika", + "username_placeholder": "Wprowadź nazwę użytkownika", + "login_identifier": "Nazwa użytkownika lub e-mail", + "login_identifier_placeholder": "Wpisz nazwę użytkownika lub e-mail", + "password": "Hasło", + "password_placeholder": "Wprowadź hasło", + "login_button": "Zaloguj się", + "no_account": "Nie masz konta?", + "register": "Zarejestruj się", + "admin_setup": "Pierwszy raz?", + "setup": "Konfiguracja administratora", + "register_title": "Utwórz konto", + "email": "E-mail", + "email_placeholder": "Wprowadź adres e-mail", + "confirm_password": "Potwierdź hasło", + "confirm_password_placeholder": "Potwierdź hasło", + "register_button": "Utwórz konto", + "have_account": "Masz już konto?", + "login": "Zaloguj się", + "setup_title": "Konfiguracja początkowa", + "setup_step1": "Administrator", + "setup_step2": "System", + "setup_step3": "Gotowe", + "admin_username": "Nazwa administratora", + "admin_email": "E-mail administratora", + "admin_password": "Hasło administratora", + "create_admin": "Utwórz administratora", + "back_to_login": "Już skonfigurowane?", + "admin_success": "Konto administratora zostało utworzone! Możesz się teraz zalogować.", + "account_success": "Konto utworzone pomyślnie! Możesz się teraz zalogować.", + "passwords_mismatch": "Hasła nie są zgodne", + "admin_create_error": "Błąd podczas tworzenia konta administratora", + "or": "lub", + "sso_login": "Zaloguj się przez SSO", + "sso_login_provider": "Zaloguj się przez {{provider}}", + "magicLinkHint": "Brak hasła? Wpisz swój adres e-mail, a wyślemy Ci jednorazowy link do logowania.", + "magicLinkEmailLabel": "Adres e-mail", + "magicLinkEmailPlaceholder": "ty@przyklad.pl", + "magicLinkSubmit": "Wyślij link do logowania", + "magicLinkSent": "Jeśli konto dla tego adresu istnieje, link do logowania został wysłany. Sprawdź swoją skrzynkę odbiorczą.", + "magicLinkUnavailable": "Logowanie e-mailem nie jest dostępne na tym serwerze.", + "magicLinkNetworkError": "Nie udało się połączyć z serwerem: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "Adres e-mail", + "magic_hint": "Brak hasła? Wpisz swój adres e-mail, a wyślemy Ci jednorazowy link do logowania.", + "magic_unavailable": "Logowanie e-mailem nie jest dostępne na tym serwerze.", + "passwords_match": "Passwords match", + "sign_in": "Zaloguj się" + }, + "storage": { + "title": "Pamięć masowa", + "calculating": "Obliczanie...", + "used": "{{percentage}}% wykorzystane ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Tego typu pliku nie można wyświetlić.", + "download_file": "Pobierz plik", + "zoom_in": "Przybliż", + "zoom_out": "Oddal", + "zoom_reset": "Resetuj powiększenie" + }, + "language_selector": { + "title": "Witaj!", + "subtitle": "Wybierz język, aby kontynuować", + "continue": "Kontynuuj", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Brak ulubionych", + "empty_hint": "Oznacz pliki lub foldery gwiazdką, aby dodać je do ulubionych", + "add": "Dodaj do ulubionych", + "remove": "Usuń z ulubionych", + "added_title": "Dodano do ulubionych", + "added_msg": "dodano do ulubionych", + "removed_title": "Usunięto z ulubionych", + "removed_msg": "usunięto z ulubionych" + }, + "recent": { + "title": "Ostatnie", + "clear": "Wyczyść ostatnie", + "accessed": "Otwarte", + "empty_state": "Brak ostatnich plików", + "empty_hint": "Otwarte pliki pojawią się tutaj", + "loadMore": "Załaduj więcej" + }, + "notifications": { + "file_renamed": "Zmieniono nazwę pliku", + "file_renamed_to": "Nazwa pliku zmieniona na \"{{name}}\"", + "folder_renamed": "Zmieniono nazwę folderu", + "folder_renamed_to": "Nazwa folderu zmieniona na \"{{name}}\"", + "file_uploaded": "Plik przesłany", + "file_deleted": "Plik przeniesiony do kosza", + "folder_deleted": "Folder przeniesiony do kosza", + "item_deleted_permanently": "Element trwale usunięty", + "trash_emptied": "Kosz został opróżniony", + "title": "Powiadomienia", + "empty": "Brak powiadomień", + "link_created": "Link utworzony", + "share_success": "Link udostępniania utworzony pomyślnie", + "upload_files_section_title": "Przesyłanie niedostępne tutaj", + "upload_files_section_body": "Przejdź do sekcji Pliki, aby przesłać pliki" + }, + "batch": { + "one_selected": "Wybrano 1 element", + "n_selected": "Wybrano {{count}} elementów", + "confirm_delete": "Czy na pewno chcesz przenieść {{count}} elementów do kosza?", + "move_title": "Przenieś {{count}} element(ów)", + "add_favorites": "Dodaj do ulubionych", + "move_copy": "Przenieś lub kopiuj" + }, + "admin": { + "page_title": "Panel administratora", + "back_to_app": "Powrót do OxiCloud", + "loading": "Ładowanie…", + "access_denied": "Dostęp zabroniony", + "access_denied_desc": "Aby uzyskać dostęp do tego panelu, wymagane są uprawnienia administratora.", + "sign_in": "Zaloguj się", + "tab_dashboard": "Panel", + "tab_users": "Użytkownicy", + "tab_oidc": "SSO / OIDC", + "total_users": "Wszyscy użytkownicy", + "active_users": "Aktywni użytkownicy", + "admins": "Administratorzy", + "version": "Wersja", + "storage_overview": "Przegląd pamięci masowej", + "used": "Wykorzystane", + "total_quota": "Łączny przydział", + "usage_pct": "Wykorzystanie %", + "users_over_80": "Użytkownicy >80% przydziału", + "users_over_quota": "Użytkownicy powyżej przydziału", + "system": "System", + "auth_label": "Uwierzytelnianie", + "oidc_label": "OIDC", + "quotas_label": "Przydziały", + "enabled": "Włączone", + "disabled": "Wyłączone", + "active": "Aktywne", + "off": "Wyłączone", + "allow_registration": "Zezwalaj na publiczną samodzielną rejestrację", + "registration_warning": "Publiczna rejestracja jest wyłączona. Tylko administratorzy mogą tworzyć nowych użytkowników.", + "user_management": "Zarządzanie użytkownikami", + "create_user": "Utwórz użytkownika", + "col_user": "Użytkownik", + "col_role": "Rola", + "col_auth": "Uwierzytelnianie", + "col_status": "Status", + "col_storage": "Pamięć masowa", + "col_last_login": "Ostatnie logowanie", + "col_actions": "Akcje", + "loading_users": "Ładowanie użytkowników…", + "failed_load_users": "Nie udało się załadować użytkowników", + "no_users_found": "Nie znaleziono użytkowników", + "showing_users": "Wyświetlanie {{from}}-{{to}} z {{total}}", + "prev": "Poprzednia", + "next": "Następna", + "inactive": "Nieaktywny", + "you_badge": "(ty)", + "local": "Lokalny", + "never": "Nigdy", + "just_now": "Przed chwilą", + "minutes_ago": "{{n}} min temu", + "hours_ago": "{{n}} godz. temu", + "days_ago": "{{n}} dni temu", + "edit_quota_title": "Edytuj przydział", + "reset_password_title": "Resetuj hasło", + "toggle_role_title": "Przełącz rolę", + "deactivate_title": "Dezaktywuj", + "activate_title": "Aktywuj", + "delete_title": "Usuń", + "sso_title": "Single Sign-On (OIDC / SSO)", + "enable_sso": "Włącz uwierzytelnianie SSO", + "provider_name": "Nazwa dostawcy", + "issuer_url": "URL wystawcy", + "issuer_url_hint": "URL wystawcy OpenID Connect Twojego dostawcy tożsamości", + "auto_discover": "Automatyczne wykrywanie", + "discovering": "Wykrywanie…", + "client_id": "ID klienta", + "client_secret": "Sekret klienta", + "client_secret_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", + "secret_configured": "Sekret klienta jest już skonfigurowany", + "callback_url": "URL zwrotny", + "callback_url_hint": "(zarejestruj w swoim IdP)", + "advanced_settings": "Ustawienia zaawansowane", + "scopes": "Zakresy", + "auto_provision": "Automatycznie twórz użytkowników przy pierwszym logowaniu", + "admin_groups": "Grupy administratorów", + "admin_groups_hint": "Lista nazw grup OIDC oddzielonych przecinkami, mapowanych na rolę administratora", + "disable_password": "Wyłącz logowanie hasłem (tylko OIDC)", + "password_warning": "To uniemożliwi WSZYSTKIE logowania hasłem!", + "test_btn": "Testuj", + "save_btn": "Zapisz", + "saving": "Zapisywanie…", + "settings_saved": "Ustawienia zapisane — OIDC jest teraz {{status}}", + "quota_modal_title": "Aktualizuj przydział pamięci masowej", + "quota_user_label": "Użytkownik:", + "new_quota": "Nowy przydział", + "quota_unlimited_hint": "Ustaw 0 dla nieograniczonego", + "cancel": "Anuluj", + "create_user_title": "Utwórz nowego użytkownika", + "username_label": "Nazwa użytkownika", + "username_placeholder": "jankowalski", + "username_hint": "3–32 znaków", + "password_label": "Hasło", + "password_placeholder": "Min. 8 znaków", + "email_label": "E-mail", + "email_optional": "(opcjonalnie)", + "email_placeholder": "uzytkownik@example.com (generowany automatycznie, jeśli puste)", + "role_label": "Rola", + "role_user": "Użytkownik", + "role_admin": "Administrator", + "quota_label": "Przydział", + "creating": "Tworzenie…", + "reset_pw_title": "Resetuj hasło", + "new_password_label": "Nowe hasło", + "resetting": "Resetowanie…", + "reset_btn": "Resetuj", + "confirm_role_change": "Zmienić rolę na {{role}}?", + "confirm_deactivate": "Czy na pewno chcesz dezaktywować tego użytkownika?", + "confirm_activate": "Czy na pewno chcesz aktywować tego użytkownika?", + "confirm_delete_user": "USUNĄĆ użytkownika \"{{name}}\"? Tej akcji nie można cofnąć!", + "confirm_action": "Potwierdź akcję", + "confirm_yes": "Potwierdź", + "confirm_no": "Anuluj", + "error_username_short": "Nazwa użytkownika musi mieć co najmniej 3 znaki", + "error_password_short": "Hasło musi mieć co najmniej 8 znaków", + "error_generic": "Niepowodzenie", + "error_network": "Błąd sieci: {{message}}", + "error_create_user": "Nie udało się utworzyć użytkownika", + "tab_storage": "Pamięć masowa", + "storage_title": "Backend pamięci masowej", + "storage_current_backend": "Aktywny backend", + "storage_total_blobs": "Liczba blobów", + "storage_total_size": "Łączny rozmiar", + "storage_dedup_ratio": "Współczynnik deduplikacji", + "storage_backend": "Typ backendu", + "storage_local": "Lokalny system plików", + "storage_s3": "Kompatybilny z S3", + "storage_provider_preset": "Ustawienia dostawcy", + "storage_preset_custom": "Niestandardowy", + "storage_endpoint_url": "URL endpointu", + "storage_endpoint_hint": "Pozostaw puste dla domyślnego Amazon S3", + "storage_bucket": "Bucket", + "storage_region": "Region", + "storage_access_key": "Access Key ID", + "storage_secret_key": "Secret Access Key", + "storage_secret_configured": "Klucz tajny jest już skonfigurowany", + "storage_key_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", + "storage_path_style": "Wymuś styl ścieżki", + "storage_path_style_hint": "Wymagane dla MinIO i niektórych dostawców kompatybilnych z S3", + "storage_test_connection": "Testuj połączenie", + "storage_test_success": "Połączenie udane", + "storage_test_failure": "Połączenie nieudane", + "storage_save": "Zapisz", + "storage_saved": "Ustawienia pamięci masowej zapisane pomyślnie", + "storage_migration": "Migracja backendu", + "storage_migration_coming_soon": "Migracja backendu będzie dostępna w przyszłej aktualizacji.", + "migration_status_label": "Status:", + "migration_start": "Rozpocznij migrację", + "migration_pause": "Wstrzymaj", + "migration_resume": "Wznów", + "migration_verify": "Sprawdź integralność", + "migration_complete": "Sfinalizuj", + "migration_started": "Migracja rozpoczęta", + "migration_paused_msg": "Migracja wstrzymana", + "migration_resumed_msg": "Migracja wznowiona", + "migration_completed_msg": "Migracja sfinalizowana. Uruchom ponownie serwer, aby użyć nowego backendu.", + "migration_verifying": "Weryfikowanie…", + "migration_verify_passed": "Weryfikacja zaliczona", + "migration_verify_failed": "Weryfikacja nieudana", + "migration_failed_blobs": "nieudane bloby", + "testing": "Testowanie…", + "smtp_disabled": "Wyłączone (host nieustawiony)", + "smtp_enabled": "Włączone", + "smtp_enabled_label": "Status", + "smtp_intro": "SMTP jest konfigurowany wyłącznie przez zmienne środowiskowe (OXICLOUD_SMTP_*). Poniższe wartości są odczytywane z działającego serwera — aby je zmienić, zmodyfikuj środowisko i uruchom ponownie OxiCloud.", + "smtp_not_configured": "SMTP nie jest skonfigurowany na tym serwerze.", + "smtp_send_failed": "Wysłanie nie powiodło się.", + "smtp_send_test": "Wyślij e-mail testowy", + "smtp_sending": "Wysyłanie…", + "smtp_sent": "E-mail testowy wysłany.", + "smtp_server_code": "Odpowiedź serwera", + "smtp_test_intro": "Wysyła wstępnie zdefiniowaną wiadomość diagnostyczną do podanego poniżej odbiorcy i raportuje odpowiedź serwera SMTP, abyś mógł skorelować ją z logami swojego przekaźnika.", + "smtp_test_missing_to": "Wprowadź adres odbiorcy.", + "smtp_test_title": "Wyślij e-mail testowy", + "smtp_test_to": "Adres odbiorcy", + "smtp_title": "Poczta wychodząca (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Administratorzy", + "confirm_role": "Zmienić rolę na {{role}}?", + "dashboard": "Panel", + "email": "E-mail", + "mig_complete": "Sfinalizuj", + "mig_pause": "Wstrzymaj", + "mig_resume": "Wznów", + "mig_verify_failed": "Weryfikacja nieudana", + "mig_verify_passed": "Weryfikacja zaliczona", + "mig_verifying": "Weryfikowanie…", + "oidc_auto_provision": "Automatycznie twórz użytkowników przy pierwszym logowaniu", + "oidc_callback": "URL zwrotny", + "oidc_client_id": "ID klienta", + "oidc_disable_pw": "Wyłącz logowanie hasłem (tylko OIDC)", + "oidc_issuer": "URL wystawcy", + "oidc_scopes": "Zakresy", + "password": "Hasło", + "quotas": "Przydziały", + "reset_pw_for": "Nowe hasło dla", + "role": "Rola", + "smtp_fail": "Wysłanie nie powiodło się.", + "smtp_send": "Wyślij", + "smtp_test": "Wyślij e-mail testowy", + "smtp_user_state": "Uwierzytelnianie", + "status": "Status", + "storage": "Pamięć masowa", + "storage_endpoint": "URL endpointu", + "storage_tab": "Pamięć masowa", + "time_min_ago": "{{n}} min temu", + "title": "Administrator", + "user": "Użytkownik", + "username": "Nazwa użytkownika", + "users": "Użytkownicy" + }, + "profile": { + "page_title": "Profil", + "back_to_app": "Powrót do OxiCloud", + "loading": "Ładowanie…", + "not_authenticated": "Nieuwierzytelniony", + "not_authenticated_desc": "Zaloguj się, aby zobaczyć swój profil.", + "sign_in": "Zaloguj się", + "role_admin": "Administrator", + "role_user": "Użytkownik", + "account_details": "Szczegóły konta", + "username": "Nazwa użytkownika", + "email": "E-mail", + "role": "Rola", + "last_login": "Ostatnie logowanie", + "storage": "Pamięć masowa", + "used": "Wykorzystane", + "quota": "Przydział", + "usage": "Wykorzystanie", + "unlimited": "Nieograniczone", + "app_passwords": "Hasła aplikacji", + "app_pw_desc": "Generuj hasła dla klientów WebDAV, CalDAV i CardDAV. Każde hasło jest wyświetlane tylko raz.", + "app_pw_label_placeholder": "Etykieta (np. Thunderbird, macOS)", + "generate": "Wygeneruj", + "generating": "Generowanie…", + "new_password_for": "Nowe hasło dla", + "copy_warning": "Skopiuj to hasło teraz. Nie zobaczysz go ponownie.", + "copy_to_clipboard": "Kopiuj do schowka", + "col_label": "Etykieta", + "col_created": "Utworzone", + "col_last_used": "Ostatnio używane", + "col_status": "Status", + "active": "Aktywne", + "revoked": "Unieważnione", + "revoke_title": "Unieważnij", + "no_app_passwords": "Brak haseł aplikacji.", + "client_sessions": "Sesje klientów", + "client_sessions_desc": "Generowane automatycznie po połączeniu z klientem kompatybilnym z Nextcloud.", + "col_client": "Klient", + "never": "Nigdy", + "just_now": "Przed chwilą", + "minutes_ago": "{{n}} min temu", + "hours_ago": "{{n}} godz. temu", + "days_ago": "{{n}} dni temu", + "edit_profile": "Edytuj profil", + "edit_oidc_managed": "Aby zmienić swoje dane (nazwisko, imię, zdjęcie profilowe, …), zaktualizuj je u swojego dostawcy tożsamości. Twoje zmiany pojawią się przy następnym logowaniu.", + "username_claim_hint": "2–64 znaki, litery / cyfry / kropka / myślnik / podkreślenie. Po wybraniu nazwy użytkownika nie można jej zmienić (klienty DAV/NextCloud są od niej zależne).", + "username_already_claimed": "Nazwa użytkownika jest ustawiona i nie może być zmieniona (klienty DAV/NextCloud są od niej zależne).", + "given_name": "Imię", + "family_name": "Nazwisko", + "notify_on_share": "Wyślij mi e-mail, gdy ktoś coś mi udostępni", + "notify_on_share_hint": "Gdy odznaczone, udostępnienia nadal pojawiają się na Twoim koncie — po prostu nie otrzymasz o nich e-maila.", + "save_profile": "Zapisz zmiany", + "profile_saved": "Profil zaktualizowany", + "profile_no_changes": "Brak zmian do zapisania.", + "profile_save_failed": "Zapis nie powiódł się", + "username_taken_error": "Ta nazwa użytkownika jest już zajęta.", + "username_immutable_error": "Twoja nazwa użytkownika jest już ustawiona i nie można jej tutaj zmienić. Skontaktuj się z administratorem, jeśli chcesz ją zmienić.", + "change_password": "Zmień hasło", + "current_password": "Bieżące hasło", + "new_password": "Nowe hasło", + "min_8_chars": "Co najmniej 8 znaków", + "confirm_password": "Potwierdź nowe hasło", + "update_password": "Aktualizuj hasło", + "updating": "Aktualizowanie…", + "password_updated": "Hasło zaktualizowane pomyślnie", + "passwords_no_match": "Hasła nie są zgodne", + "password_too_short": "Hasło musi mieć co najmniej 8 znaków", + "password_change_failed": "Nie udało się zmienić hasła", + "error_network": "Błąd sieci: {{message}}", + "error_label_required": "Wprowadź etykietę", + "error_create_pw": "Nie udało się utworzyć hasła aplikacji", + "confirm_revoke": "Unieważnić hasło aplikacji \"{{label}}\"? Klienci używający tego hasła przestaną działać.", + "error_revoke": "Nie udało się unieważnić hasła aplikacji", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Hasła nie są zgodne" + }, + "upload": { + "uploading": "Przesyłanie...", + "files": "plików", + "complete": "{{count}} / {{total}} przesłano" + }, + "storage_quota_exceeded": "Przekroczono limit pamięci masowej", + "sharedwithme": { + "pageTitle": "Udostępnione dla mnie", + "pageDescription": "Pliki i foldery, które inni użytkownicy udostępnili Ci", + "emptyStateTitle": "Nic nie zostało Ci jeszcze udostępnione", + "emptyStateDesc": "Elementy udostępnione Ci przez innych użytkowników pojawią się tutaj", + "loadMore": "Załaduj więcej", + "sharedBy": "Udostępnione przez", + "colName": "Nazwa", + "colType": "Typ", + "colSharedBy": "Udostępnione przez", + "colDate": "Data udostępnienia", + "colPermissions": "Uprawnienia" + }, + "groupby": { + "none": "Brak", + "title": "Grupuj według", + "owner": "Właściciel", + "shareDate": "Data udostępnienia", + "type": "Typ", + "type.folders": "Foldery", + "accessedAt": "Data dostępu", + "modifiedAt": "Data modyfikacji", + "createdAt": "Data utworzenia", + "size": "Rozmiar", + "favoriteDate": "Data dodania do ulubionych", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Nowe", + "folders": "Foldery" + }, + "dateBucket": { + "today": "Dzisiaj", + "last7days": "Ostatnie 7 dni", + "last30days": "Ostatnie 30 dni", + "unknown": "Nieznany" + }, + "groups": { + "title": "Zarządzaj grupami", + "create_button": "Utwórz grupę", + "create_dialog_title": "Nowa grupa", + "edit_dialog_title": "Zmień nazwę grupy", + "name_label": "Nazwa", + "name_placeholder": "inzynieria", + "description_label": "Opis (opcjonalny)", + "members_section": "Członkowie", + "add_member_placeholder": "Dodaj użytkownika lub grupę…", + "no_members": "Brak członków.", + "remove_member": "Usuń", + "delete_group": "Usuń grupę", + "delete_confirm": "Usunąć grupę „{name}\"? Uprawnienia odwołujące się do tej grupy zostaną cofnięte.", + "empty_state": "Brak grup.", + "load_more": "Załaduj więcej", + "back_to_list": "Wstecz", + "loading": "Ładowanie…", + "virtual_badge": "System", + "member_count_zero": "Brak członków", + "member_count_one": "1 członek", + "member_count_other": "{count} członków", + "delete_confirm_label": "Wpisz nazwę grupy, aby potwierdzić:", + "delete_confirm_mismatch": "Wpisz nazwę grupy dokładnie, aby potwierdzić.", + "virtual_internal_name": "Wewnętrzni", + "members_loading": "Ładowanie członków…", + "members_empty": "Brak członków", + "virtual_internal_explanation": "Każdy użytkownik wewnętrzny na tym serwerze", + "create": "Utwórz grupę", + "empty": "Brak grup.", + "members": "Członkowie" + }, + "myshares": { + "copyLink": "Skopiuj link", + "deleteLink": "Usuń link", + "notifyByEmail": "Powiadom e-mailem", + "notifyFailed": "Nie udało się wysłać powiadomienia.", + "notifyGroupMembers": "Powiadom członków grupy", + "notifyRateLimited": "Zbyt wiele powiadomień dla tego odbiorcy — spróbuj ponownie później.", + "removeAccess": "Usuń dostęp", + "resendInvitation": "Wyślij ponownie e-mail z zaproszeniem", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Audio", + "code": "Kod", + "text": "Tekst" + }, + "common": { + "add": "Dodaj", + "cancel": "Anuluj", + "clear": "Clear", + "close": "Zamknij", + "confirm": "Potwierdź", + "copy": "Kopiuj", + "create": "Utwórz", + "delete": "Usuń", + "download": "Pobierz", + "load_more": "Załaduj więcej", + "loading": "Ładowanie…", + "next": "Następny", + "no": "Nie", + "previous": "Poprzedni", + "remove": "Usuń", + "rename": "Zmień nazwę", + "save": "Zapisz", + "search": "Szukaj", + "yes": "Tak" + }, + "device": { + "continue": "Kontynuuj", + "unknown": "Nieznany" + }, + "expiryBucket": { + "expired": "Wygasł", + "noExpiry": "Bez wygaśnięcia", + "today": "Dziś", + "tomorrow": "Jutro" + }, + "nextcloud": { + "error_title": "Błąd", + "sign_in_with": "Zaloguj się przez {{provider}}" + }, + "search": { + "size_label": "Rozmiar", + "title": "Szukaj", + "type": { + "audio": "Audio" + }, + "type_label": "Typ" + }, + "sizeBucket": { + "folders": "Foldery" + }, + "view": { + "grid": "Widok siatki", + "list": "Widok listy" + } } diff --git a/frontend/static/locales/pt.json b/frontend/static/locales/pt.json index 1e8b3657..30a70cb6 100644 --- a/frontend/static/locales/pt.json +++ b/frontend/static/locales/pt.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Este link de início de sessão já não é válido", - "expired_body": "O link pode ter expirado ou já ter sido usado. Podemos enviar-lhe um novo — chegará à sua caixa de entrada em alguns segundos.", - "resend_to": "Enviar um novo link para {{email}}", - "generic_unavailable": "Este link de início de sessão já não é válido. Pode já ter sido usado ou ter expirado. Solicite um novo link na página de início de sessão.", - "service_unavailable": "O início de sessão por magic link não está ativado neste servidor.", - "internal_error": "Ocorreu um erro ao iniciar sessão. Por favor, tente novamente.", - "resend_failure": "Ocorreu um erro ao enviar o link. Por favor, tente novamente.", - "cross_browser_title": "Continuar o início de sessão neste dispositivo?", - "cross_browser_body": "Abriu este link de início de sessão num navegador ou dispositivo diferente daquele em que o solicitou.", - "cross_browser_warning": "Se foi você quem solicitou este link, é seguro continuar. Caso contrário, feche esta página — clicar em Continuar iniciaria sessão de outra pessoa na sua conta.", - "cross_browser_continue": "Continuar e iniciar sessão", - "resend_confirmation_title": "Verifique a sua caixa de entrada", - "resend_confirmation_body": "Se o link de início de sessão pertencia a uma conta ativa, um novo link acaba de ser enviado. Por favor, verifique a sua caixa de entrada.", - "return_link": "Voltar ao OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", - "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra-o clicando no link abaixo:\n{{link}}\n\nO link é de uso único e expira em {{ttl_hours}} horas.\nSe não esperava este convite, pode ignorar esta mensagem.\n\n— OxiCloud" - }, - "login": { - "subject": "Iniciar sessão no OxiCloud", - "body": "Olá,\n\nUse o link abaixo para iniciar sessão no OxiCloud. O link é de uso único e expira em {{ttl_minutes}} minutos. Abra-o no mesmo dispositivo em que o solicitou.\n\n{{link}}\n\nSe não solicitou este link de início de sessão, pode ignorar esta mensagem — não é necessária qualquer outra ação.\n\n— OxiCloud" - }, - "kind_file": "ficheiro", - "kind_folder": "pasta", - "english_fallback_divider": "--- Versão em inglês abaixo ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Este link de início de sessão já não é válido", + "expired_body": "O link pode ter expirado ou já ter sido usado. Podemos enviar-lhe um novo — chegará à sua caixa de entrada em alguns segundos.", + "resend_to": "Enviar um novo link para {{email}}", + "generic_unavailable": "Este link de início de sessão já não é válido. Pode já ter sido usado ou ter expirado. Solicite um novo link na página de início de sessão.", + "service_unavailable": "O início de sessão por magic link não está ativado neste servidor.", + "internal_error": "Ocorreu um erro ao iniciar sessão. Por favor, tente novamente.", + "resend_failure": "Ocorreu um erro ao enviar o link. Por favor, tente novamente.", + "cross_browser_title": "Continuar o início de sessão neste dispositivo?", + "cross_browser_body": "Abriu este link de início de sessão num navegador ou dispositivo diferente daquele em que o solicitou.", + "cross_browser_warning": "Se foi você quem solicitou este link, é seguro continuar. Caso contrário, feche esta página — clicar em Continuar iniciaria sessão de outra pessoa na sua conta.", + "cross_browser_continue": "Continuar e iniciar sessão", + "resend_confirmation_title": "Verifique a sua caixa de entrada", + "resend_confirmation_body": "Se o link de início de sessão pertencia a uma conta ativa, um novo link acaba de ser enviado. Por favor, verifique a sua caixa de entrada.", + "return_link": "Voltar ao OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", + "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra-o clicando no link abaixo:\n{{link}}\n\nO link é de uso único e expira em {{ttl_hours}} horas.\nSe não esperava este convite, pode ignorar esta mensagem.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", - "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra o OxiCloud para ver a sua nova partilha:\n{{login_link}}\n\nPode ter mais partilhas novas de {{inviter}} — inicie sessão para ver todos os itens partilhados consigo.\n\n— OxiCloud\n\nRecebeu esta mensagem porque tem uma conta OxiCloud e a preferência de notificação de partilhas está ativada. Pode desativá-la no seu perfil (Avisar-me por e-mail quando alguém compartilhar comigo)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Sistema de armazenamento em nuvem minimalista" - }, - "nav": { - "files": "Arquivos", - "shared": "Compartilhamentos", - "recent": "Recentes", - "favorites": "Favoritos", - "photos": "Fotos", - "music": "Música", - "trash": "Lixeira", - "sharedwithme": "Compartilhados comigo" - }, - "photos": { - "empty_state": "Nenhuma foto ainda", - "empty_hint": "Envie imagens ou vídeos para vê-los aqui", - "items_selected": "selecionados", - "view_daily": "Dia", - "view_monthly": "Mês", - "view_yearly": "Ano" - }, - "music": { - "create_playlist": "Criar Playlist", - "playlists": "Playlists", - "no_playlists": "Nenhuma playlist ainda", - "select_playlist": "Selecione uma playlist", - "select_hint": "Escolha uma playlist na barra lateral ou crie uma nova", - "add_tracks": "Adicionar Faixas", - "no_tracks": "Nenhuma faixa nesta playlist", - "unknown_artist": "Artista Desconhecido", - "unknown_title": "Desconhecido", - "confirm_delete": "Excluir esta playlist?", - "playlist_name": "Nome da playlist", - "create": "Criar", - "delete": "Excluir", - "share": "Compartilhar", - "edit": "Editar", - "play_all": "Reproduzir Tudo", - "shuffle": "Aleatório", - "repeat": "Repetir", - "repeat_one": "Repetir Uma", - "queue": "Fila", - "queue_empty": "Fila vazia", - "not_playing": "Não reproduzindo", - "play": "Reproduzir", - "pause": "Pausar", - "previous": "Anterior", - "next": "Próximo", - "volume": "Volume", - "mute": "Mudo", - "unmute": "Ativar som", - "title": "Título", - "artist": "Artista", - "album": "Álbum", - "tracks": "faixas", - "add": "Adicionar", - "added": "Adicionado!", - "added_to_playlist": "adicionado à playlist", - "add_to_playlist": "Adicionar à playlist", - "load_error": "Erro ao carregar playlists", - "add_error": "Não foi possível adicionar as faixas", - "no_playlists_yet": "Nenhuma playlist ainda. Crie uma primeiro!", - "selected_files": "Selecionados:", - "error": "Erro", - "search_audio": "Pesquisar ficheiros de áudio…", - "no_audio_files": "Nenhum ficheiro de áudio encontrado", - "selected": "selecionados", - "loading": "A carregar…", - "search_error": "Não foi possível carregar os ficheiros de áudio", - "adding": "A adicionar…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Pesquisar arquivos...", - "new_folder": "Nova pasta", - "upload": "Enviar", - "upload_files": "Enviar arquivos", - "upload_folder": "Enviar pasta", - "upload.uploading": "Enviando...", - "upload.complete": "{count} / {total} enviados", - "upload.files": "arquivos", - "rename": "Renomear", - "move": "Mover para...", - "move_to": "Mover para", - "delete": "Excluir", - "download": "Baixar", - "view": "Visualizar", - "cancel": "Cancelar", - "confirm": "Confirmar", - "share": "Compartilhar", - "favorite": "Adicionar aos favoritos", - "unfavorite": "Remover dos favoritos", - "copy": "Copiar", - "notify": "Notificar", - "send": "Enviar", - "clear_recent": "Limpar recentes", - "logout": "Sair", - "create": "Criar", - "search_btn": "Pesquisar", - "close": "Fechar", - "delete_permanently": "Excluir permanentemente", - "empty_trash": "Esvaziar lixeira", - "open_parent_folder": "Ir para a pasta pai", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Aparência", - "about": "Sobre o OxiCloud", - "about_description": "Plataforma de armazenamento em nuvem construída com Rust e Arquitetura Limpa. Rápida, segura e privada.", - "admin_panel": "Painel de administração", - "profile": "Meu perfil", - "role_user": "Usuário", - "theme": { - "light": "Claro", - "dark": "Escuro", - "auto": "Como o sistema" + "login": { + "subject": "Iniciar sessão no OxiCloud", + "body": "Olá,\n\nUse o link abaixo para iniciar sessão no OxiCloud. O link é de uso único e expira em {{ttl_minutes}} minutos. Abra-o no mesmo dispositivo em que o solicitou.\n\n{{link}}\n\nSe não solicitou este link de início de sessão, pode ignorar esta mensagem — não é necessária qualquer outra ação.\n\n— OxiCloud" }, - "manage_groups": "Gerenciar grupos" + "kind_file": "ficheiro", + "kind_folder": "pasta", + "english_fallback_divider": "--- Versão em inglês abaixo ---" + } }, - "share": { - "dialogTitle": "Link de compartilhamento", - "linkLabel": "Link compartilhado:", - "copyLink": "Copiar", - "permissions": "Permissões:", - "permissionRead": "Leitura", - "permissionWrite": "Escrita", - "permissionReshare": "Recompartilhar", - "password": "Proteção por senha:", - "generatePassword": "Gerar", - "expiration": "Data de expiração:", - "update": "Atualizar compartilhamento", - "remove": "Remover compartilhamento", - "notifyTitle": "Enviar notificação", - "notifyEmailLabel": "Endereço de e-mail:", - "notifyMessageLabel": "Mensagem (opcional):", - "notifySend": "Enviar notificação", - "shareWithOthers": "Compartilhar com outros", - "sharePublicly": "Compartilhar publicamente", - "shareSettings": "Configurações de compartilhamento", - "shareCopied": "Link copiado para a área de transferência", - "shareCreated": "Link de compartilhamento criado com sucesso", - "shareUpdated": "Configurações de compartilhamento atualizadas", - "shareRemoved": "Compartilhamento removido com sucesso", - "inviteByEmail": "Convidar por e-mail — o convite será enviado", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link de compartilhamento", - "share_linkLabel": "Link compartilhado:", - "share_copyLink": "Copiar", - "share_permissions": "Permissões:", - "share_permissionRead": "Leitura", - "share_permissionWrite": "Escrita", - "share_permissionReshare": "Recompartilhar", - "share_password": "Proteção por senha:", - "share_generatePassword": "Gerar", - "share_expiration": "Data de expiração:", - "share_update": "Atualizar compartilhamento", - "share_remove": "Remover compartilhamento", - "share_notifyTitle": "Enviar notificação", - "share_notifyEmailLabel": "Endereço de e-mail:", - "share_notifyMessageLabel": "Mensagem (opcional):", - "share_notifySend": "Enviar notificação", - "shared": { - "backToFiles": "Voltar aos arquivos", - "pageTitle": "Recursos compartilhados", - "pageDescription": "Gerencie seus arquivos e pastas compartilhados", - "filterType": "Tipo:", - "filterAll": "Todos", - "filterFiles": "Arquivos", - "filterFolders": "Pastas", - "sortBy": "Ordenar por:", - "sortByName": "Nome", - "sortByDate": "Data de compartilhamento", - "sortByExpiration": "Expiração", - "search": "Pesquisar", - "colName": "Nome", - "colType": "Tipo", - "colDateShared": "Data de compartilhamento", - "colExpiration": "Expiração", - "colPermissions": "Permissões", - "colPassword": "Senha", - "colActions": "Ações", - "emptyStateTitle": "Nenhum recurso compartilhado ainda", - "emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", - "goToFiles": "Ir para arquivos", - "typeFile": "Arquivo", - "typeFolder": "Pasta", - "noExpiration": "Sem expiração", - "hasPassword": "Sim", - "noPassword": "Não", - "editShare": "Editar compartilhamento", - "notifyShare": "Notificar alguém", - "copyLink": "Copiar link", - "removeShare": "Remover compartilhamento", - "linkCopied": "Link copiado para a área de transferência!", - "linkCopyFailed": "Falha ao copiar o link", - "itemUpdated": "Configurações de compartilhamento atualizadas", - "itemRemoved": "Compartilhamento removido com sucesso", - "invalidEmail": "Por favor, insira um endereço de e-mail válido", - "notificationSent": "Notificação enviada com sucesso", - "notificationFailed": "Falha ao enviar a notificação", - "shared_backToFiles": "Voltar aos arquivos", - "shared_pageTitle": "Recursos compartilhados", - "shared_pageDescription": "Gerencie seus arquivos e pastas compartilhados", - "shared_filterType": "Tipo:", - "shared_filterAll": "Todos", - "shared_filterFiles": "Arquivos", - "shared_filterFolders": "Pastas", - "shared_sortBy": "Ordenar por:", - "shared_sortByName": "Nome", - "shared_sortByDate": "Data de compartilhamento", - "shared_sortByExpiration": "Expiração", - "shared_search": "Pesquisar", - "shared_colName": "Nome", - "shared_colType": "Tipo", - "shared_colDateShared": "Data de compartilhamento", - "shared_colExpiration": "Expiração", - "shared_colPermissions": "Permissões", - "shared_colPassword": "Senha", - "shared_colActions": "Ações", - "shared_emptyStateTitle": "Nenhum recurso compartilhado ainda", - "shared_emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", - "shared_goToFiles": "Ir para arquivos", - "shared_typeFile": "Arquivo", - "shared_typeFolder": "Pasta", - "shared_noExpiration": "Sem expiração", - "shared_hasPassword": "Sim", - "shared_noPassword": "Não", - "shared_editShare": "Editar compartilhamento", - "shared_notifyShare": "Notificar alguém", - "shared_copyLink": "Copiar link", - "shared_removeShare": "Remover compartilhamento", - "shared_linkCopied": "Link copiado para a área de transferência!", - "shared_linkCopyFailed": "Falha ao copiar o link", - "shared_itemUpdated": "Configurações de compartilhamento atualizadas", - "shared_itemRemoved": "Compartilhamento removido com sucesso", - "shared_invalidEmail": "Por favor, insira um endereço de e-mail válido", - "shared_notificationSent": "Notificação enviada com sucesso", - "shared_notificationFailed": "Falha ao enviar a notificação" - }, - "files": { - "name": "Nome", - "type": "Tipo", - "size": "Tamanho", - "modified": "Modificado", - "no_files": "Nenhum arquivo nesta pasta", - "empty_hint": "Envie arquivos ou crie pastas para começar", - "loading": "Carregando arquivos…", - "view_grid": "Visualização em grade", - "view_list": "Visualização em lista", - "file_types": { - "document": "Documento", - "image": "Imagem", - "video": "Vídeo", - "audio": "Áudio", - "pdf": "PDF", - "text": "Texto", - "folder": "Pasta", - "spreadsheet": "Planilha", - "presentation": "Apresentação", - "archive": "Arquivo compactado", - "installer": "Instalador", - "code": "Código" - }, - "owner": "Proprietário" - }, - "dialogs": { - "rename_folder": "Renomear pasta", - "rename_file": "Renomear arquivo", - "new_name": "Novo nome", - "new_folder_title": "Nova pasta", - "folder_name": "Nome da pasta", - "folder_placeholder": "Minha pasta", - "rename_title": "Renomear", - "move_file": "Mover arquivo", - "move_folder": "Mover pasta", - "select_destination": "Selecione a pasta de destino:", - "root": "Raiz", - "delete_confirmation": "Tem certeza de que deseja excluir", - "and_contents": "e todo o seu conteúdo", - "no_undo": "Esta ação não pode ser desfeita", - "confirm_title": "Confirmar ação", - "confirm_delete": "Mover para a lixeira", - "confirm_delete_file": "Tem certeza de que deseja mover o arquivo \"{{name}}\" para a lixeira?", - "confirm_delete_folder": "Tem certeza de que deseja mover a pasta \"{{name}}\" e todo o seu conteúdo para a lixeira?", - "confirm_permanent_delete": "Excluir permanentemente", - "confirm_permanent_delete_msg": "Tem certeza de que deseja excluir permanentemente este item? Esta ação não pode ser desfeita.", - "confirm_empty_trash": "Esvaziar lixeira", - "confirm_delete_share": "Excluir link de compartilhamento", - "confirm_delete_share_msg": "Tem certeza de que deseja excluir este link de compartilhamento?", - "share_file": "Compartilhar arquivo", - "share_folder": "Compartilhar pasta", - "existing_shares": "Compartilhamentos existentes", - "share_options": "Opções de compartilhamento", - "password": "Senha", - "expiration": "Expiração", - "permissions": "Permissões", - "generated_link": "Link gerado", - "notify": "Enviar notificação", - "recipient": "Destinatário", - "message": "Mensagem", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Mover para a pasta inicial" - }, - "dropzone": { - "drag_files": "Arraste arquivos aqui ou clique para selecionar", - "drop_files": "Solte os arquivos para enviar" - }, - "permissions": { - "read": "Leitura", - "write": "Escrita", - "reshare": "Recompartilhar" - }, - "errors": { - "file_not_found": "Arquivo não encontrado", - "folder_not_found": "Pasta não encontrada", - "delete_error": "Erro ao excluir", - "upload_error": "Erro ao enviar o arquivo", - "rename_error": "Erro ao renomear", - "move_error": "Erro ao mover", - "empty_name": "O nome não pode estar vazio", - "name_exists": "Já existe um arquivo ou pasta com esse nome", - "generic_error": "Ocorreu um erro", - "group_name_invalid": "O nome do grupo deve seguir o formato de prefixo de e-mail (letras, dígitos, ponto, hífen, sublinhado; 1–64 caracteres).", - "group_cycle": "Este membro criaria uma referência circular entre grupos.", - "group_depth_exceeded": "Esta profundidade de aninhamento excede o máximo permitido (8).", - "group_virtual_immutable": "O grupo «Internal» é gerenciado pelo sistema e não pode ser modificado.", - "group_not_found": "Grupo não encontrado.", - "group_name_taken": "Já existe um grupo com este nome." - }, - "breadcrumb": { - "home": "Início" - }, - "trash": { - "empty_trash": "Esvaziar lixeira", - "empty_state": "A lixeira está vazia", - "original_location": "Local original", - "deleted_date": "Data de exclusão", - "remaining": "Restante", - "actions": "Ações", - "restore": "Restaurar", - "delete_permanently": "Excluir permanentemente", - "empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente.", - "groupby": { - "remaining_days": "Dias restantes", - "trashed_time": "Data de exclusão" - } - }, - "daysRemaining": { - "expired": "Expirado", - "today": "Hoje", - "tomorrow": "Amanhã", - "inDays": "{{count}} dias" - }, - "expiryChip": { - "never": "Nunca expira", - "expired": "Expirado", - "today": "Expira hoje", - "tomorrow": "Expira amanhã", - "inDays": "Expira em {{count}} dias", - "onDate": "Expira em {{date}}" - }, - "auth": { - "login_title": "Entrar", - "username": "Usuário", - "username_placeholder": "Digite seu nome de usuário", - "login_identifier": "Usuário ou e-mail", - "login_identifier_placeholder": "Digite seu usuário ou e-mail", - "password": "Senha", - "password_placeholder": "Digite sua senha", - "login_button": "Entrar", - "no_account": "Não tem uma conta?", - "register": "Cadastre-se", - "admin_setup": "Primeira vez?", - "setup": "Configurar administrador", - "register_title": "Criar conta", - "email": "E-mail", - "email_placeholder": "Digite seu e-mail", - "confirm_password": "Confirmar senha", - "confirm_password_placeholder": "Confirme sua senha", - "register_button": "Criar conta", - "have_account": "Já tem uma conta?", - "login": "Entrar", - "setup_title": "Configuração inicial", - "setup_step1": "Admin", - "setup_step2": "Sistema", - "setup_step3": "Concluído", - "admin_username": "Usuário administrador", - "admin_email": "E-mail do administrador", - "admin_password": "Senha do administrador", - "create_admin": "Criar administrador", - "back_to_login": "Já configurado?", - "admin_success": "Conta de administrador criada com sucesso! Agora você pode entrar.", - "account_success": "Conta criada com sucesso! Agora você pode entrar.", - "passwords_mismatch": "As senhas não coincidem", - "admin_create_error": "Erro ao criar conta de administrador", - "or": "ou", - "sso_login": "Entrar com SSO", - "sso_login_provider": "Entrar com {{provider}}", - "magicLinkHint": "Sem senha? Digite seu e-mail e enviaremos um link de acesso único.", - "magicLinkEmailLabel": "Endereço de e-mail", - "magicLinkEmailPlaceholder": "voce@exemplo.com", - "magicLinkSubmit": "Enviar link de acesso", - "magicLinkSent": "Se existir uma conta para este e-mail, um link de acesso foi enviado. Verifique sua caixa de entrada.", - "magicLinkUnavailable": "O acesso por e-mail não está disponível neste servidor.", - "magicLinkNetworkError": "Não foi possível conectar ao servidor: {{message}}", - "magicLinkToggle": "Sem palavra-passe? Receba um link por e-mail", - "passwordsMatch": "As palavras-passe coincidem", - "capsLock": "Caps Lock ativado" - }, - "storage": { - "title": "Armazenamento", - "calculating": "Calculando...", - "used": "{{percentage}}% usado ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Este tipo de arquivo não pode ser visualizado.", - "download_file": "Baixar arquivo", - "zoom_in": "Ampliar", - "zoom_out": "Reduzir", - "zoom_reset": "Redefinir zoom" - }, - "language_selector": { - "title": "Bem-vindo!", - "subtitle": "Selecione seu idioma para continuar", - "continue": "Continuar", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Nenhum favorito ainda", - "empty_hint": "Marque arquivos ou pastas com estrela para adicioná-los aos seus favoritos", - "add": "Adicionar aos favoritos", - "remove": "Remover dos favoritos", - "added_title": "Adicionado aos favoritos", - "added_msg": "adicionado aos favoritos", - "removed_title": "Removido dos favoritos", - "removed_msg": "removido dos favoritos" - }, - "recent": { - "title": "Recentes", - "clear": "Limpar recentes", - "accessed": "Acessado", - "empty_state": "Nenhum arquivo recente", - "empty_hint": "Os arquivos que você abrir aparecerão aqui", - "loadMore": "Carregar mais" - }, - "notifications": { - "file_renamed": "Arquivo renomeado", - "file_renamed_to": "Arquivo renomeado para \"{{name}}\"", - "folder_renamed": "Pasta renomeada", - "folder_renamed_to": "Pasta renomeada para \"{{name}}\"", - "file_uploaded": "Arquivo enviado", - "file_deleted": "Arquivo movido para a lixeira", - "folder_deleted": "Pasta movida para a lixeira", - "item_deleted_permanently": "Item excluído permanentemente", - "trash_emptied": "Lixeira esvaziada com sucesso", - "empty": "No notifications", - "title": "Notifications", - "link_created": "Link criado", - "share_success": "Link de partilha criado com sucesso", - "upload_files_section_title": "Upload não disponível aqui", - "upload_files_section_body": "Vá para a seção Arquivos para enviar arquivos" - }, - "batch": { - "one_selected": "1 item selecionado", - "n_selected": "{{count}} itens selecionados", - "confirm_delete": "Tem certeza de que deseja mover {{count}} itens para a lixeira?", - "move_title": "Mover {{count}} item(ns)", - "add_favorites": "Adicionar aos favoritos", - "move_copy": "Mover ou copiar" - }, - "admin": { - "page_title": "Painel de Administração", - "back_to_app": "Voltar ao OxiCloud", - "loading": "Carregando…", - "access_denied": "Acesso Negado", - "access_denied_desc": "Privilégios de administrador necessários.", - "sign_in": "Entrar", - "tab_dashboard": "Painel", - "tab_users": "Usuários", - "tab_oidc": "SSO / OIDC", - "total_users": "Total de Usuários", - "active_users": "Usuários Ativos", - "admins": "Admins", - "version": "Versão", - "storage_overview": "Visão do Armazenamento", - "used": "Usado", - "total_quota": "Cota Total", - "usage_pct": "Uso %", - "users_over_80": "Usuários >80% cota", - "users_over_quota": "Usuários acima da cota", - "system": "Sistema", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Cotas", - "enabled": "Habilitado", - "disabled": "Desabilitado", - "active": "Ativo", - "off": "Inativo", - "allow_registration": "Permitir registro público", - "registration_warning": "O registro público está desabilitado. Apenas administradores podem criar novos usuários.", - "user_management": "Gerenciamento de Usuários", - "create_user": "Criar Usuário", - "col_user": "Usuário", - "col_role": "Função", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Armazenamento", - "col_last_login": "Último Login", - "col_actions": "Ações", - "loading_users": "Carregando usuários…", - "failed_load_users": "Falha ao carregar", - "no_users_found": "Nenhum usuário encontrado", - "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", - "prev": "Anterior", - "next": "Próximo", - "inactive": "Inativo", - "you_badge": "(você)", - "local": "Local", - "never": "Nunca", - "just_now": "Agora mesmo", - "minutes_ago": "{{n}}min atrás", - "hours_ago": "{{n}}h atrás", - "days_ago": "{{n}}d atrás", - "edit_quota_title": "Editar cota", - "reset_password_title": "Redefinir senha", - "toggle_role_title": "Alternar função", - "deactivate_title": "Desativar", - "activate_title": "Ativar", - "delete_title": "Excluir", - "sso_title": "Login Único (OIDC / SSO)", - "enable_sso": "Habilitar autenticação SSO", - "provider_name": "Nome do Provedor", - "issuer_url": "URL do Emissor", - "issuer_url_hint": "URL do emissor OpenID Connect", - "auto_discover": "Auto-descoberta", - "discovering": "Descobrindo…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Deixe vazio para manter o valor atual", - "secret_configured": "Um client secret já está configurado", - "callback_url": "URL de Callback", - "callback_url_hint": "(registrar no seu IdP)", - "advanced_settings": "Configurações Avançadas", - "scopes": "Scopes", - "auto_provision": "Provisionar usuários automaticamente", - "admin_groups": "Grupos de Admin", - "admin_groups_hint": "Nomes de grupos OIDC separados por vírgula", - "disable_password": "Desabilitar login por senha (apenas OIDC)", - "password_warning": "Isso impedirá TODOS os logins por senha!", - "test_btn": "Testar", - "save_btn": "Salvar", - "saving": "Salvando…", - "settings_saved": "Configurações salvas — OIDC agora está {{status}}", - "quota_modal_title": "Atualizar Cota", - "quota_user_label": "Usuário:", - "new_quota": "Nova Cota", - "quota_unlimited_hint": "0 para ilimitado", - "cancel": "Cancelar", - "create_user_title": "Criar Novo Usuário", - "username_label": "Nome de usuário", - "username_placeholder": "joaosilva", - "username_hint": "3–32 caracteres", - "password_label": "Senha", - "password_placeholder": "Mín 8 caracteres", - "email_label": "E-mail", - "email_optional": "(opcional)", - "email_placeholder": "usuario@exemplo.com (gerado automaticamente se vazio)", - "role_label": "Função", - "role_user": "Usuário", - "role_admin": "Admin", - "quota_label": "Cota", - "creating": "Criando…", - "reset_pw_title": "Redefinir Senha", - "new_password_label": "Nova Senha", - "resetting": "Redefinindo…", - "reset_btn": "Redefinir", - "confirm_role_change": "Alterar função para {{role}}?", - "confirm_deactivate": "Tem certeza de que deseja desativar este usuário?", - "confirm_activate": "Tem certeza de que deseja ativar este usuário?", - "confirm_delete_user": "EXCLUIR usuário \"{{name}}\"? Não pode ser desfeito!", - "confirm_action": "Confirmar Ação", - "confirm_yes": "Confirmar", - "confirm_no": "Cancelar", - "error_username_short": "O nome de usuário deve ter pelo menos 3 caracteres", - "error_password_short": "A senha deve ter pelo menos 8 caracteres", - "error_generic": "Falha", - "error_network": "Erro de rede: {{message}}", - "error_create_user": "Falha ao criar usuário", - "tab_storage": "Armazenamento", - "storage_title": "Configuração de armazenamento", - "storage_current_backend": "Backend atual", - "storage_total_blobs": "Total de blobs", - "storage_total_size": "Tamanho total", - "storage_dedup_ratio": "Taxa de deduplicação", - "storage_backend": "Backend", - "storage_local": "Local", - "storage_s3": "Compatível com S3", - "storage_provider_preset": "Predefinição do fornecedor", - "storage_preset_custom": "Personalizado", - "storage_endpoint_url": "URL do endpoint", - "storage_endpoint_hint": "Deixar em branco para AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Região", - "storage_access_key": "Chave de acesso", - "storage_secret_key": "Chave secreta", - "storage_secret_configured": "Chave configurada", - "storage_key_placeholder": "Introduzir nova chave", - "storage_path_style": "Forçar estilo de caminho", - "storage_path_style_hint": "Necessário para MinIO e alguns serviços compatíveis com S3", - "storage_test_connection": "Testar ligação", - "storage_test_success": "Ligação bem-sucedida", - "storage_test_failure": "Falha na ligação", - "storage_save": "Guardar configuração", - "storage_saved": "Configuração guardada", - "storage_migration": "Migração de dados", - "storage_migration_coming_soon": "Ferramentas de migração em breve", - "migration_status_label": "Estado da migração", - "migration_start": "Iniciar migração", - "migration_pause": "Pausar", - "migration_resume": "Retomar", - "migration_verify": "Verificar", - "migration_complete": "Concluir", - "migration_started": "Migração iniciada", - "migration_paused_msg": "Migração pausada", - "migration_resumed_msg": "Migração retomada", - "migration_completed_msg": "Migração concluída com sucesso", - "migration_verifying": "A verificar...", - "migration_verify_passed": "Verificação aprovada", - "migration_verify_failed": "Verificação falhou", - "migration_failed_blobs": "Blobs com falha", - "testing": "A testar...", - "smtp_disabled": "Desativado (host não configurado)", - "smtp_enabled": "Ativado", - "smtp_enabled_label": "Estado", - "smtp_intro": "SMTP é configurado exclusivamente através de variáveis de ambiente (OXICLOUD_SMTP_*). Os valores abaixo são lidos do servidor em execução — para alterá-los, edite o ambiente e reinicie o OxiCloud.", - "smtp_not_configured": "SMTP não está configurado neste servidor.", - "smtp_send_failed": "Falha no envio.", - "smtp_send_test": "Enviar e-mail de teste", - "smtp_sending": "A enviar…", - "smtp_sent": "E-mail de teste enviado.", - "smtp_server_code": "Resposta do servidor", - "smtp_test_intro": "Envia uma mensagem de diagnóstico pré-definida para o destinatário abaixo e reporta a resposta do servidor SMTP, para que possa correlacioná-la com os registos do seu relay.", - "smtp_test_missing_to": "Introduza um endereço de destinatário.", - "smtp_test_title": "Enviar um e-mail de teste", - "smtp_test_to": "Endereço do destinatário", - "smtp_title": "E-mail de saída (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Perfil", - "back_to_app": "Voltar ao OxiCloud", - "loading": "Carregando…", - "not_authenticated": "Não Autenticado", - "not_authenticated_desc": "Faça login para ver seu perfil.", - "sign_in": "Entrar", - "role_admin": "Administrador", - "role_user": "Usuário", - "account_details": "Detalhes da Conta", - "username": "Nome de usuário", - "email": "E-mail", - "role": "Função", - "last_login": "Último login", - "storage": "Armazenamento", - "used": "Usado", - "quota": "Cota", - "usage": "Uso", - "unlimited": "Ilimitado", - "app_passwords": "Senhas de Aplicativo", - "app_pw_desc": "Gere senhas para clientes WebDAV, CalDAV e CardDAV. Cada senha é exibida apenas uma vez.", - "app_pw_label_placeholder": "Rótulo (ex. Thunderbird, macOS)", - "generate": "Gerar", - "generating": "Gerando…", - "new_password_for": "Nova senha para", - "copy_warning": "Copie esta senha agora. Você não poderá vê-la novamente.", - "copy_to_clipboard": "Copiar para área de transferência", - "col_label": "Rótulo", - "col_created": "Criado", - "col_last_used": "Último uso", - "col_status": "Status", - "active": "Ativa", - "revoked": "Revogada", - "revoke_title": "Revogar", - "no_app_passwords": "Nenhuma senha de aplicativo ainda.", - "client_sessions": "Sessões de cliente", - "client_sessions_desc": "Geradas automaticamente ao conectar um cliente compatível com Nextcloud.", - "col_client": "Cliente", - "never": "Nunca", - "just_now": "Agora mesmo", - "minutes_ago": "{{n}} min atrás", - "hours_ago": "{{n}}h atrás", - "days_ago": "{{n}} dias atrás", - "edit_profile": "Editar perfil", - "edit_oidc_managed": "Para alterar suas informações (nome, sobrenome, foto de perfil, …), atualize-as no seu provedor de identidade. As mudanças aparecerão no próximo login.", - "username_claim_hint": "De 2 a 64 caracteres, letras / dígitos / ponto / hífen / sublinhado. Uma vez escolhido, o nome de usuário não pode ser alterado (clientes DAV/NextCloud dependem dele).", - "username_already_claimed": "Nome de usuário definido e não pode ser alterado (clientes DAV/NextCloud dependem dele).", - "given_name": "Nome", - "family_name": "Sobrenome", - "notify_on_share": "Avisar-me por e-mail quando alguém compartilhar comigo", - "notify_on_share_hint": "Quando desmarcado, os compartilhamentos continuarão aparecendo na sua conta — você apenas não receberá um e-mail sobre eles.", - "save_profile": "Salvar alterações", - "profile_saved": "Perfil atualizado", - "profile_no_changes": "Sem alterações para salvar.", - "profile_save_failed": "Falha ao salvar", - "username_taken_error": "Este nome de usuário já está em uso.", - "username_immutable_error": "Seu nome de usuário já está definido e não pode ser alterado aqui. Contate um administrador se desejar renomeá-lo.", - "change_password": "Alterar Senha", - "current_password": "Senha Atual", - "new_password": "Nova Senha", - "min_8_chars": "Pelo menos 8 caracteres", - "confirm_password": "Confirmar Nova Senha", - "update_password": "Atualizar Senha", - "updating": "Atualizando…", - "password_updated": "Senha atualizada com sucesso", - "passwords_no_match": "As senhas não coincidem", - "password_too_short": "A senha deve ter pelo menos 8 caracteres", - "password_change_failed": "Falha ao alterar a senha", - "error_network": "Erro de rede: {{message}}", - "error_label_required": "Digite um rótulo", - "error_create_pw": "Falha ao criar senha de aplicativo", - "confirm_revoke": "Revogar senha \"{{label}}\"? Clientes que usam esta senha deixarão de funcionar.", - "error_revoke": "Falha ao revogar", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "A carregar...", - "files": "ficheiros", - "complete": "{{count}} / {{total}} carregados" - }, - "storage_quota_exceeded": "Cota de armazenamento excedida", - "sharedwithme": { - "pageTitle": "Compartilhado comigo", - "pageDescription": "Arquivos e pastas que outros usuários compartilharam com você", - "emptyStateTitle": "Nada compartilhado com você ainda", - "emptyStateDesc": "Itens compartilhados com você por outros usuários aparecerão aqui", - "loadMore": "Carregar mais", - "sharedBy": "Compartilhado por", - "colName": "Nome", - "colType": "Tipo", - "colSharedBy": "Compartilhado por", - "colDate": "Data de compartilhamento", - "colPermissions": "Permissões" - }, - "groupby": { - "none": "Nenhum", - "title": "Agrupar por", - "owner": "Proprietário", - "shareDate": "Data de partilha", - "type": "Tipo", - "type.folders": "Pastas", - "accessedAt": "Data de acesso", - "modifiedAt": "Data de modificação", - "createdAt": "Data de criação", - "size": "Tamanho", - "favoriteDate": "Data de favorito", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Novo" - }, - "dateBucket": { - "today": "Hoje", - "last7days": "Últimos 7 dias", - "last30days": "Últimos 30 dias" - }, - "groups": { - "title": "Gerenciar grupos", - "create_button": "Criar grupo", - "create_dialog_title": "Novo grupo", - "edit_dialog_title": "Renomear grupo", - "name_label": "Nome", - "name_placeholder": "engenharia", - "description_label": "Descrição (opcional)", - "members_section": "Membros", - "add_member_placeholder": "Adicionar um usuário ou grupo…", - "no_members": "Ainda não há membros.", - "remove_member": "Remover", - "delete_group": "Excluir grupo", - "delete_confirm": "Excluir o grupo \"{name}\"? As concessões que referenciam este grupo serão revogadas.", - "empty_state": "Ainda não há grupos.", - "load_more": "Carregar mais", - "back_to_list": "Voltar", - "loading": "Carregando…", - "virtual_badge": "Sistema", - "member_count_zero": "Sem membros", - "member_count_one": "1 membro", - "member_count_other": "{count} membros", - "delete_confirm_label": "Digite o nome do grupo para confirmar:", - "delete_confirm_mismatch": "Digite o nome do grupo exatamente para confirmar.", - "virtual_internal_name": "Interno", - "members_loading": "A carregar membros…", - "members_empty": "Sem membros", - "virtual_internal_explanation": "Todos os utilizadores internos neste servidor" - }, - "myshares": { - "copyLink": "Copiar link", - "deleteLink": "Eliminar link", - "notifyByEmail": "Notificar por e-mail", - "notifyFailed": "Não foi possível enviar a notificação.", - "notifyGroupMembers": "Notificar membros do grupo", - "notifyRateLimited": "Demasiadas notificações para este destinatário — tente novamente mais tarde.", - "removeAccess": "Remover acesso", - "resendInvitation": "Reenviar e-mail de convite" - }, - "sort": { - "asc": "ascendente", - "desc": "descendente" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", + "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra o OxiCloud para ver a sua nova partilha:\n{{login_link}}\n\nPode ter mais partilhas novas de {{inviter}} — inicie sessão para ver todos os itens partilhados consigo.\n\n— OxiCloud\n\nRecebeu esta mensagem porque tem uma conta OxiCloud e a preferência de notificação de partilhas está ativada. Pode desativá-la no seu perfil (Avisar-me por e-mail quando alguém compartilhar comigo)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Sistema de armazenamento em nuvem minimalista" + }, + "nav": { + "files": "Arquivos", + "shared": "Compartilhamentos", + "recent": "Recentes", + "favorites": "Favoritos", + "photos": "Fotos", + "music": "Música", + "trash": "Lixeira", + "sharedwithme": "Compartilhados comigo", + "profile": "Perfil", + "shared_with_me": "Compartilhados comigo" + }, + "photos": { + "empty_state": "Nenhuma foto ainda", + "empty_hint": "Envie imagens ou vídeos para vê-los aqui", + "items_selected": "selecionados", + "view_daily": "Dia", + "view_monthly": "Mês", + "view_yearly": "Ano", + "group_by": "Agrupar por" + }, + "music": { + "create_playlist": "Criar Playlist", + "playlists": "Playlists", + "no_playlists": "Nenhuma playlist ainda", + "select_playlist": "Selecione uma playlist", + "select_hint": "Escolha uma playlist na barra lateral ou crie uma nova", + "add_tracks": "Adicionar Faixas", + "no_tracks": "Nenhuma faixa nesta playlist", + "unknown_artist": "Artista Desconhecido", + "unknown_title": "Desconhecido", + "confirm_delete": "Excluir esta playlist?", + "playlist_name": "Nome da playlist", + "create": "Criar", + "delete": "Excluir", + "share": "Compartilhar", + "edit": "Editar", + "play_all": "Reproduzir Tudo", + "shuffle": "Aleatório", + "repeat": "Repetir", + "repeat_one": "Repetir Uma", + "queue": "Fila", + "queue_empty": "Fila vazia", + "not_playing": "Não reproduzindo", + "play": "Reproduzir", + "pause": "Pausar", + "previous": "Anterior", + "next": "Próximo", + "volume": "Volume", + "mute": "Mudo", + "unmute": "Ativar som", + "title": "Título", + "artist": "Artista", + "album": "Álbum", + "tracks": "faixas", + "add": "Adicionar", + "added": "Adicionado!", + "added_to_playlist": "adicionado à playlist", + "add_to_playlist": "Adicionar à playlist", + "load_error": "Erro ao carregar playlists", + "add_error": "Não foi possível adicionar as faixas", + "no_playlists_yet": "Nenhuma playlist ainda. Crie uma primeiro!", + "selected_files": "Selecionados:", + "error": "Erro", + "search_audio": "Pesquisar ficheiros de áudio…", + "no_audio_files": "Nenhum ficheiro de áudio encontrado", + "selected": "selecionados", + "loading": "A carregar…", + "search_error": "Não foi possível carregar os ficheiros de áudio", + "adding": "A adicionar…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "Anterior" + }, + "actions": { + "search": "Pesquisar arquivos...", + "new_folder": "Nova pasta", + "upload": "Enviar", + "upload_files": "Enviar arquivos", + "upload_folder": "Enviar pasta", + "upload.uploading": "Enviando...", + "upload.complete": "{count} / {total} enviados", + "upload.files": "arquivos", + "rename": "Renomear", + "move": "Mover para...", + "move_to": "Mover para", + "delete": "Excluir", + "download": "Baixar", + "view": "Visualizar", + "cancel": "Cancelar", + "confirm": "Confirmar", + "share": "Compartilhar", + "favorite": "Adicionar aos favoritos", + "unfavorite": "Remover dos favoritos", + "copy": "Copiar", + "notify": "Notificar", + "send": "Enviar", + "clear_recent": "Limpar recentes", + "logout": "Sair", + "create": "Criar", + "search_btn": "Pesquisar", + "close": "Fechar", + "delete_permanently": "Excluir permanentemente", + "empty_trash": "Esvaziar lixeira", + "open_parent_folder": "Ir para a pasta pai", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Aparência", + "about": "Sobre o OxiCloud", + "about_description": "Plataforma de armazenamento em nuvem construída com Rust e Arquitetura Limpa. Rápida, segura e privada.", + "admin_panel": "Painel de administração", + "profile": "Meu perfil", + "role_user": "Usuário", + "theme": { + "light": "Claro", + "dark": "Escuro", + "auto": "Como o sistema" + }, + "manage_groups": "Gerenciar grupos", + "admin": "Admin" + }, + "share": { + "dialogTitle": "Link de compartilhamento", + "linkLabel": "Link compartilhado:", + "copyLink": "Copiar", + "permissions": "Permissões:", + "permissionRead": "Leitura", + "permissionWrite": "Escrita", + "permissionReshare": "Recompartilhar", + "password": "Proteção por senha:", + "generatePassword": "Gerar", + "expiration": "Data de expiração:", + "update": "Atualizar compartilhamento", + "remove": "Remover compartilhamento", + "notifyTitle": "Enviar notificação", + "notifyEmailLabel": "Endereço de e-mail:", + "notifyMessageLabel": "Mensagem (opcional):", + "notifySend": "Enviar notificação", + "shareWithOthers": "Compartilhar com outros", + "sharePublicly": "Compartilhar publicamente", + "shareSettings": "Configurações de compartilhamento", + "shareCopied": "Link copiado para a área de transferência", + "shareCreated": "Link de compartilhamento criado com sucesso", + "shareUpdated": "Configurações de compartilhamento atualizadas", + "shareRemoved": "Compartilhamento removido com sucesso", + "inviteByEmail": "Convidar por e-mail — o convite será enviado", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Copiar", + "copy_failed": "Could not copy link", + "download": "Baixar", + "files": "Arquivos", + "folders": "Pastas", + "link_name": "Link name (optional)", + "notifyByEmail": "Notificar por e-mail", + "revoke": "Remove", + "role_label": "Função" + }, + "share_dialogTitle": "Link de compartilhamento", + "share_linkLabel": "Link compartilhado:", + "share_copyLink": "Copiar", + "share_permissions": "Permissões:", + "share_permissionRead": "Leitura", + "share_permissionWrite": "Escrita", + "share_permissionReshare": "Recompartilhar", + "share_password": "Proteção por senha:", + "share_generatePassword": "Gerar", + "share_expiration": "Data de expiração:", + "share_update": "Atualizar compartilhamento", + "share_remove": "Remover compartilhamento", + "share_notifyTitle": "Enviar notificação", + "share_notifyEmailLabel": "Endereço de e-mail:", + "share_notifyMessageLabel": "Mensagem (opcional):", + "share_notifySend": "Enviar notificação", + "shared": { + "backToFiles": "Voltar aos arquivos", + "pageTitle": "Recursos compartilhados", + "pageDescription": "Gerencie seus arquivos e pastas compartilhados", + "filterType": "Tipo:", + "filterAll": "Todos", + "filterFiles": "Arquivos", + "filterFolders": "Pastas", + "sortBy": "Ordenar por:", + "sortByName": "Nome", + "sortByDate": "Data de compartilhamento", + "sortByExpiration": "Expiração", + "search": "Pesquisar", + "colName": "Nome", + "colType": "Tipo", + "colDateShared": "Data de compartilhamento", + "colExpiration": "Expiração", + "colPermissions": "Permissões", + "colPassword": "Senha", + "colActions": "Ações", + "emptyStateTitle": "Nenhum recurso compartilhado ainda", + "emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", + "goToFiles": "Ir para arquivos", + "typeFile": "Arquivo", + "typeFolder": "Pasta", + "noExpiration": "Sem expiração", + "hasPassword": "Sim", + "noPassword": "Não", + "editShare": "Editar compartilhamento", + "notifyShare": "Notificar alguém", + "copyLink": "Copiar link", + "removeShare": "Remover compartilhamento", + "linkCopied": "Link copiado para a área de transferência!", + "linkCopyFailed": "Falha ao copiar o link", + "itemUpdated": "Configurações de compartilhamento atualizadas", + "itemRemoved": "Compartilhamento removido com sucesso", + "invalidEmail": "Por favor, insira um endereço de e-mail válido", + "notificationSent": "Notificação enviada com sucesso", + "notificationFailed": "Falha ao enviar a notificação", + "shared_backToFiles": "Voltar aos arquivos", + "shared_pageTitle": "Recursos compartilhados", + "shared_pageDescription": "Gerencie seus arquivos e pastas compartilhados", + "shared_filterType": "Tipo:", + "shared_filterAll": "Todos", + "shared_filterFiles": "Arquivos", + "shared_filterFolders": "Pastas", + "shared_sortBy": "Ordenar por:", + "shared_sortByName": "Nome", + "shared_sortByDate": "Data de compartilhamento", + "shared_sortByExpiration": "Expiração", + "shared_search": "Pesquisar", + "shared_colName": "Nome", + "shared_colType": "Tipo", + "shared_colDateShared": "Data de compartilhamento", + "shared_colExpiration": "Expiração", + "shared_colPermissions": "Permissões", + "shared_colPassword": "Senha", + "shared_colActions": "Ações", + "shared_emptyStateTitle": "Nenhum recurso compartilhado ainda", + "shared_emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", + "shared_goToFiles": "Ir para arquivos", + "shared_typeFile": "Arquivo", + "shared_typeFolder": "Pasta", + "shared_noExpiration": "Sem expiração", + "shared_hasPassword": "Sim", + "shared_noPassword": "Não", + "shared_editShare": "Editar compartilhamento", + "shared_notifyShare": "Notificar alguém", + "shared_copyLink": "Copiar link", + "shared_removeShare": "Remover compartilhamento", + "shared_linkCopied": "Link copiado para a área de transferência!", + "shared_linkCopyFailed": "Falha ao copiar o link", + "shared_itemUpdated": "Configurações de compartilhamento atualizadas", + "shared_itemRemoved": "Compartilhamento removido com sucesso", + "shared_invalidEmail": "Por favor, insira um endereço de e-mail válido", + "shared_notificationSent": "Notificação enviada com sucesso", + "shared_notificationFailed": "Falha ao enviar a notificação" + }, + "files": { + "name": "Nome", + "type": "Tipo", + "size": "Tamanho", + "modified": "Modificado", + "no_files": "Nenhum arquivo nesta pasta", + "empty_hint": "Envie arquivos ou crie pastas para começar", + "loading": "Carregando arquivos…", + "view_grid": "Visualização em grade", + "view_list": "Visualização em lista", + "file_types": { + "document": "Documento", + "image": "Imagem", + "video": "Vídeo", + "audio": "Áudio", + "pdf": "PDF", + "text": "Texto", + "folder": "Pasta", + "spreadsheet": "Planilha", + "presentation": "Apresentação", + "archive": "Arquivo compactado", + "installer": "Instalador", + "code": "Código" + }, + "owner": "Proprietário", + "add_favorites": "Adicionar aos favoritos", + "added_favorites": "Adicionado aos favoritos", + "col_name": "Nome", + "col_owner": "Proprietário", + "col_size": "Tamanho", + "col_type": "Tipo", + "copy": "Copiar", + "edit": "Editar", + "file": "Arquivo", + "folder": "Pasta", + "new_folder": "Nova pasta", + "share": "Compartilhar", + "view": "Visualizar" + }, + "dialogs": { + "rename_folder": "Renomear pasta", + "rename_file": "Renomear arquivo", + "new_name": "Novo nome", + "new_folder_title": "Nova pasta", + "folder_name": "Nome da pasta", + "folder_placeholder": "Minha pasta", + "rename_title": "Renomear", + "move_file": "Mover arquivo", + "move_folder": "Mover pasta", + "select_destination": "Selecione a pasta de destino:", + "root": "Raiz", + "delete_confirmation": "Tem certeza de que deseja excluir", + "and_contents": "e todo o seu conteúdo", + "no_undo": "Esta ação não pode ser desfeita", + "confirm_title": "Confirmar ação", + "confirm_delete": "Mover para a lixeira", + "confirm_delete_file": "Tem certeza de que deseja mover o arquivo \"{{name}}\" para a lixeira?", + "confirm_delete_folder": "Tem certeza de que deseja mover a pasta \"{{name}}\" e todo o seu conteúdo para a lixeira?", + "confirm_permanent_delete": "Excluir permanentemente", + "confirm_permanent_delete_msg": "Tem certeza de que deseja excluir permanentemente este item? Esta ação não pode ser desfeita.", + "confirm_empty_trash": "Esvaziar lixeira", + "confirm_delete_share": "Excluir link de compartilhamento", + "confirm_delete_share_msg": "Tem certeza de que deseja excluir este link de compartilhamento?", + "share_file": "Compartilhar arquivo", + "share_folder": "Compartilhar pasta", + "existing_shares": "Compartilhamentos existentes", + "share_options": "Opções de compartilhamento", + "password": "Senha", + "expiration": "Expiração", + "permissions": "Permissões", + "generated_link": "Link gerado", + "notify": "Enviar notificação", + "recipient": "Destinatário", + "message": "Mensagem", + "go_to_parent": ".. (parent folder)", + "no_subfolders": "No subfolders", + "select_this_folder": "Select this folder", + "move_to_home": "Mover para a pasta inicial" + }, + "dropzone": { + "drag_files": "Arraste arquivos aqui ou clique para selecionar", + "drop_files": "Solte os arquivos para enviar" + }, + "permissions": { + "read": "Leitura", + "write": "Escrita", + "reshare": "Recompartilhar" + }, + "errors": { + "file_not_found": "Arquivo não encontrado", + "folder_not_found": "Pasta não encontrada", + "delete_error": "Erro ao excluir", + "upload_error": "Erro ao enviar o arquivo", + "rename_error": "Erro ao renomear", + "move_error": "Erro ao mover", + "empty_name": "O nome não pode estar vazio", + "name_exists": "Já existe um arquivo ou pasta com esse nome", + "generic_error": "Ocorreu um erro", + "group_name_invalid": "O nome do grupo deve seguir o formato de prefixo de e-mail (letras, dígitos, ponto, hífen, sublinhado; 1–64 caracteres).", + "group_cycle": "Este membro criaria uma referência circular entre grupos.", + "group_depth_exceeded": "Esta profundidade de aninhamento excede o máximo permitido (8).", + "group_virtual_immutable": "O grupo «Internal» é gerenciado pelo sistema e não pode ser modificado.", + "group_not_found": "Grupo não encontrado.", + "group_name_taken": "Já existe um grupo com este nome." + }, + "breadcrumb": { + "home": "Início" + }, + "trash": { + "empty_trash": "Esvaziar lixeira", + "empty_state": "A lixeira está vazia", + "original_location": "Local original", + "deleted_date": "Data de exclusão", + "remaining": "Restante", + "actions": "Ações", + "restore": "Restaurar", + "delete_permanently": "Excluir permanentemente", + "empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente.", + "groupby": { + "remaining_days": "Dias restantes", + "trashed_time": "Data de exclusão" + }, + "delete": "Excluir permanentemente", + "empty_action": "Esvaziar lixeira" + }, + "daysRemaining": { + "expired": "Expirado", + "today": "Hoje", + "tomorrow": "Amanhã", + "inDays": "{{count}} dias" + }, + "expiryChip": { + "never": "Nunca expira", + "expired": "Expirado", + "today": "Expira hoje", + "tomorrow": "Expira amanhã", + "inDays": "Expira em {{count}} dias", + "onDate": "Expira em {{date}}" + }, + "auth": { + "login_title": "Entrar", + "username": "Usuário", + "username_placeholder": "Digite seu nome de usuário", + "login_identifier": "Usuário ou e-mail", + "login_identifier_placeholder": "Digite seu usuário ou e-mail", + "password": "Senha", + "password_placeholder": "Digite sua senha", + "login_button": "Entrar", + "no_account": "Não tem uma conta?", + "register": "Cadastre-se", + "admin_setup": "Primeira vez?", + "setup": "Configurar administrador", + "register_title": "Criar conta", + "email": "E-mail", + "email_placeholder": "Digite seu e-mail", + "confirm_password": "Confirmar senha", + "confirm_password_placeholder": "Confirme sua senha", + "register_button": "Criar conta", + "have_account": "Já tem uma conta?", + "login": "Entrar", + "setup_title": "Configuração inicial", + "setup_step1": "Admin", + "setup_step2": "Sistema", + "setup_step3": "Concluído", + "admin_username": "Usuário administrador", + "admin_email": "E-mail do administrador", + "admin_password": "Senha do administrador", + "create_admin": "Criar administrador", + "back_to_login": "Já configurado?", + "admin_success": "Conta de administrador criada com sucesso! Agora você pode entrar.", + "account_success": "Conta criada com sucesso! Agora você pode entrar.", + "passwords_mismatch": "As senhas não coincidem", + "admin_create_error": "Erro ao criar conta de administrador", + "or": "ou", + "sso_login": "Entrar com SSO", + "sso_login_provider": "Entrar com {{provider}}", + "magicLinkHint": "Sem senha? Digite seu e-mail e enviaremos um link de acesso único.", + "magicLinkEmailLabel": "Endereço de e-mail", + "magicLinkEmailPlaceholder": "voce@exemplo.com", + "magicLinkSubmit": "Enviar link de acesso", + "magicLinkSent": "Se existir uma conta para este e-mail, um link de acesso foi enviado. Verifique sua caixa de entrada.", + "magicLinkUnavailable": "O acesso por e-mail não está disponível neste servidor.", + "magicLinkNetworkError": "Não foi possível conectar ao servidor: {{message}}", + "magicLinkToggle": "Sem palavra-passe? Receba um link por e-mail", + "passwordsMatch": "As palavras-passe coincidem", + "capsLock": "Caps Lock ativado", + "caps_lock": "Caps Lock ativado", + "magic_email_label": "Endereço de e-mail", + "magic_hint": "Sem senha? Digite seu e-mail e enviaremos um link de acesso único.", + "magic_unavailable": "O acesso por e-mail não está disponível neste servidor.", + "passwords_match": "As palavras-passe coincidem", + "sign_in": "Entrar" + }, + "storage": { + "title": "Armazenamento", + "calculating": "Calculando...", + "used": "{{percentage}}% usado ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Este tipo de arquivo não pode ser visualizado.", + "download_file": "Baixar arquivo", + "zoom_in": "Ampliar", + "zoom_out": "Reduzir", + "zoom_reset": "Redefinir zoom" + }, + "language_selector": { + "title": "Bem-vindo!", + "subtitle": "Selecione seu idioma para continuar", + "continue": "Continuar", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "Nenhum favorito ainda", + "empty_hint": "Marque arquivos ou pastas com estrela para adicioná-los aos seus favoritos", + "add": "Adicionar aos favoritos", + "remove": "Remover dos favoritos", + "added_title": "Adicionado aos favoritos", + "added_msg": "adicionado aos favoritos", + "removed_title": "Removido dos favoritos", + "removed_msg": "removido dos favoritos" + }, + "recent": { + "title": "Recentes", + "clear": "Limpar recentes", + "accessed": "Acessado", + "empty_state": "Nenhum arquivo recente", + "empty_hint": "Os arquivos que você abrir aparecerão aqui", + "loadMore": "Carregar mais" + }, + "notifications": { + "file_renamed": "Arquivo renomeado", + "file_renamed_to": "Arquivo renomeado para \"{{name}}\"", + "folder_renamed": "Pasta renomeada", + "folder_renamed_to": "Pasta renomeada para \"{{name}}\"", + "file_uploaded": "Arquivo enviado", + "file_deleted": "Arquivo movido para a lixeira", + "folder_deleted": "Pasta movida para a lixeira", + "item_deleted_permanently": "Item excluído permanentemente", + "trash_emptied": "Lixeira esvaziada com sucesso", + "empty": "No notifications", + "title": "Notifications", + "link_created": "Link criado", + "share_success": "Link de partilha criado com sucesso", + "upload_files_section_title": "Upload não disponível aqui", + "upload_files_section_body": "Vá para a seção Arquivos para enviar arquivos" + }, + "batch": { + "one_selected": "1 item selecionado", + "n_selected": "{{count}} itens selecionados", + "confirm_delete": "Tem certeza de que deseja mover {{count}} itens para a lixeira?", + "move_title": "Mover {{count}} item(ns)", + "add_favorites": "Adicionar aos favoritos", + "move_copy": "Mover ou copiar" + }, + "admin": { + "page_title": "Painel de Administração", + "back_to_app": "Voltar ao OxiCloud", + "loading": "Carregando…", + "access_denied": "Acesso Negado", + "access_denied_desc": "Privilégios de administrador necessários.", + "sign_in": "Entrar", + "tab_dashboard": "Painel", + "tab_users": "Usuários", + "tab_oidc": "SSO / OIDC", + "total_users": "Total de Usuários", + "active_users": "Usuários Ativos", + "admins": "Admins", + "version": "Versão", + "storage_overview": "Visão do Armazenamento", + "used": "Usado", + "total_quota": "Cota Total", + "usage_pct": "Uso %", + "users_over_80": "Usuários >80% cota", + "users_over_quota": "Usuários acima da cota", + "system": "Sistema", + "auth_label": "Auth", + "oidc_label": "OIDC", + "quotas_label": "Cotas", + "enabled": "Habilitado", + "disabled": "Desabilitado", + "active": "Ativo", + "off": "Inativo", + "allow_registration": "Permitir registro público", + "registration_warning": "O registro público está desabilitado. Apenas administradores podem criar novos usuários.", + "user_management": "Gerenciamento de Usuários", + "create_user": "Criar Usuário", + "col_user": "Usuário", + "col_role": "Função", + "col_auth": "Auth", + "col_status": "Status", + "col_storage": "Armazenamento", + "col_last_login": "Último Login", + "col_actions": "Ações", + "loading_users": "Carregando usuários…", + "failed_load_users": "Falha ao carregar", + "no_users_found": "Nenhum usuário encontrado", + "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", + "prev": "Anterior", + "next": "Próximo", + "inactive": "Inativo", + "you_badge": "(você)", + "local": "Local", + "never": "Nunca", + "just_now": "Agora mesmo", + "minutes_ago": "{{n}}min atrás", + "hours_ago": "{{n}}h atrás", + "days_ago": "{{n}}d atrás", + "edit_quota_title": "Editar cota", + "reset_password_title": "Redefinir senha", + "toggle_role_title": "Alternar função", + "deactivate_title": "Desativar", + "activate_title": "Ativar", + "delete_title": "Excluir", + "sso_title": "Login Único (OIDC / SSO)", + "enable_sso": "Habilitar autenticação SSO", + "provider_name": "Nome do Provedor", + "issuer_url": "URL do Emissor", + "issuer_url_hint": "URL do emissor OpenID Connect", + "auto_discover": "Auto-descoberta", + "discovering": "Descobrindo…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Deixe vazio para manter o valor atual", + "secret_configured": "Um client secret já está configurado", + "callback_url": "URL de Callback", + "callback_url_hint": "(registrar no seu IdP)", + "advanced_settings": "Configurações Avançadas", + "scopes": "Scopes", + "auto_provision": "Provisionar usuários automaticamente", + "admin_groups": "Grupos de Admin", + "admin_groups_hint": "Nomes de grupos OIDC separados por vírgula", + "disable_password": "Desabilitar login por senha (apenas OIDC)", + "password_warning": "Isso impedirá TODOS os logins por senha!", + "test_btn": "Testar", + "save_btn": "Salvar", + "saving": "Salvando…", + "settings_saved": "Configurações salvas — OIDC agora está {{status}}", + "quota_modal_title": "Atualizar Cota", + "quota_user_label": "Usuário:", + "new_quota": "Nova Cota", + "quota_unlimited_hint": "0 para ilimitado", + "cancel": "Cancelar", + "create_user_title": "Criar Novo Usuário", + "username_label": "Nome de usuário", + "username_placeholder": "joaosilva", + "username_hint": "3–32 caracteres", + "password_label": "Senha", + "password_placeholder": "Mín 8 caracteres", + "email_label": "E-mail", + "email_optional": "(opcional)", + "email_placeholder": "usuario@exemplo.com (gerado automaticamente se vazio)", + "role_label": "Função", + "role_user": "Usuário", + "role_admin": "Admin", + "quota_label": "Cota", + "creating": "Criando…", + "reset_pw_title": "Redefinir Senha", + "new_password_label": "Nova Senha", + "resetting": "Redefinindo…", + "reset_btn": "Redefinir", + "confirm_role_change": "Alterar função para {{role}}?", + "confirm_deactivate": "Tem certeza de que deseja desativar este usuário?", + "confirm_activate": "Tem certeza de que deseja ativar este usuário?", + "confirm_delete_user": "EXCLUIR usuário \"{{name}}\"? Não pode ser desfeito!", + "confirm_action": "Confirmar Ação", + "confirm_yes": "Confirmar", + "confirm_no": "Cancelar", + "error_username_short": "O nome de usuário deve ter pelo menos 3 caracteres", + "error_password_short": "A senha deve ter pelo menos 8 caracteres", + "error_generic": "Falha", + "error_network": "Erro de rede: {{message}}", + "error_create_user": "Falha ao criar usuário", + "tab_storage": "Armazenamento", + "storage_title": "Configuração de armazenamento", + "storage_current_backend": "Backend atual", + "storage_total_blobs": "Total de blobs", + "storage_total_size": "Tamanho total", + "storage_dedup_ratio": "Taxa de deduplicação", + "storage_backend": "Backend", + "storage_local": "Local", + "storage_s3": "Compatível com S3", + "storage_provider_preset": "Predefinição do fornecedor", + "storage_preset_custom": "Personalizado", + "storage_endpoint_url": "URL do endpoint", + "storage_endpoint_hint": "Deixar em branco para AWS S3", + "storage_bucket": "Bucket", + "storage_region": "Região", + "storage_access_key": "Chave de acesso", + "storage_secret_key": "Chave secreta", + "storage_secret_configured": "Chave configurada", + "storage_key_placeholder": "Introduzir nova chave", + "storage_path_style": "Forçar estilo de caminho", + "storage_path_style_hint": "Necessário para MinIO e alguns serviços compatíveis com S3", + "storage_test_connection": "Testar ligação", + "storage_test_success": "Ligação bem-sucedida", + "storage_test_failure": "Falha na ligação", + "storage_save": "Guardar configuração", + "storage_saved": "Configuração guardada", + "storage_migration": "Migração de dados", + "storage_migration_coming_soon": "Ferramentas de migração em breve", + "migration_status_label": "Estado da migração", + "migration_start": "Iniciar migração", + "migration_pause": "Pausar", + "migration_resume": "Retomar", + "migration_verify": "Verificar", + "migration_complete": "Concluir", + "migration_started": "Migração iniciada", + "migration_paused_msg": "Migração pausada", + "migration_resumed_msg": "Migração retomada", + "migration_completed_msg": "Migração concluída com sucesso", + "migration_verifying": "A verificar...", + "migration_verify_passed": "Verificação aprovada", + "migration_verify_failed": "Verificação falhou", + "migration_failed_blobs": "Blobs com falha", + "testing": "A testar...", + "smtp_disabled": "Desativado (host não configurado)", + "smtp_enabled": "Ativado", + "smtp_enabled_label": "Estado", + "smtp_intro": "SMTP é configurado exclusivamente através de variáveis de ambiente (OXICLOUD_SMTP_*). Os valores abaixo são lidos do servidor em execução — para alterá-los, edite o ambiente e reinicie o OxiCloud.", + "smtp_not_configured": "SMTP não está configurado neste servidor.", + "smtp_send_failed": "Falha no envio.", + "smtp_send_test": "Enviar e-mail de teste", + "smtp_sending": "A enviar…", + "smtp_sent": "E-mail de teste enviado.", + "smtp_server_code": "Resposta do servidor", + "smtp_test_intro": "Envia uma mensagem de diagnóstico pré-definida para o destinatário abaixo e reporta a resposta do servidor SMTP, para que possa correlacioná-la com os registos do seu relay.", + "smtp_test_missing_to": "Introduza um endereço de destinatário.", + "smtp_test_title": "Enviar um e-mail de teste", + "smtp_test_to": "Endereço do destinatário", + "smtp_title": "E-mail de saída (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Admins", + "confirm_role": "Alterar função para {{role}}?", + "dashboard": "Painel", + "email": "E-mail", + "mig_complete": "Concluir", + "mig_pause": "Pausar", + "mig_resume": "Retomar", + "mig_verify_failed": "Verificação falhou", + "mig_verify_passed": "Verificação aprovada", + "mig_verifying": "A verificar...", + "oidc_auto_provision": "Provisionar usuários automaticamente", + "oidc_callback": "URL de Callback", + "oidc_client_id": "Client ID", + "oidc_disable_pw": "Desabilitar login por senha (apenas OIDC)", + "oidc_issuer": "URL do Emissor", + "oidc_scopes": "Scopes", + "password": "Senha", + "quotas": "Cotas", + "reset_pw_for": "Nova senha para", + "role": "Função", + "smtp_fail": "Falha no envio.", + "smtp_send": "Enviar", + "smtp_test": "Enviar e-mail de teste", + "smtp_user_state": "Auth", + "status": "Status", + "storage": "Armazenamento", + "storage_endpoint": "URL do endpoint", + "storage_tab": "Armazenamento", + "time_min_ago": "{{n}} min atrás", + "title": "Admin", + "user": "Usuário", + "username": "Nome de usuário", + "users": "Usuários" + }, + "profile": { + "page_title": "Perfil", + "back_to_app": "Voltar ao OxiCloud", + "loading": "Carregando…", + "not_authenticated": "Não Autenticado", + "not_authenticated_desc": "Faça login para ver seu perfil.", + "sign_in": "Entrar", + "role_admin": "Administrador", + "role_user": "Usuário", + "account_details": "Detalhes da Conta", + "username": "Nome de usuário", + "email": "E-mail", + "role": "Função", + "last_login": "Último login", + "storage": "Armazenamento", + "used": "Usado", + "quota": "Cota", + "usage": "Uso", + "unlimited": "Ilimitado", + "app_passwords": "Senhas de Aplicativo", + "app_pw_desc": "Gere senhas para clientes WebDAV, CalDAV e CardDAV. Cada senha é exibida apenas uma vez.", + "app_pw_label_placeholder": "Rótulo (ex. Thunderbird, macOS)", + "generate": "Gerar", + "generating": "Gerando…", + "new_password_for": "Nova senha para", + "copy_warning": "Copie esta senha agora. Você não poderá vê-la novamente.", + "copy_to_clipboard": "Copiar para área de transferência", + "col_label": "Rótulo", + "col_created": "Criado", + "col_last_used": "Último uso", + "col_status": "Status", + "active": "Ativa", + "revoked": "Revogada", + "revoke_title": "Revogar", + "no_app_passwords": "Nenhuma senha de aplicativo ainda.", + "client_sessions": "Sessões de cliente", + "client_sessions_desc": "Geradas automaticamente ao conectar um cliente compatível com Nextcloud.", + "col_client": "Cliente", + "never": "Nunca", + "just_now": "Agora mesmo", + "minutes_ago": "{{n}} min atrás", + "hours_ago": "{{n}}h atrás", + "days_ago": "{{n}} dias atrás", + "edit_profile": "Editar perfil", + "edit_oidc_managed": "Para alterar suas informações (nome, sobrenome, foto de perfil, …), atualize-as no seu provedor de identidade. As mudanças aparecerão no próximo login.", + "username_claim_hint": "De 2 a 64 caracteres, letras / dígitos / ponto / hífen / sublinhado. Uma vez escolhido, o nome de usuário não pode ser alterado (clientes DAV/NextCloud dependem dele).", + "username_already_claimed": "Nome de usuário definido e não pode ser alterado (clientes DAV/NextCloud dependem dele).", + "given_name": "Nome", + "family_name": "Sobrenome", + "notify_on_share": "Avisar-me por e-mail quando alguém compartilhar comigo", + "notify_on_share_hint": "Quando desmarcado, os compartilhamentos continuarão aparecendo na sua conta — você apenas não receberá um e-mail sobre eles.", + "save_profile": "Salvar alterações", + "profile_saved": "Perfil atualizado", + "profile_no_changes": "Sem alterações para salvar.", + "profile_save_failed": "Falha ao salvar", + "username_taken_error": "Este nome de usuário já está em uso.", + "username_immutable_error": "Seu nome de usuário já está definido e não pode ser alterado aqui. Contate um administrador se desejar renomeá-lo.", + "change_password": "Alterar Senha", + "current_password": "Senha Atual", + "new_password": "Nova Senha", + "min_8_chars": "Pelo menos 8 caracteres", + "confirm_password": "Confirmar Nova Senha", + "update_password": "Atualizar Senha", + "updating": "Atualizando…", + "password_updated": "Senha atualizada com sucesso", + "passwords_no_match": "As senhas não coincidem", + "password_too_short": "A senha deve ter pelo menos 8 caracteres", + "password_change_failed": "Falha ao alterar a senha", + "error_network": "Erro de rede: {{message}}", + "error_label_required": "Digite um rótulo", + "error_create_pw": "Falha ao criar senha de aplicativo", + "confirm_revoke": "Revogar senha \"{{label}}\"? Clientes que usam esta senha deixarão de funcionar.", + "error_revoke": "Falha ao revogar", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "As senhas não coincidem" + }, + "upload": { + "uploading": "A carregar...", + "files": "ficheiros", + "complete": "{{count}} / {{total}} carregados" + }, + "storage_quota_exceeded": "Cota de armazenamento excedida", + "sharedwithme": { + "pageTitle": "Compartilhado comigo", + "pageDescription": "Arquivos e pastas que outros usuários compartilharam com você", + "emptyStateTitle": "Nada compartilhado com você ainda", + "emptyStateDesc": "Itens compartilhados com você por outros usuários aparecerão aqui", + "loadMore": "Carregar mais", + "sharedBy": "Compartilhado por", + "colName": "Nome", + "colType": "Tipo", + "colSharedBy": "Compartilhado por", + "colDate": "Data de compartilhamento", + "colPermissions": "Permissões" + }, + "groupby": { + "none": "Nenhum", + "title": "Agrupar por", + "owner": "Proprietário", + "shareDate": "Data de partilha", + "type": "Tipo", + "type.folders": "Pastas", + "accessedAt": "Data de acesso", + "modifiedAt": "Data de modificação", + "createdAt": "Data de criação", + "size": "Tamanho", + "favoriteDate": "Data de favorito", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Novo", + "folders": "Pastas" + }, + "dateBucket": { + "today": "Hoje", + "last7days": "Últimos 7 dias", + "last30days": "Últimos 30 dias", + "unknown": "Desconhecido" + }, + "groups": { + "title": "Gerenciar grupos", + "create_button": "Criar grupo", + "create_dialog_title": "Novo grupo", + "edit_dialog_title": "Renomear grupo", + "name_label": "Nome", + "name_placeholder": "engenharia", + "description_label": "Descrição (opcional)", + "members_section": "Membros", + "add_member_placeholder": "Adicionar um usuário ou grupo…", + "no_members": "Ainda não há membros.", + "remove_member": "Remover", + "delete_group": "Excluir grupo", + "delete_confirm": "Excluir o grupo \"{name}\"? As concessões que referenciam este grupo serão revogadas.", + "empty_state": "Ainda não há grupos.", + "load_more": "Carregar mais", + "back_to_list": "Voltar", + "loading": "Carregando…", + "virtual_badge": "Sistema", + "member_count_zero": "Sem membros", + "member_count_one": "1 membro", + "member_count_other": "{count} membros", + "delete_confirm_label": "Digite o nome do grupo para confirmar:", + "delete_confirm_mismatch": "Digite o nome do grupo exatamente para confirmar.", + "virtual_internal_name": "Interno", + "members_loading": "A carregar membros…", + "members_empty": "Sem membros", + "virtual_internal_explanation": "Todos os utilizadores internos neste servidor", + "create": "Criar grupo", + "empty": "Ainda não há grupos.", + "members": "Membros" + }, + "myshares": { + "copyLink": "Copiar link", + "deleteLink": "Eliminar link", + "notifyByEmail": "Notificar por e-mail", + "notifyFailed": "Não foi possível enviar a notificação.", + "notifyGroupMembers": "Notificar membros do grupo", + "notifyRateLimited": "Demasiadas notificações para este destinatário — tente novamente mais tarde.", + "removeAccess": "Remover acesso", + "resendInvitation": "Reenviar e-mail de convite", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascendente", + "desc": "descendente" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Áudio", + "code": "Código", + "text": "Texto" + }, + "common": { + "add": "Adicionar", + "cancel": "Cancelar", + "clear": "Clear", + "close": "Fechar", + "confirm": "Confirmar", + "copy": "Copiar", + "create": "Criar", + "delete": "Excluir", + "download": "Baixar", + "load_more": "Carregar mais", + "loading": "A carregar…", + "next": "Próximo", + "no": "Não", + "previous": "Anterior", + "remove": "Remove", + "rename": "Renomear", + "save": "Salvar", + "search": "Pesquisar", + "yes": "Sim" + }, + "device": { + "continue": "Continuar", + "unknown": "Desconhecido" + }, + "expiryBucket": { + "expired": "Expirado", + "noExpiry": "Sem expiração", + "today": "Hoje", + "tomorrow": "Amanhã" + }, + "nextcloud": { + "error_title": "Erro", + "sign_in_with": "Entrar com {{provider}}" + }, + "search": { + "size_label": "Tamanho", + "title": "Pesquisar", + "type": { + "audio": "Áudio" + }, + "type_label": "Tipo" + }, + "sizeBucket": { + "folders": "Pastas" + }, + "view": { + "grid": "Visualização em grade", + "list": "Visualização em lista" + } } diff --git a/frontend/static/locales/ru.json b/frontend/static/locales/ru.json index 5f72caf0..3742c536 100644 --- a/frontend/static/locales/ru.json +++ b/frontend/static/locales/ru.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "Эта ссылка для входа больше не действительна", - "expired_body": "Срок действия ссылки мог истечь, или она уже была использована. Мы можем отправить вам новую — она придёт в ваш почтовый ящик через несколько секунд.", - "resend_to": "Отправить новую ссылку на {{email}}", - "generic_unavailable": "Эта ссылка для входа больше не действительна. Возможно, она уже использовалась или срок её действия истёк. Запросите новую ссылку на странице входа.", - "service_unavailable": "Вход по magic-ссылке не включён на этом сервере.", - "internal_error": "При входе произошла ошибка. Пожалуйста, попробуйте ещё раз.", - "resend_failure": "При отправке ссылки произошла ошибка. Пожалуйста, попробуйте ещё раз.", - "cross_browser_title": "Продолжить вход на этом устройстве?", - "cross_browser_body": "Вы открыли эту ссылку для входа в браузере или на устройстве, отличном от того, где она была запрошена.", - "cross_browser_warning": "Если эту ссылку запросили вы, можно безопасно продолжить. В противном случае закройте эту страницу — нажатие «Продолжить» приведёт к входу другого человека в вашу учётную запись.", - "cross_browser_continue": "Продолжить и войти", - "resend_confirmation_title": "Проверьте ваш почтовый ящик", - "resend_confirmation_body": "Если ссылка для входа принадлежала активной учётной записи, новая ссылка только что была отправлена. Пожалуйста, проверьте ваш почтовый ящик.", - "return_link": "Вернуться в OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", - "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте, нажав на ссылку ниже:\n{{link}}\n\nСсылка работает один раз и истекает через {{ttl_hours}} часов.\nЕсли вы не ожидали этого приглашения, можете спокойно проигнорировать это сообщение.\n\n— OxiCloud" - }, - "login": { - "subject": "Вход в OxiCloud", - "body": "Здравствуйте,\n\nИспользуйте ссылку ниже, чтобы войти в OxiCloud. Ссылка работает один раз и истекает через {{ttl_minutes}} минут. Откройте её на том же устройстве, где вы её запросили.\n\n{{link}}\n\nЕсли вы не запрашивали эту ссылку для входа, можете спокойно проигнорировать это сообщение — никаких дальнейших действий не требуется.\n\n— OxiCloud" - }, - "kind_file": "файл", - "kind_folder": "папку", - "english_fallback_divider": "--- Английская версия ниже ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "Эта ссылка для входа больше не действительна", + "expired_body": "Срок действия ссылки мог истечь, или она уже была использована. Мы можем отправить вам новую — она придёт в ваш почтовый ящик через несколько секунд.", + "resend_to": "Отправить новую ссылку на {{email}}", + "generic_unavailable": "Эта ссылка для входа больше не действительна. Возможно, она уже использовалась или срок её действия истёк. Запросите новую ссылку на странице входа.", + "service_unavailable": "Вход по magic-ссылке не включён на этом сервере.", + "internal_error": "При входе произошла ошибка. Пожалуйста, попробуйте ещё раз.", + "resend_failure": "При отправке ссылки произошла ошибка. Пожалуйста, попробуйте ещё раз.", + "cross_browser_title": "Продолжить вход на этом устройстве?", + "cross_browser_body": "Вы открыли эту ссылку для входа в браузере или на устройстве, отличном от того, где она была запрошена.", + "cross_browser_warning": "Если эту ссылку запросили вы, можно безопасно продолжить. В противном случае закройте эту страницу — нажатие «Продолжить» приведёт к входу другого человека в вашу учётную запись.", + "cross_browser_continue": "Продолжить и войти", + "resend_confirmation_title": "Проверьте ваш почтовый ящик", + "resend_confirmation_body": "Если ссылка для входа принадлежала активной учётной записи, новая ссылка только что была отправлена. Пожалуйста, проверьте ваш почтовый ящик.", + "return_link": "Вернуться в OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", + "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте, нажав на ссылку ниже:\n{{link}}\n\nСсылка работает один раз и истекает через {{ttl_hours}} часов.\nЕсли вы не ожидали этого приглашения, можете спокойно проигнорировать это сообщение.\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", - "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте OxiCloud, чтобы увидеть новый общий ресурс:\n{{login_link}}\n\nВозможно, у вас есть и другие новые общие ресурсы от {{inviter}} — войдите, чтобы увидеть все элементы, которыми с вами поделились.\n\n— OxiCloud\n\nВы получаете это сообщение, потому что у вас есть учётная запись OxiCloud и предпочтение уведомлений об общих ресурсах включено. Вы можете отключить его в своём профиле (Уведомлять меня по электронной почте, когда кто-то делится со мной)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Минималистичная система облачного хранения" - }, - "nav": { - "files": "Файлы", - "shared": "Общие", - "recent": "Недавние", - "favorites": "Избранное", - "photos": "Фото", - "music": "Музыка", - "trash": "Корзина", - "sharedwithme": "Доступно мне" - }, - "photos": { - "empty_state": "Фотографий пока нет", - "empty_hint": "Загрузите изображения или видео, чтобы увидеть их здесь", - "items_selected": "выбрано", - "view_daily": "День", - "view_monthly": "Месяц", - "view_yearly": "Год" - }, - "music": { - "create_playlist": "Создать плейлист", - "playlists": "Плейлисты", - "no_playlists": "Плейлистов пока нет", - "select_playlist": "Выберите плейлист", - "select_hint": "Выберите плейлист на панели слева или создайте новый", - "add_tracks": "Добавить треки", - "no_tracks": "В этом плейлисте нет треков", - "unknown_artist": "Неизвестный исполнитель", - "unknown_title": "Неизвестно", - "confirm_delete": "Удалить этот плейлист?", - "playlist_name": "Название плейлиста", - "create": "Создать", - "delete": "Удалить", - "share": "Поделиться", - "edit": "Редактировать", - "play_all": "Воспроизвести все", - "shuffle": "Перемешать", - "repeat": "Повтор", - "repeat_one": "Повторять один", - "queue": "Очередь", - "queue_empty": "Очередь пуста", - "not_playing": "Ничего не играет", - "play": "Воспроизвести", - "pause": "Пауза", - "previous": "Предыдущий", - "next": "Следующий", - "volume": "Громкость", - "mute": "Выключить звук", - "unmute": "Включить звук", - "title": "Название", - "artist": "Исполнитель", - "album": "Альбом", - "tracks": "треков", - "add": "Добавить", - "added": "Добавлено!", - "added_to_playlist": "добавлен в плейлист", - "add_to_playlist": "Добавить в плейлист", - "load_error": "Ошибка загрузки плейлистов", - "add_error": "Не удалось добавить треки в плейлист", - "no_playlists_yet": "Плейлистов пока нет. Создайте сначала!", - "selected_files": "Выбрано:", - "error": "Ошибка", - "search_audio": "Поиск аудиофайлов…", - "no_audio_files": "Аудиофайлы не найдены", - "selected": "выбрано", - "loading": "Загрузка…", - "search_error": "Не удалось загрузить аудиофайлы", - "adding": "Добавление…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Поиск файлов...", - "new_folder": "Новая папка", - "upload": "Загрузить", - "upload_files": "Загрузить файлы", - "upload_folder": "Загрузить папку", - "upload.uploading": "Загрузка...", - "upload.complete": "{count} / {total} загружено", - "upload.files": "файлов", - "rename": "Переименовать", - "move": "Переместить в...", - "move_to": "Переместить в", - "delete": "Удалить", - "download": "Скачать", - "view": "Просмотр", - "cancel": "Отмена", - "confirm": "Подтвердить", - "share": "Поделиться", - "favorite": "В избранное", - "unfavorite": "Из избранного", - "copy": "Копировать", - "notify": "Уведомить", - "send": "Отправить", - "clear_recent": "Очистить недавние", - "logout": "Выйти", - "create": "Создать", - "search_btn": "Найти", - "close": "Закрыть", - "delete_permanently": "Удалить навсегда", - "empty_trash": "Очистить корзину", - "open_parent_folder": "Перейти в родительскую папку", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Оформление", - "about": "О OxiCloud", - "about_description": "Платформа облачного хранения на Rust с чистой архитектурой. Быстрая, безопасная и конфиденциальная.", - "admin_panel": "Панель администратора", - "profile": "Мой профиль", - "role_user": "Пользователь", - "theme": { - "light": "Светлая", - "dark": "Тёмная", - "auto": "Как в системе" + "login": { + "subject": "Вход в OxiCloud", + "body": "Здравствуйте,\n\nИспользуйте ссылку ниже, чтобы войти в OxiCloud. Ссылка работает один раз и истекает через {{ttl_minutes}} минут. Откройте её на том же устройстве, где вы её запросили.\n\n{{link}}\n\nЕсли вы не запрашивали эту ссылку для входа, можете спокойно проигнорировать это сообщение — никаких дальнейших действий не требуется.\n\n— OxiCloud" }, - "manage_groups": "Управление группами" + "kind_file": "файл", + "kind_folder": "папку", + "english_fallback_divider": "--- Английская версия ниже ---" + } }, - "share": { - "dialogTitle": "Ссылка для обмена", - "linkLabel": "Ссылка:", - "copyLink": "Копировать", - "permissions": "Разрешения:", - "permissionRead": "Чтение", - "permissionWrite": "Запись", - "permissionReshare": "Пересылка", - "password": "Защита паролем:", - "generatePassword": "Сгенерировать", - "expiration": "Срок действия:", - "update": "Обновить общий доступ", - "remove": "Удалить общий доступ", - "notifyTitle": "Отправить уведомление", - "notifyEmailLabel": "Адрес email:", - "notifyMessageLabel": "Сообщение (необязательно):", - "notifySend": "Отправить уведомление", - "shareWithOthers": "Поделиться с другими", - "sharePublicly": "Общий доступ", - "shareSettings": "Настройки общего доступа", - "shareCopied": "Ссылка скопирована в буфер обмена", - "shareCreated": "Ссылка для общего доступа успешно создана", - "shareUpdated": "Настройки общего доступа успешно обновлены", - "shareRemoved": "Общий доступ успешно удалён", - "inviteByEmail": "Пригласить по e-mail — приглашение будет отправлено", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Ссылка для обмена", - "share_linkLabel": "Ссылка:", - "share_copyLink": "Копировать", - "share_permissions": "Разрешения:", - "share_permissionRead": "Чтение", - "share_permissionWrite": "Запись", - "share_permissionReshare": "Пересылка", - "share_password": "Защита паролем:", - "share_generatePassword": "Сгенерировать", - "share_expiration": "Срок действия:", - "share_update": "Обновить общий доступ", - "share_remove": "Удалить общий доступ", - "share_notifyTitle": "Отправить уведомление", - "share_notifyEmailLabel": "Адрес email:", - "share_notifyMessageLabel": "Сообщение (необязательно):", - "share_notifySend": "Отправить уведомление", - "shared": { - "backToFiles": "Назад к файлам", - "pageTitle": "Общие ресурсы", - "pageDescription": "Управление общими файлами и папками", - "filterType": "Тип:", - "filterAll": "Все", - "filterFiles": "Файлы", - "filterFolders": "Папки", - "sortBy": "Сортировка:", - "sortByName": "Имя", - "sortByDate": "Дата", - "sortByExpiration": "Срок действия", - "search": "Поиск", - "colName": "Имя", - "colType": "Тип", - "colDateShared": "Дата общего доступа", - "colExpiration": "Срок действия", - "colPermissions": "Разрешения", - "colPassword": "Пароль", - "colActions": "Действия", - "emptyStateTitle": "Общих ресурсов пока нет", - "emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", - "goToFiles": "Перейти к файлам", - "typeFile": "Файл", - "typeFolder": "Папка", - "noExpiration": "Без срока", - "hasPassword": "Да", - "noPassword": "Нет", - "editShare": "Изменить общий доступ", - "notifyShare": "Уведомить", - "copyLink": "Копировать ссылку", - "removeShare": "Удалить общий доступ", - "linkCopied": "Ссылка скопирована в буфер обмена!", - "linkCopyFailed": "Не удалось скопировать ссылку", - "itemUpdated": "Настройки общего доступа обновлены", - "itemRemoved": "Общий доступ удалён", - "invalidEmail": "Укажите корректный адрес email", - "notificationSent": "Уведомление успешно отправлено", - "notificationFailed": "Не удалось отправить уведомление", - "shared_backToFiles": "Назад к файлам", - "shared_pageTitle": "Общие ресурсы", - "shared_pageDescription": "Управление общими файлами и папками", - "shared_filterType": "Тип:", - "shared_filterAll": "Все", - "shared_filterFiles": "Файлы", - "shared_filterFolders": "Папки", - "shared_sortBy": "Сортировка:", - "shared_sortByName": "Имя", - "shared_sortByDate": "Дата", - "shared_sortByExpiration": "Срок действия", - "shared_search": "Поиск", - "shared_colName": "Имя", - "shared_colType": "Тип", - "shared_colDateShared": "Дата общего доступа", - "shared_colExpiration": "Срок действия", - "shared_colPermissions": "Разрешения", - "shared_colPassword": "Пароль", - "shared_colActions": "Действия", - "shared_emptyStateTitle": "Общих ресурсов пока нет", - "shared_emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", - "shared_goToFiles": "Перейти к файлам", - "shared_typeFile": "Файл", - "shared_typeFolder": "Папка", - "shared_noExpiration": "Без срока", - "shared_hasPassword": "Да", - "shared_noPassword": "Нет", - "shared_editShare": "Изменить общий доступ", - "shared_notifyShare": "Уведомить", - "shared_copyLink": "Копировать ссылку", - "shared_removeShare": "Удалить общий доступ", - "shared_linkCopied": "Ссылка скопирована в буфер обмена!", - "shared_linkCopyFailed": "Не удалось скопировать ссылку", - "shared_itemUpdated": "Настройки общего доступа обновлены", - "shared_itemRemoved": "Общий доступ удалён", - "shared_invalidEmail": "Укажите корректный адрес email", - "shared_notificationSent": "Уведомление успешно отправлено", - "shared_notificationFailed": "Не удалось отправить уведомление" - }, - "files": { - "name": "Имя", - "type": "Тип", - "size": "Размер", - "modified": "Изменён", - "no_files": "В этой папке нет файлов", - "empty_hint": "Загрузите файлы или создайте папки, чтобы начать", - "loading": "Загрузка файлов…", - "view_grid": "Сетка", - "view_list": "Список", - "file_types": { - "document": "Документ", - "image": "Изображение", - "video": "Видео", - "audio": "Аудио", - "pdf": "PDF", - "text": "Текст", - "folder": "Папка", - "spreadsheet": "Таблица", - "presentation": "Презентация", - "archive": "Архив", - "installer": "Установщик", - "code": "Код" - }, - "owner": "Владелец" - }, - "dialogs": { - "rename_folder": "Переименовать папку", - "rename_file": "Переименовать файл", - "new_name": "Новое имя", - "new_folder_title": "Новая папка", - "folder_name": "Имя папки", - "folder_placeholder": "Моя папка", - "rename_title": "Переименовать", - "move_file": "Переместить файл", - "move_folder": "Переместить папку", - "select_destination": "Выберите папку назначения:", - "select_this_folder": "Выбрать эту папку", - "go_to_parent": ".. (родительская папка)", - "no_subfolders": "Нет подпапок", - "root": "Корень", - "delete_confirmation": "Вы уверены, что хотите удалить", - "and_contents": "и всё его содержимое", - "no_undo": "Это действие невозможно отменить", - "confirm_title": "Подтверждение действия", - "confirm_delete": "В корзину", - "confirm_delete_file": "Вы уверены, что хотите переместить файл \"{{name}}\" в корзину?", - "confirm_delete_folder": "Вы уверены, что хотите переместить папку \"{{name}}\" и всё её содержимое в корзину?", - "confirm_permanent_delete": "Удалить навсегда", - "confirm_permanent_delete_msg": "Вы уверены, что хотите навсегда удалить этот элемент? Это действие невозможно отменить.", - "confirm_empty_trash": "Очистить корзину", - "confirm_delete_share": "Удалить ссылку общего доступа", - "confirm_delete_share_msg": "Вы уверены, что хотите удалить эту ссылку общего доступа?", - "share_file": "Поделиться файлом", - "share_folder": "Поделиться папкой", - "existing_shares": "Существующие общие доступы", - "share_options": "Параметры общего доступа", - "password": "Пароль", - "expiration": "Срок действия", - "permissions": "Разрешения", - "generated_link": "Сгенерированная ссылка", - "notify": "Отправить уведомление", - "recipient": "Получатель", - "message": "Сообщение", - "move_to_home": "Переместить в домашнюю папку" - }, - "dropzone": { - "drag_files": "Перетащите файлы сюда или нажмите для выбора", - "drop_files": "Отпустите файлы для загрузки" - }, - "permissions": { - "read": "Чтение", - "write": "Запись", - "reshare": "Пересылка" - }, - "errors": { - "file_not_found": "Файл не найден", - "folder_not_found": "Папка не найдена", - "delete_error": "Ошибка удаления", - "upload_error": "Ошибка загрузки файла", - "rename_error": "Ошибка переименования", - "move_error": "Ошибка перемещения", - "empty_name": "Имя не может быть пустым", - "name_exists": "Файл или папка с таким именем уже существует", - "generic_error": "Произошла ошибка", - "group_name_invalid": "Имя группы должно соответствовать формату префикса эл. почты (буквы, цифры, точка, дефис, подчёркивание; 1–64 символов).", - "group_cycle": "Этот участник создаст циклическую ссылку между группами.", - "group_depth_exceeded": "Глубина вложенности превышает допустимый максимум (8).", - "group_virtual_immutable": "Группа «Internal» управляется системой и не может быть изменена.", - "group_not_found": "Группа не найдена.", - "group_name_taken": "Группа с таким именем уже существует." - }, - "breadcrumb": { - "home": "Главная" - }, - "trash": { - "empty_trash": "Очистить корзину", - "empty_state": "Корзина пуста", - "original_location": "Исходное расположение", - "deleted_date": "Дата удаления", - "remaining": "Осталось", - "actions": "Действия", - "restore": "Восстановить", - "delete_permanently": "Удалить навсегда", - "empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда.", - "groupby": { - "remaining_days": "Осталось дней", - "trashed_time": "Время удаления" - } - }, - "daysRemaining": { - "expired": "Истёк", - "today": "Сегодня", - "tomorrow": "Завтра", - "inDays": "{{count}} дн." - }, - "expiryChip": { - "never": "Никогда не истекает", - "expired": "Истёк", - "today": "Истекает сегодня", - "tomorrow": "Истекает завтра", - "inDays": "Истекает через {{count}} дн.", - "onDate": "Истекает {{date}}" - }, - "auth": { - "login_title": "Вход", - "username": "Имя пользователя", - "username_placeholder": "Введите имя пользователя", - "login_identifier": "Имя пользователя или email", - "login_identifier_placeholder": "Введите имя пользователя или email", - "password": "Пароль", - "password_placeholder": "Введите пароль", - "login_button": "Войти", - "no_account": "Нет аккаунта?", - "register": "Зарегистрироваться", - "admin_setup": "Первый запуск?", - "setup": "Настроить администратора", - "register_title": "Создание аккаунта", - "email": "Email", - "email_placeholder": "Введите email", - "confirm_password": "Подтвердите пароль", - "confirm_password_placeholder": "Подтвердите пароль", - "register_button": "Создать аккаунт", - "have_account": "Уже есть аккаунт?", - "login": "Войти", - "setup_title": "Начальная настройка", - "setup_step1": "Админ", - "setup_step2": "Система", - "setup_step3": "Готово", - "admin_username": "Имя администратора", - "admin_email": "Email администратора", - "admin_password": "Пароль администратора", - "create_admin": "Создать администратора", - "back_to_login": "Уже настроено?", - "admin_success": "Аккаунт администратора успешно создан! Теперь вы можете войти.", - "account_success": "Аккаунт успешно создан! Теперь вы можете войти.", - "passwords_mismatch": "Пароли не совпадают", - "admin_create_error": "Ошибка создания аккаунта администратора", - "or": "или", - "sso_login": "Войти через SSO", - "sso_login_provider": "Войти через {{provider}}", - "magicLinkHint": "Нет пароля? Введите ваш email, и мы пришлём вам одноразовую ссылку для входа.", - "magicLinkEmailLabel": "Адрес электронной почты", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "Отправить ссылку для входа", - "magicLinkSent": "Если для этого адреса существует учётная запись, ссылка для входа отправлена. Проверьте входящие.", - "magicLinkUnavailable": "Вход по электронной почте недоступен на этом сервере.", - "magicLinkNetworkError": "Не удалось подключиться к серверу: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Хранилище", - "calculating": "Вычисление...", - "used": "{{percentage}}% использовано ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Предварительный просмотр этого типа файлов недоступен.", - "download_file": "Скачать файл", - "zoom_in": "Увеличить", - "zoom_out": "Уменьшить", - "zoom_reset": "Сбросить масштаб" - }, - "language_selector": { - "title": "Добро пожаловать!", - "subtitle": "Выберите язык для продолжения", - "continue": "Продолжить", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ru": "Русский", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands" - } - }, - "favorites": { - "empty_state": "Избранного пока нет", - "empty_hint": "Добавьте файлы или папки в избранное, нажав на звёздочку", - "add": "В избранное", - "remove": "Из избранного", - "added_title": "Добавлено в избранное", - "added_msg": "добавлено в избранное", - "removed_title": "Удалено из избранного", - "removed_msg": "удалено из избранного" - }, - "recent": { - "title": "Недавние", - "clear": "Очистить недавние", - "accessed": "Открыт", - "empty_state": "Нет недавних файлов", - "empty_hint": "Открытые вами файлы будут отображаться здесь", - "loadMore": "Загрузить ещё" - }, - "notifications": { - "file_renamed": "Файл переименован", - "file_renamed_to": "Файл переименован в \"{{name}}\"", - "folder_renamed": "Папка переименована", - "folder_renamed_to": "Папка переименована в \"{{name}}\"", - "file_uploaded": "Файл загружен", - "file_deleted": "Файл перемещён в корзину", - "folder_deleted": "Папка перемещена в корзину", - "item_deleted_permanently": "Элемент удалён навсегда", - "trash_emptied": "Корзина успешно очищена", - "title": "Уведомления", - "empty": "Нет уведомлений", - "link_created": "Ссылка создана", - "share_success": "Ссылка для общего доступа успешно создана", - "upload_files_section_title": "Загрузка здесь недоступна", - "upload_files_section_body": "Перейдите в раздел «Файлы», чтобы загрузить файлы" - }, - "batch": { - "one_selected": "Выбран 1 элемент", - "n_selected": "Выбрано {{count}} элементов", - "confirm_delete": "Вы уверены, что хотите переместить {{count}} элементов в корзину?", - "move_title": "Переместить {{count}} элементов", - "add_favorites": "В избранное", - "move_copy": "Переместить или копировать" - }, - "admin": { - "page_title": "Панель администратора", - "back_to_app": "Назад в OxiCloud", - "loading": "Загрузка…", - "access_denied": "Доступ запрещён", - "access_denied_desc": "Необходимы права администратора.", - "sign_in": "Войти", - "tab_dashboard": "Панель", - "tab_users": "Пользователи", - "tab_oidc": "SSO / OIDC", - "total_users": "Всего пользователей", - "active_users": "Активные", - "admins": "Администраторы", - "version": "Версия", - "storage_overview": "Обзор хранилища", - "used": "Использовано", - "total_quota": "Общая квота", - "usage_pct": "Использование %", - "users_over_80": "Пользователи >80%", - "users_over_quota": "Сверх квоты", - "system": "Система", - "auth_label": "Аутентификация", - "oidc_label": "OIDC", - "quotas_label": "Квоты", - "enabled": "Включено", - "disabled": "Отключено", - "active": "Активен", - "off": "Выкл", - "allow_registration": "Разрешить публичную регистрацию", - "registration_warning": "Публичная регистрация отключена. Только админы могут создавать пользователей.", - "user_management": "Управление пользователями", - "create_user": "Создать пользователя", - "col_user": "Пользователь", - "col_role": "Роль", - "col_auth": "Аутентификация", - "col_status": "Статус", - "col_storage": "Хранилище", - "col_last_login": "Последний вход", - "col_actions": "Действия", - "loading_users": "Загрузка пользователей…", - "failed_load_users": "Не удалось загрузить", - "no_users_found": "Пользователи не найдены", - "showing_users": "Показано {{from}}-{{to}} из {{total}}", - "prev": "Назад", - "next": "Далее", - "inactive": "Неактивен", - "you_badge": "(вы)", - "local": "Локальный", - "never": "Никогда", - "just_now": "Только что", - "minutes_ago": "{{n}} мин назад", - "hours_ago": "{{n}} ч назад", - "days_ago": "{{n}} дн назад", - "edit_quota_title": "Изменить квоту", - "reset_password_title": "Сбросить пароль", - "toggle_role_title": "Сменить роль", - "deactivate_title": "Деактивировать", - "activate_title": "Активировать", - "delete_title": "Удалить", - "sso_title": "Единый вход (OIDC / SSO)", - "enable_sso": "Включить SSO", - "provider_name": "Имя провайдера", - "issuer_url": "URL издателя", - "issuer_url_hint": "URL издателя OpenID Connect", - "auto_discover": "Авто-обнаружение", - "discovering": "Обнаружение…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Оставьте пустым для сохранения", - "secret_configured": "Client secret уже настроен", - "callback_url": "URL обратного вызова", - "callback_url_hint": "(зарегистрируйте в IdP)", - "advanced_settings": "Расширенные настройки", - "scopes": "Области", - "auto_provision": "Автоматически создавать пользователей", - "admin_groups": "Группы администраторов", - "admin_groups_hint": "Имена групп OIDC через запятую", - "disable_password": "Отключить вход по паролю (только OIDC)", - "password_warning": "Это заблокирует ВСЕ входы по паролю!", - "test_btn": "Тест", - "save_btn": "Сохранить", - "saving": "Сохранение…", - "settings_saved": "Настройки сохранены — OIDC теперь {{status}}", - "quota_modal_title": "Обновить квоту", - "quota_user_label": "Пользователь:", - "new_quota": "Новая квота", - "quota_unlimited_hint": "0 для безлимитного", - "cancel": "Отмена", - "create_user_title": "Создать пользователя", - "username_label": "Имя пользователя", - "username_placeholder": "ivanov", - "username_hint": "3–32 символа", - "password_label": "Пароль", - "password_placeholder": "Мин. 8 символов", - "email_label": "Эл. почта", - "email_optional": "(необязательно)", - "email_placeholder": "user@example.com (автоматически если пусто)", - "role_label": "Роль", - "role_user": "Пользователь", - "role_admin": "Админ", - "quota_label": "Квота", - "creating": "Создание…", - "reset_pw_title": "Сбросить пароль", - "new_password_label": "Новый пароль", - "resetting": "Сброс…", - "reset_btn": "Сбросить", - "confirm_role_change": "Изменить роль на {{role}}?", - "confirm_deactivate": "Деактивировать этого пользователя?", - "confirm_activate": "Активировать этого пользователя?", - "confirm_delete_user": "УДАЛИТЬ пользователя \"{{name}}\"? Нельзя отменить!", - "confirm_action": "Подтвердить", - "confirm_yes": "Подтвердить", - "confirm_no": "Отмена", - "error_username_short": "Имя минимум 3 символа", - "error_password_short": "Пароль минимум 8 символов", - "error_generic": "Ошибка", - "error_network": "Ошибка сети: {{message}}", - "error_create_user": "Не удалось создать", - "tab_storage": "Хранилище", - "storage_title": "Настройка хранилища", - "storage_current_backend": "Текущий бэкенд", - "storage_total_blobs": "Всего блобов", - "storage_total_size": "Общий размер", - "storage_dedup_ratio": "Коэффициент дедупликации", - "storage_backend": "Бэкенд", - "storage_local": "Локальный", - "storage_s3": "Совместимый с S3", - "storage_provider_preset": "Пресет провайдера", - "storage_preset_custom": "Пользовательский", - "storage_endpoint_url": "URL конечной точки", - "storage_endpoint_hint": "Оставьте пустым для AWS S3", - "storage_bucket": "Бакет", - "storage_region": "Регион", - "storage_access_key": "Ключ доступа", - "storage_secret_key": "Секретный ключ", - "storage_secret_configured": "Ключ настроен", - "storage_key_placeholder": "Введите новый ключ", - "storage_path_style": "Принудительный стиль пути", - "storage_path_style_hint": "Требуется для MinIO и некоторых S3-совместимых сервисов", - "storage_test_connection": "Проверить соединение", - "storage_test_success": "Соединение успешно", - "storage_test_failure": "Ошибка соединения", - "storage_save": "Сохранить конфигурацию", - "storage_saved": "Конфигурация сохранена", - "storage_migration": "Миграция данных", - "storage_migration_coming_soon": "Инструменты миграции скоро появятся", - "migration_status_label": "Статус миграции", - "migration_start": "Начать миграцию", - "migration_pause": "Пауза", - "migration_resume": "Возобновить", - "migration_verify": "Проверить", - "migration_complete": "Завершить", - "migration_started": "Миграция начата", - "migration_paused_msg": "Миграция приостановлена", - "migration_resumed_msg": "Миграция возобновлена", - "migration_completed_msg": "Миграция успешно завершена", - "migration_verifying": "Проверка...", - "migration_verify_passed": "Проверка пройдена", - "migration_verify_failed": "Проверка не пройдена", - "migration_failed_blobs": "Неудачные блобы", - "testing": "Тестирование...", - "smtp_disabled": "Отключено (хост не задан)", - "smtp_enabled": "Включено", - "smtp_enabled_label": "Статус", - "smtp_intro": "SMTP настраивается исключительно через переменные окружения (OXICLOUD_SMTP_*). Значения ниже считываются с работающего сервера — чтобы изменить их, отредактируйте окружение и перезапустите OxiCloud.", - "smtp_not_configured": "SMTP не настроен на этом сервере.", - "smtp_send_failed": "Сбой отправки.", - "smtp_send_test": "Отправить тестовое письмо", - "smtp_sending": "Отправка…", - "smtp_sent": "Тестовое письмо отправлено.", - "smtp_server_code": "Ответ сервера", - "smtp_test_intro": "Отправляет заранее заданное диагностическое сообщение указанному ниже получателю и сообщает ответ SMTP-сервера, чтобы вы могли сопоставить его с журналами вашего relay.", - "smtp_test_missing_to": "Введите адрес получателя.", - "smtp_test_title": "Отправить тестовое письмо", - "smtp_test_to": "Адрес получателя", - "smtp_title": "Исходящая почта (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Профиль", - "back_to_app": "Назад в OxiCloud", - "loading": "Загрузка…", - "not_authenticated": "Не аутентифицирован", - "not_authenticated_desc": "Войдите, чтобы просмотреть свой профиль.", - "sign_in": "Войти", - "role_admin": "Администратор", - "role_user": "Пользователь", - "account_details": "Данные аккаунта", - "username": "Имя пользователя", - "email": "Эл. почта", - "role": "Роль", - "last_login": "Последний вход", - "storage": "Хранилище", - "used": "Использовано", - "quota": "Квота", - "usage": "Использование", - "unlimited": "Безлимитный", - "app_passwords": "Пароли приложений", - "app_pw_desc": "Создайте пароли для клиентов WebDAV, CalDAV и CardDAV. Каждый пароль показывается только один раз.", - "app_pw_label_placeholder": "Метка (напр. Thunderbird, macOS)", - "generate": "Создать", - "generating": "Создание…", - "new_password_for": "Новый пароль для", - "copy_warning": "Скопируйте пароль сейчас. Вы не сможете увидеть его снова.", - "copy_to_clipboard": "Копировать в буфер", - "col_label": "Метка", - "col_created": "Создан", - "col_last_used": "Последнее использование", - "col_status": "Статус", - "active": "Активен", - "revoked": "Отозван", - "revoke_title": "Отозвать", - "no_app_passwords": "Паролей приложений пока нет.", - "client_sessions": "Сессии клиентов", - "client_sessions_desc": "Автоматически создаются при подключении клиента, совместимого с Nextcloud.", - "col_client": "Клиент", - "never": "Никогда", - "just_now": "Только что", - "minutes_ago": "{{n}} мин назад", - "hours_ago": "{{n}} ч назад", - "days_ago": "{{n}} дн назад", - "edit_profile": "Редактировать профиль", - "edit_oidc_managed": "Чтобы изменить ваши данные (имя, фамилию, фотографию профиля, …), обновите их у вашего провайдера идентификации. Изменения появятся при следующем входе.", - "username_claim_hint": "2–64 символа, буквы / цифры / точка / дефис / подчёркивание. После выбора имя пользователя нельзя изменить (клиенты DAV/NextCloud зависят от него).", - "username_already_claimed": "Имя пользователя установлено и не может быть изменено (клиенты DAV/NextCloud зависят от него).", - "given_name": "Имя", - "family_name": "Фамилия", - "notify_on_share": "Уведомлять меня по электронной почте, когда кто-то делится со мной", - "notify_on_share_hint": "Если флажок снят, общие ресурсы по-прежнему будут отображаться в вашей учётной записи — вы просто не будете получать о них письма.", - "save_profile": "Сохранить изменения", - "profile_saved": "Профиль обновлён", - "profile_no_changes": "Нет изменений для сохранения.", - "profile_save_failed": "Не удалось сохранить", - "username_taken_error": "Это имя пользователя уже занято.", - "username_immutable_error": "Ваше имя пользователя уже установлено и не может быть изменено здесь. Свяжитесь с администратором, если нужно переименовать.", - "change_password": "Изменить пароль", - "current_password": "Текущий пароль", - "new_password": "Новый пароль", - "min_8_chars": "Минимум 8 символов", - "confirm_password": "Подтвердите новый пароль", - "update_password": "Обновить пароль", - "updating": "Обновление…", - "password_updated": "Пароль успешно обновлён", - "passwords_no_match": "Пароли не совпадают", - "password_too_short": "Пароль должен быть не менее 8 символов", - "password_change_failed": "Не удалось изменить пароль", - "error_network": "Ошибка сети: {{message}}", - "error_label_required": "Введите метку", - "error_create_pw": "Не удалось создать пароль", - "confirm_revoke": "Отозвать пароль «{{label}}»? Клиенты перестанут работать.", - "error_revoke": "Не удалось отозвать", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Загрузка...", - "files": "файлов", - "complete": "{{count}} / {{total}} загружено" - }, - "storage_quota_exceeded": "Превышена квота хранилища", - "sharedwithme": { - "pageTitle": "Доступно мне", - "pageDescription": "Файлы и папки, которые другие пользователи предоставили вам", - "emptyStateTitle": "Вам ещё ничего не предоставлено", - "emptyStateDesc": "Элементы, которые другие пользователи предоставят вам, появятся здесь", - "loadMore": "Загрузить ещё", - "sharedBy": "Предоставлено", - "colName": "Имя", - "colType": "Тип", - "colSharedBy": "Предоставлено", - "colDate": "Дата предоставления", - "colPermissions": "Права" - }, - "groupby": { - "none": "Нет", - "title": "Группировать по", - "owner": "Владелец", - "shareDate": "Дата общего доступа", - "type": "Тип", - "type.folders": "Папки", - "accessedAt": "Дата доступа", - "modifiedAt": "Дата изменения", - "createdAt": "Дата создания", - "size": "Размер", - "favoriteDate": "Дата добавления в избранное", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Новые" - }, - "dateBucket": { - "today": "Сегодня", - "last7days": "Последние 7 дней", - "last30days": "Последние 30 дней" - }, - "groups": { - "title": "Управление группами", - "create_button": "Создать группу", - "create_dialog_title": "Новая группа", - "edit_dialog_title": "Переименовать группу", - "name_label": "Имя", - "name_placeholder": "инженеры", - "description_label": "Описание (необязательно)", - "members_section": "Участники", - "add_member_placeholder": "Добавить пользователя или группу…", - "no_members": "Пока нет участников.", - "remove_member": "Удалить", - "delete_group": "Удалить группу", - "delete_confirm": "Удалить группу «{name}»? Все привязанные к ней разрешения будут отозваны.", - "empty_state": "Пока нет групп.", - "load_more": "Загрузить ещё", - "back_to_list": "Назад", - "loading": "Загрузка…", - "virtual_badge": "Системная", - "member_count_zero": "Нет участников", - "member_count_one": "1 участник", - "member_count_other": "{count} участников", - "delete_confirm_label": "Введите имя группы для подтверждения:", - "delete_confirm_mismatch": "Введите имя группы точно для подтверждения.", - "virtual_internal_name": "Внутренние", - "members_loading": "Загрузка участников…", - "members_empty": "Нет участников", - "virtual_internal_explanation": "Каждый внутренний пользователь на этом сервере" - }, - "myshares": { - "copyLink": "Копировать ссылку", - "deleteLink": "Удалить ссылку", - "notifyByEmail": "Уведомить по e-mail", - "notifyFailed": "Не удалось отправить уведомление.", - "notifyGroupMembers": "Уведомить участников группы", - "notifyRateLimited": "Слишком много уведомлений для этого получателя — попробуйте позже.", - "removeAccess": "Отозвать доступ", - "resendInvitation": "Отправить приглашение повторно" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", + "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте OxiCloud, чтобы увидеть новый общий ресурс:\n{{login_link}}\n\nВозможно, у вас есть и другие новые общие ресурсы от {{inviter}} — войдите, чтобы увидеть все элементы, которыми с вами поделились.\n\n— OxiCloud\n\nВы получаете это сообщение, потому что у вас есть учётная запись OxiCloud и предпочтение уведомлений об общих ресурсах включено. Вы можете отключить его в своём профиле (Уведомлять меня по электронной почте, когда кто-то делится со мной)." + } } + }, + "app": { + "title": "OxiCloud", + "description": "Минималистичная система облачного хранения" + }, + "nav": { + "files": "Файлы", + "shared": "Общие", + "recent": "Недавние", + "favorites": "Избранное", + "photos": "Фото", + "music": "Музыка", + "trash": "Корзина", + "sharedwithme": "Доступно мне", + "profile": "Профиль", + "shared_with_me": "Доступно мне" + }, + "photos": { + "empty_state": "Фотографий пока нет", + "empty_hint": "Загрузите изображения или видео, чтобы увидеть их здесь", + "items_selected": "выбрано", + "view_daily": "День", + "view_monthly": "Месяц", + "view_yearly": "Год", + "group_by": "Группировать по" + }, + "music": { + "create_playlist": "Создать плейлист", + "playlists": "Плейлисты", + "no_playlists": "Плейлистов пока нет", + "select_playlist": "Выберите плейлист", + "select_hint": "Выберите плейлист на панели слева или создайте новый", + "add_tracks": "Добавить треки", + "no_tracks": "В этом плейлисте нет треков", + "unknown_artist": "Неизвестный исполнитель", + "unknown_title": "Неизвестно", + "confirm_delete": "Удалить этот плейлист?", + "playlist_name": "Название плейлиста", + "create": "Создать", + "delete": "Удалить", + "share": "Поделиться", + "edit": "Редактировать", + "play_all": "Воспроизвести все", + "shuffle": "Перемешать", + "repeat": "Повтор", + "repeat_one": "Повторять один", + "queue": "Очередь", + "queue_empty": "Очередь пуста", + "not_playing": "Ничего не играет", + "play": "Воспроизвести", + "pause": "Пауза", + "previous": "Предыдущий", + "next": "Следующий", + "volume": "Громкость", + "mute": "Выключить звук", + "unmute": "Включить звук", + "title": "Название", + "artist": "Исполнитель", + "album": "Альбом", + "tracks": "треков", + "add": "Добавить", + "added": "Добавлено!", + "added_to_playlist": "добавлен в плейлист", + "add_to_playlist": "Добавить в плейлист", + "load_error": "Ошибка загрузки плейлистов", + "add_error": "Не удалось добавить треки в плейлист", + "no_playlists_yet": "Плейлистов пока нет. Создайте сначала!", + "selected_files": "Выбрано:", + "error": "Ошибка", + "search_audio": "Поиск аудиофайлов…", + "no_audio_files": "Аудиофайлы не найдены", + "selected": "выбрано", + "loading": "Загрузка…", + "search_error": "Не удалось загрузить аудиофайлы", + "adding": "Добавление…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "Предыдущий" + }, + "actions": { + "search": "Поиск файлов...", + "new_folder": "Новая папка", + "upload": "Загрузить", + "upload_files": "Загрузить файлы", + "upload_folder": "Загрузить папку", + "upload.uploading": "Загрузка...", + "upload.complete": "{count} / {total} загружено", + "upload.files": "файлов", + "rename": "Переименовать", + "move": "Переместить в...", + "move_to": "Переместить в", + "delete": "Удалить", + "download": "Скачать", + "view": "Просмотр", + "cancel": "Отмена", + "confirm": "Подтвердить", + "share": "Поделиться", + "favorite": "В избранное", + "unfavorite": "Из избранного", + "copy": "Копировать", + "notify": "Уведомить", + "send": "Отправить", + "clear_recent": "Очистить недавние", + "logout": "Выйти", + "create": "Создать", + "search_btn": "Найти", + "close": "Закрыть", + "delete_permanently": "Удалить навсегда", + "empty_trash": "Очистить корзину", + "open_parent_folder": "Перейти в родительскую папку", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "Оформление", + "about": "О OxiCloud", + "about_description": "Платформа облачного хранения на Rust с чистой архитектурой. Быстрая, безопасная и конфиденциальная.", + "admin_panel": "Панель администратора", + "profile": "Мой профиль", + "role_user": "Пользователь", + "theme": { + "light": "Светлая", + "dark": "Тёмная", + "auto": "Как в системе" + }, + "manage_groups": "Управление группами", + "admin": "Админ" + }, + "share": { + "dialogTitle": "Ссылка для обмена", + "linkLabel": "Ссылка:", + "copyLink": "Копировать", + "permissions": "Разрешения:", + "permissionRead": "Чтение", + "permissionWrite": "Запись", + "permissionReshare": "Пересылка", + "password": "Защита паролем:", + "generatePassword": "Сгенерировать", + "expiration": "Срок действия:", + "update": "Обновить общий доступ", + "remove": "Удалить общий доступ", + "notifyTitle": "Отправить уведомление", + "notifyEmailLabel": "Адрес email:", + "notifyMessageLabel": "Сообщение (необязательно):", + "notifySend": "Отправить уведомление", + "shareWithOthers": "Поделиться с другими", + "sharePublicly": "Общий доступ", + "shareSettings": "Настройки общего доступа", + "shareCopied": "Ссылка скопирована в буфер обмена", + "shareCreated": "Ссылка для общего доступа успешно создана", + "shareUpdated": "Настройки общего доступа успешно обновлены", + "shareRemoved": "Общий доступ успешно удалён", + "inviteByEmail": "Пригласить по e-mail — приглашение будет отправлено", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "Копировать", + "copy_failed": "Could not copy link", + "download": "Скачать", + "files": "Файлы", + "folders": "Папки", + "link_name": "Link name (optional)", + "notifyByEmail": "Уведомить по e-mail", + "revoke": "Remove", + "role_label": "Роль" + }, + "share_dialogTitle": "Ссылка для обмена", + "share_linkLabel": "Ссылка:", + "share_copyLink": "Копировать", + "share_permissions": "Разрешения:", + "share_permissionRead": "Чтение", + "share_permissionWrite": "Запись", + "share_permissionReshare": "Пересылка", + "share_password": "Защита паролем:", + "share_generatePassword": "Сгенерировать", + "share_expiration": "Срок действия:", + "share_update": "Обновить общий доступ", + "share_remove": "Удалить общий доступ", + "share_notifyTitle": "Отправить уведомление", + "share_notifyEmailLabel": "Адрес email:", + "share_notifyMessageLabel": "Сообщение (необязательно):", + "share_notifySend": "Отправить уведомление", + "shared": { + "backToFiles": "Назад к файлам", + "pageTitle": "Общие ресурсы", + "pageDescription": "Управление общими файлами и папками", + "filterType": "Тип:", + "filterAll": "Все", + "filterFiles": "Файлы", + "filterFolders": "Папки", + "sortBy": "Сортировка:", + "sortByName": "Имя", + "sortByDate": "Дата", + "sortByExpiration": "Срок действия", + "search": "Поиск", + "colName": "Имя", + "colType": "Тип", + "colDateShared": "Дата общего доступа", + "colExpiration": "Срок действия", + "colPermissions": "Разрешения", + "colPassword": "Пароль", + "colActions": "Действия", + "emptyStateTitle": "Общих ресурсов пока нет", + "emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", + "goToFiles": "Перейти к файлам", + "typeFile": "Файл", + "typeFolder": "Папка", + "noExpiration": "Без срока", + "hasPassword": "Да", + "noPassword": "Нет", + "editShare": "Изменить общий доступ", + "notifyShare": "Уведомить", + "copyLink": "Копировать ссылку", + "removeShare": "Удалить общий доступ", + "linkCopied": "Ссылка скопирована в буфер обмена!", + "linkCopyFailed": "Не удалось скопировать ссылку", + "itemUpdated": "Настройки общего доступа обновлены", + "itemRemoved": "Общий доступ удалён", + "invalidEmail": "Укажите корректный адрес email", + "notificationSent": "Уведомление успешно отправлено", + "notificationFailed": "Не удалось отправить уведомление", + "shared_backToFiles": "Назад к файлам", + "shared_pageTitle": "Общие ресурсы", + "shared_pageDescription": "Управление общими файлами и папками", + "shared_filterType": "Тип:", + "shared_filterAll": "Все", + "shared_filterFiles": "Файлы", + "shared_filterFolders": "Папки", + "shared_sortBy": "Сортировка:", + "shared_sortByName": "Имя", + "shared_sortByDate": "Дата", + "shared_sortByExpiration": "Срок действия", + "shared_search": "Поиск", + "shared_colName": "Имя", + "shared_colType": "Тип", + "shared_colDateShared": "Дата общего доступа", + "shared_colExpiration": "Срок действия", + "shared_colPermissions": "Разрешения", + "shared_colPassword": "Пароль", + "shared_colActions": "Действия", + "shared_emptyStateTitle": "Общих ресурсов пока нет", + "shared_emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", + "shared_goToFiles": "Перейти к файлам", + "shared_typeFile": "Файл", + "shared_typeFolder": "Папка", + "shared_noExpiration": "Без срока", + "shared_hasPassword": "Да", + "shared_noPassword": "Нет", + "shared_editShare": "Изменить общий доступ", + "shared_notifyShare": "Уведомить", + "shared_copyLink": "Копировать ссылку", + "shared_removeShare": "Удалить общий доступ", + "shared_linkCopied": "Ссылка скопирована в буфер обмена!", + "shared_linkCopyFailed": "Не удалось скопировать ссылку", + "shared_itemUpdated": "Настройки общего доступа обновлены", + "shared_itemRemoved": "Общий доступ удалён", + "shared_invalidEmail": "Укажите корректный адрес email", + "shared_notificationSent": "Уведомление успешно отправлено", + "shared_notificationFailed": "Не удалось отправить уведомление" + }, + "files": { + "name": "Имя", + "type": "Тип", + "size": "Размер", + "modified": "Изменён", + "no_files": "В этой папке нет файлов", + "empty_hint": "Загрузите файлы или создайте папки, чтобы начать", + "loading": "Загрузка файлов…", + "view_grid": "Сетка", + "view_list": "Список", + "file_types": { + "document": "Документ", + "image": "Изображение", + "video": "Видео", + "audio": "Аудио", + "pdf": "PDF", + "text": "Текст", + "folder": "Папка", + "spreadsheet": "Таблица", + "presentation": "Презентация", + "archive": "Архив", + "installer": "Установщик", + "code": "Код" + }, + "owner": "Владелец", + "add_favorites": "В избранное", + "added_favorites": "Добавлено в избранное", + "col_name": "Имя", + "col_owner": "Владелец", + "col_size": "Размер", + "col_type": "Тип", + "copy": "Копировать", + "edit": "Редактировать", + "file": "Файл", + "folder": "Папка", + "new_folder": "Новая папка", + "share": "Поделиться", + "view": "Просмотр" + }, + "dialogs": { + "rename_folder": "Переименовать папку", + "rename_file": "Переименовать файл", + "new_name": "Новое имя", + "new_folder_title": "Новая папка", + "folder_name": "Имя папки", + "folder_placeholder": "Моя папка", + "rename_title": "Переименовать", + "move_file": "Переместить файл", + "move_folder": "Переместить папку", + "select_destination": "Выберите папку назначения:", + "select_this_folder": "Выбрать эту папку", + "go_to_parent": ".. (родительская папка)", + "no_subfolders": "Нет подпапок", + "root": "Корень", + "delete_confirmation": "Вы уверены, что хотите удалить", + "and_contents": "и всё его содержимое", + "no_undo": "Это действие невозможно отменить", + "confirm_title": "Подтверждение действия", + "confirm_delete": "В корзину", + "confirm_delete_file": "Вы уверены, что хотите переместить файл \"{{name}}\" в корзину?", + "confirm_delete_folder": "Вы уверены, что хотите переместить папку \"{{name}}\" и всё её содержимое в корзину?", + "confirm_permanent_delete": "Удалить навсегда", + "confirm_permanent_delete_msg": "Вы уверены, что хотите навсегда удалить этот элемент? Это действие невозможно отменить.", + "confirm_empty_trash": "Очистить корзину", + "confirm_delete_share": "Удалить ссылку общего доступа", + "confirm_delete_share_msg": "Вы уверены, что хотите удалить эту ссылку общего доступа?", + "share_file": "Поделиться файлом", + "share_folder": "Поделиться папкой", + "existing_shares": "Существующие общие доступы", + "share_options": "Параметры общего доступа", + "password": "Пароль", + "expiration": "Срок действия", + "permissions": "Разрешения", + "generated_link": "Сгенерированная ссылка", + "notify": "Отправить уведомление", + "recipient": "Получатель", + "message": "Сообщение", + "move_to_home": "Переместить в домашнюю папку" + }, + "dropzone": { + "drag_files": "Перетащите файлы сюда или нажмите для выбора", + "drop_files": "Отпустите файлы для загрузки" + }, + "permissions": { + "read": "Чтение", + "write": "Запись", + "reshare": "Пересылка" + }, + "errors": { + "file_not_found": "Файл не найден", + "folder_not_found": "Папка не найдена", + "delete_error": "Ошибка удаления", + "upload_error": "Ошибка загрузки файла", + "rename_error": "Ошибка переименования", + "move_error": "Ошибка перемещения", + "empty_name": "Имя не может быть пустым", + "name_exists": "Файл или папка с таким именем уже существует", + "generic_error": "Произошла ошибка", + "group_name_invalid": "Имя группы должно соответствовать формату префикса эл. почты (буквы, цифры, точка, дефис, подчёркивание; 1–64 символов).", + "group_cycle": "Этот участник создаст циклическую ссылку между группами.", + "group_depth_exceeded": "Глубина вложенности превышает допустимый максимум (8).", + "group_virtual_immutable": "Группа «Internal» управляется системой и не может быть изменена.", + "group_not_found": "Группа не найдена.", + "group_name_taken": "Группа с таким именем уже существует." + }, + "breadcrumb": { + "home": "Главная" + }, + "trash": { + "empty_trash": "Очистить корзину", + "empty_state": "Корзина пуста", + "original_location": "Исходное расположение", + "deleted_date": "Дата удаления", + "remaining": "Осталось", + "actions": "Действия", + "restore": "Восстановить", + "delete_permanently": "Удалить навсегда", + "empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда.", + "groupby": { + "remaining_days": "Осталось дней", + "trashed_time": "Время удаления" + }, + "delete": "Удалить навсегда", + "empty_action": "Очистить корзину" + }, + "daysRemaining": { + "expired": "Истёк", + "today": "Сегодня", + "tomorrow": "Завтра", + "inDays": "{{count}} дн." + }, + "expiryChip": { + "never": "Никогда не истекает", + "expired": "Истёк", + "today": "Истекает сегодня", + "tomorrow": "Истекает завтра", + "inDays": "Истекает через {{count}} дн.", + "onDate": "Истекает {{date}}" + }, + "auth": { + "login_title": "Вход", + "username": "Имя пользователя", + "username_placeholder": "Введите имя пользователя", + "login_identifier": "Имя пользователя или email", + "login_identifier_placeholder": "Введите имя пользователя или email", + "password": "Пароль", + "password_placeholder": "Введите пароль", + "login_button": "Войти", + "no_account": "Нет аккаунта?", + "register": "Зарегистрироваться", + "admin_setup": "Первый запуск?", + "setup": "Настроить администратора", + "register_title": "Создание аккаунта", + "email": "Email", + "email_placeholder": "Введите email", + "confirm_password": "Подтвердите пароль", + "confirm_password_placeholder": "Подтвердите пароль", + "register_button": "Создать аккаунт", + "have_account": "Уже есть аккаунт?", + "login": "Войти", + "setup_title": "Начальная настройка", + "setup_step1": "Админ", + "setup_step2": "Система", + "setup_step3": "Готово", + "admin_username": "Имя администратора", + "admin_email": "Email администратора", + "admin_password": "Пароль администратора", + "create_admin": "Создать администратора", + "back_to_login": "Уже настроено?", + "admin_success": "Аккаунт администратора успешно создан! Теперь вы можете войти.", + "account_success": "Аккаунт успешно создан! Теперь вы можете войти.", + "passwords_mismatch": "Пароли не совпадают", + "admin_create_error": "Ошибка создания аккаунта администратора", + "or": "или", + "sso_login": "Войти через SSO", + "sso_login_provider": "Войти через {{provider}}", + "magicLinkHint": "Нет пароля? Введите ваш email, и мы пришлём вам одноразовую ссылку для входа.", + "magicLinkEmailLabel": "Адрес электронной почты", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "Отправить ссылку для входа", + "magicLinkSent": "Если для этого адреса существует учётная запись, ссылка для входа отправлена. Проверьте входящие.", + "magicLinkUnavailable": "Вход по электронной почте недоступен на этом сервере.", + "magicLinkNetworkError": "Не удалось подключиться к серверу: {{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "Адрес электронной почты", + "magic_hint": "Нет пароля? Введите ваш email, и мы пришлём вам одноразовую ссылку для входа.", + "magic_unavailable": "Вход по электронной почте недоступен на этом сервере.", + "passwords_match": "Passwords match", + "sign_in": "Вход" + }, + "storage": { + "title": "Хранилище", + "calculating": "Вычисление...", + "used": "{{percentage}}% использовано ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "Предварительный просмотр этого типа файлов недоступен.", + "download_file": "Скачать файл", + "zoom_in": "Увеличить", + "zoom_out": "Уменьшить", + "zoom_reset": "Сбросить масштаб" + }, + "language_selector": { + "title": "Добро пожаловать!", + "subtitle": "Выберите язык для продолжения", + "continue": "Продолжить", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ru": "Русский", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands" + } + }, + "favorites": { + "empty_state": "Избранного пока нет", + "empty_hint": "Добавьте файлы или папки в избранное, нажав на звёздочку", + "add": "В избранное", + "remove": "Из избранного", + "added_title": "Добавлено в избранное", + "added_msg": "добавлено в избранное", + "removed_title": "Удалено из избранного", + "removed_msg": "удалено из избранного" + }, + "recent": { + "title": "Недавние", + "clear": "Очистить недавние", + "accessed": "Открыт", + "empty_state": "Нет недавних файлов", + "empty_hint": "Открытые вами файлы будут отображаться здесь", + "loadMore": "Загрузить ещё" + }, + "notifications": { + "file_renamed": "Файл переименован", + "file_renamed_to": "Файл переименован в \"{{name}}\"", + "folder_renamed": "Папка переименована", + "folder_renamed_to": "Папка переименована в \"{{name}}\"", + "file_uploaded": "Файл загружен", + "file_deleted": "Файл перемещён в корзину", + "folder_deleted": "Папка перемещена в корзину", + "item_deleted_permanently": "Элемент удалён навсегда", + "trash_emptied": "Корзина успешно очищена", + "title": "Уведомления", + "empty": "Нет уведомлений", + "link_created": "Ссылка создана", + "share_success": "Ссылка для общего доступа успешно создана", + "upload_files_section_title": "Загрузка здесь недоступна", + "upload_files_section_body": "Перейдите в раздел «Файлы», чтобы загрузить файлы" + }, + "batch": { + "one_selected": "Выбран 1 элемент", + "n_selected": "Выбрано {{count}} элементов", + "confirm_delete": "Вы уверены, что хотите переместить {{count}} элементов в корзину?", + "move_title": "Переместить {{count}} элементов", + "add_favorites": "В избранное", + "move_copy": "Переместить или копировать" + }, + "admin": { + "page_title": "Панель администратора", + "back_to_app": "Назад в OxiCloud", + "loading": "Загрузка…", + "access_denied": "Доступ запрещён", + "access_denied_desc": "Необходимы права администратора.", + "sign_in": "Войти", + "tab_dashboard": "Панель", + "tab_users": "Пользователи", + "tab_oidc": "SSO / OIDC", + "total_users": "Всего пользователей", + "active_users": "Активные", + "admins": "Администраторы", + "version": "Версия", + "storage_overview": "Обзор хранилища", + "used": "Использовано", + "total_quota": "Общая квота", + "usage_pct": "Использование %", + "users_over_80": "Пользователи >80%", + "users_over_quota": "Сверх квоты", + "system": "Система", + "auth_label": "Аутентификация", + "oidc_label": "OIDC", + "quotas_label": "Квоты", + "enabled": "Включено", + "disabled": "Отключено", + "active": "Активен", + "off": "Выкл", + "allow_registration": "Разрешить публичную регистрацию", + "registration_warning": "Публичная регистрация отключена. Только админы могут создавать пользователей.", + "user_management": "Управление пользователями", + "create_user": "Создать пользователя", + "col_user": "Пользователь", + "col_role": "Роль", + "col_auth": "Аутентификация", + "col_status": "Статус", + "col_storage": "Хранилище", + "col_last_login": "Последний вход", + "col_actions": "Действия", + "loading_users": "Загрузка пользователей…", + "failed_load_users": "Не удалось загрузить", + "no_users_found": "Пользователи не найдены", + "showing_users": "Показано {{from}}-{{to}} из {{total}}", + "prev": "Назад", + "next": "Далее", + "inactive": "Неактивен", + "you_badge": "(вы)", + "local": "Локальный", + "never": "Никогда", + "just_now": "Только что", + "minutes_ago": "{{n}} мин назад", + "hours_ago": "{{n}} ч назад", + "days_ago": "{{n}} дн назад", + "edit_quota_title": "Изменить квоту", + "reset_password_title": "Сбросить пароль", + "toggle_role_title": "Сменить роль", + "deactivate_title": "Деактивировать", + "activate_title": "Активировать", + "delete_title": "Удалить", + "sso_title": "Единый вход (OIDC / SSO)", + "enable_sso": "Включить SSO", + "provider_name": "Имя провайдера", + "issuer_url": "URL издателя", + "issuer_url_hint": "URL издателя OpenID Connect", + "auto_discover": "Авто-обнаружение", + "discovering": "Обнаружение…", + "client_id": "Client ID", + "client_secret": "Client Secret", + "client_secret_placeholder": "Оставьте пустым для сохранения", + "secret_configured": "Client secret уже настроен", + "callback_url": "URL обратного вызова", + "callback_url_hint": "(зарегистрируйте в IdP)", + "advanced_settings": "Расширенные настройки", + "scopes": "Области", + "auto_provision": "Автоматически создавать пользователей", + "admin_groups": "Группы администраторов", + "admin_groups_hint": "Имена групп OIDC через запятую", + "disable_password": "Отключить вход по паролю (только OIDC)", + "password_warning": "Это заблокирует ВСЕ входы по паролю!", + "test_btn": "Тест", + "save_btn": "Сохранить", + "saving": "Сохранение…", + "settings_saved": "Настройки сохранены — OIDC теперь {{status}}", + "quota_modal_title": "Обновить квоту", + "quota_user_label": "Пользователь:", + "new_quota": "Новая квота", + "quota_unlimited_hint": "0 для безлимитного", + "cancel": "Отмена", + "create_user_title": "Создать пользователя", + "username_label": "Имя пользователя", + "username_placeholder": "ivanov", + "username_hint": "3–32 символа", + "password_label": "Пароль", + "password_placeholder": "Мин. 8 символов", + "email_label": "Эл. почта", + "email_optional": "(необязательно)", + "email_placeholder": "user@example.com (автоматически если пусто)", + "role_label": "Роль", + "role_user": "Пользователь", + "role_admin": "Админ", + "quota_label": "Квота", + "creating": "Создание…", + "reset_pw_title": "Сбросить пароль", + "new_password_label": "Новый пароль", + "resetting": "Сброс…", + "reset_btn": "Сбросить", + "confirm_role_change": "Изменить роль на {{role}}?", + "confirm_deactivate": "Деактивировать этого пользователя?", + "confirm_activate": "Активировать этого пользователя?", + "confirm_delete_user": "УДАЛИТЬ пользователя \"{{name}}\"? Нельзя отменить!", + "confirm_action": "Подтвердить", + "confirm_yes": "Подтвердить", + "confirm_no": "Отмена", + "error_username_short": "Имя минимум 3 символа", + "error_password_short": "Пароль минимум 8 символов", + "error_generic": "Ошибка", + "error_network": "Ошибка сети: {{message}}", + "error_create_user": "Не удалось создать", + "tab_storage": "Хранилище", + "storage_title": "Настройка хранилища", + "storage_current_backend": "Текущий бэкенд", + "storage_total_blobs": "Всего блобов", + "storage_total_size": "Общий размер", + "storage_dedup_ratio": "Коэффициент дедупликации", + "storage_backend": "Бэкенд", + "storage_local": "Локальный", + "storage_s3": "Совместимый с S3", + "storage_provider_preset": "Пресет провайдера", + "storage_preset_custom": "Пользовательский", + "storage_endpoint_url": "URL конечной точки", + "storage_endpoint_hint": "Оставьте пустым для AWS S3", + "storage_bucket": "Бакет", + "storage_region": "Регион", + "storage_access_key": "Ключ доступа", + "storage_secret_key": "Секретный ключ", + "storage_secret_configured": "Ключ настроен", + "storage_key_placeholder": "Введите новый ключ", + "storage_path_style": "Принудительный стиль пути", + "storage_path_style_hint": "Требуется для MinIO и некоторых S3-совместимых сервисов", + "storage_test_connection": "Проверить соединение", + "storage_test_success": "Соединение успешно", + "storage_test_failure": "Ошибка соединения", + "storage_save": "Сохранить конфигурацию", + "storage_saved": "Конфигурация сохранена", + "storage_migration": "Миграция данных", + "storage_migration_coming_soon": "Инструменты миграции скоро появятся", + "migration_status_label": "Статус миграции", + "migration_start": "Начать миграцию", + "migration_pause": "Пауза", + "migration_resume": "Возобновить", + "migration_verify": "Проверить", + "migration_complete": "Завершить", + "migration_started": "Миграция начата", + "migration_paused_msg": "Миграция приостановлена", + "migration_resumed_msg": "Миграция возобновлена", + "migration_completed_msg": "Миграция успешно завершена", + "migration_verifying": "Проверка...", + "migration_verify_passed": "Проверка пройдена", + "migration_verify_failed": "Проверка не пройдена", + "migration_failed_blobs": "Неудачные блобы", + "testing": "Тестирование...", + "smtp_disabled": "Отключено (хост не задан)", + "smtp_enabled": "Включено", + "smtp_enabled_label": "Статус", + "smtp_intro": "SMTP настраивается исключительно через переменные окружения (OXICLOUD_SMTP_*). Значения ниже считываются с работающего сервера — чтобы изменить их, отредактируйте окружение и перезапустите OxiCloud.", + "smtp_not_configured": "SMTP не настроен на этом сервере.", + "smtp_send_failed": "Сбой отправки.", + "smtp_send_test": "Отправить тестовое письмо", + "smtp_sending": "Отправка…", + "smtp_sent": "Тестовое письмо отправлено.", + "smtp_server_code": "Ответ сервера", + "smtp_test_intro": "Отправляет заранее заданное диагностическое сообщение указанному ниже получателю и сообщает ответ SMTP-сервера, чтобы вы могли сопоставить его с журналами вашего relay.", + "smtp_test_missing_to": "Введите адрес получателя.", + "smtp_test_title": "Отправить тестовое письмо", + "smtp_test_to": "Адрес получателя", + "smtp_title": "Исходящая почта (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "Администраторы", + "confirm_role": "Изменить роль на {{role}}?", + "dashboard": "Панель", + "email": "Эл. почта", + "mig_complete": "Завершить", + "mig_pause": "Пауза", + "mig_resume": "Возобновить", + "mig_verify_failed": "Проверка не пройдена", + "mig_verify_passed": "Проверка пройдена", + "mig_verifying": "Проверка...", + "oidc_auto_provision": "Автоматически создавать пользователей", + "oidc_callback": "URL обратного вызова", + "oidc_client_id": "Client ID", + "oidc_disable_pw": "Отключить вход по паролю (только OIDC)", + "oidc_issuer": "URL издателя", + "oidc_scopes": "Области", + "password": "Пароль", + "quotas": "Квоты", + "reset_pw_for": "Новый пароль для", + "role": "Роль", + "smtp_fail": "Сбой отправки.", + "smtp_send": "Отправить", + "smtp_test": "Отправить тестовое письмо", + "smtp_user_state": "Аутентификация", + "status": "Статус", + "storage": "Хранилище", + "storage_endpoint": "URL конечной точки", + "storage_tab": "Хранилище", + "time_min_ago": "{{n}} мин назад", + "title": "Админ", + "user": "Пользователь", + "username": "Имя пользователя", + "users": "Пользователи" + }, + "profile": { + "page_title": "Профиль", + "back_to_app": "Назад в OxiCloud", + "loading": "Загрузка…", + "not_authenticated": "Не аутентифицирован", + "not_authenticated_desc": "Войдите, чтобы просмотреть свой профиль.", + "sign_in": "Войти", + "role_admin": "Администратор", + "role_user": "Пользователь", + "account_details": "Данные аккаунта", + "username": "Имя пользователя", + "email": "Эл. почта", + "role": "Роль", + "last_login": "Последний вход", + "storage": "Хранилище", + "used": "Использовано", + "quota": "Квота", + "usage": "Использование", + "unlimited": "Безлимитный", + "app_passwords": "Пароли приложений", + "app_pw_desc": "Создайте пароли для клиентов WebDAV, CalDAV и CardDAV. Каждый пароль показывается только один раз.", + "app_pw_label_placeholder": "Метка (напр. Thunderbird, macOS)", + "generate": "Создать", + "generating": "Создание…", + "new_password_for": "Новый пароль для", + "copy_warning": "Скопируйте пароль сейчас. Вы не сможете увидеть его снова.", + "copy_to_clipboard": "Копировать в буфер", + "col_label": "Метка", + "col_created": "Создан", + "col_last_used": "Последнее использование", + "col_status": "Статус", + "active": "Активен", + "revoked": "Отозван", + "revoke_title": "Отозвать", + "no_app_passwords": "Паролей приложений пока нет.", + "client_sessions": "Сессии клиентов", + "client_sessions_desc": "Автоматически создаются при подключении клиента, совместимого с Nextcloud.", + "col_client": "Клиент", + "never": "Никогда", + "just_now": "Только что", + "minutes_ago": "{{n}} мин назад", + "hours_ago": "{{n}} ч назад", + "days_ago": "{{n}} дн назад", + "edit_profile": "Редактировать профиль", + "edit_oidc_managed": "Чтобы изменить ваши данные (имя, фамилию, фотографию профиля, …), обновите их у вашего провайдера идентификации. Изменения появятся при следующем входе.", + "username_claim_hint": "2–64 символа, буквы / цифры / точка / дефис / подчёркивание. После выбора имя пользователя нельзя изменить (клиенты DAV/NextCloud зависят от него).", + "username_already_claimed": "Имя пользователя установлено и не может быть изменено (клиенты DAV/NextCloud зависят от него).", + "given_name": "Имя", + "family_name": "Фамилия", + "notify_on_share": "Уведомлять меня по электронной почте, когда кто-то делится со мной", + "notify_on_share_hint": "Если флажок снят, общие ресурсы по-прежнему будут отображаться в вашей учётной записи — вы просто не будете получать о них письма.", + "save_profile": "Сохранить изменения", + "profile_saved": "Профиль обновлён", + "profile_no_changes": "Нет изменений для сохранения.", + "profile_save_failed": "Не удалось сохранить", + "username_taken_error": "Это имя пользователя уже занято.", + "username_immutable_error": "Ваше имя пользователя уже установлено и не может быть изменено здесь. Свяжитесь с администратором, если нужно переименовать.", + "change_password": "Изменить пароль", + "current_password": "Текущий пароль", + "new_password": "Новый пароль", + "min_8_chars": "Минимум 8 символов", + "confirm_password": "Подтвердите новый пароль", + "update_password": "Обновить пароль", + "updating": "Обновление…", + "password_updated": "Пароль успешно обновлён", + "passwords_no_match": "Пароли не совпадают", + "password_too_short": "Пароль должен быть не менее 8 символов", + "password_change_failed": "Не удалось изменить пароль", + "error_network": "Ошибка сети: {{message}}", + "error_label_required": "Введите метку", + "error_create_pw": "Не удалось создать пароль", + "confirm_revoke": "Отозвать пароль «{{label}}»? Клиенты перестанут работать.", + "error_revoke": "Не удалось отозвать", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "Пароли не совпадают" + }, + "upload": { + "uploading": "Загрузка...", + "files": "файлов", + "complete": "{{count}} / {{total}} загружено" + }, + "storage_quota_exceeded": "Превышена квота хранилища", + "sharedwithme": { + "pageTitle": "Доступно мне", + "pageDescription": "Файлы и папки, которые другие пользователи предоставили вам", + "emptyStateTitle": "Вам ещё ничего не предоставлено", + "emptyStateDesc": "Элементы, которые другие пользователи предоставят вам, появятся здесь", + "loadMore": "Загрузить ещё", + "sharedBy": "Предоставлено", + "colName": "Имя", + "colType": "Тип", + "colSharedBy": "Предоставлено", + "colDate": "Дата предоставления", + "colPermissions": "Права" + }, + "groupby": { + "none": "Нет", + "title": "Группировать по", + "owner": "Владелец", + "shareDate": "Дата общего доступа", + "type": "Тип", + "type.folders": "Папки", + "accessedAt": "Дата доступа", + "modifiedAt": "Дата изменения", + "createdAt": "Дата создания", + "size": "Размер", + "favoriteDate": "Дата добавления в избранное", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "Новые", + "folders": "Папки" + }, + "dateBucket": { + "today": "Сегодня", + "last7days": "Последние 7 дней", + "last30days": "Последние 30 дней", + "unknown": "Неизвестно" + }, + "groups": { + "title": "Управление группами", + "create_button": "Создать группу", + "create_dialog_title": "Новая группа", + "edit_dialog_title": "Переименовать группу", + "name_label": "Имя", + "name_placeholder": "инженеры", + "description_label": "Описание (необязательно)", + "members_section": "Участники", + "add_member_placeholder": "Добавить пользователя или группу…", + "no_members": "Пока нет участников.", + "remove_member": "Удалить", + "delete_group": "Удалить группу", + "delete_confirm": "Удалить группу «{name}»? Все привязанные к ней разрешения будут отозваны.", + "empty_state": "Пока нет групп.", + "load_more": "Загрузить ещё", + "back_to_list": "Назад", + "loading": "Загрузка…", + "virtual_badge": "Системная", + "member_count_zero": "Нет участников", + "member_count_one": "1 участник", + "member_count_other": "{count} участников", + "delete_confirm_label": "Введите имя группы для подтверждения:", + "delete_confirm_mismatch": "Введите имя группы точно для подтверждения.", + "virtual_internal_name": "Внутренние", + "members_loading": "Загрузка участников…", + "members_empty": "Нет участников", + "virtual_internal_explanation": "Каждый внутренний пользователь на этом сервере", + "create": "Создать группу", + "empty": "Пока нет групп.", + "members": "Участники" + }, + "myshares": { + "copyLink": "Копировать ссылку", + "deleteLink": "Удалить ссылку", + "notifyByEmail": "Уведомить по e-mail", + "notifyFailed": "Не удалось отправить уведомление.", + "notifyGroupMembers": "Уведомить участников группы", + "notifyRateLimited": "Слишком много уведомлений для этого получателя — попробуйте позже.", + "removeAccess": "Отозвать доступ", + "resendInvitation": "Отправить приглашение повторно", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "Аудио", + "code": "Код", + "text": "Текст" + }, + "common": { + "add": "Добавить", + "cancel": "Отмена", + "clear": "Clear", + "close": "Закрыть", + "confirm": "Подтвердить", + "copy": "Копировать", + "create": "Создать", + "delete": "Удалить", + "download": "Скачать", + "load_more": "Загрузить ещё", + "loading": "Загрузка…", + "next": "Следующий", + "no": "Нет", + "previous": "Предыдущий", + "remove": "Remove", + "rename": "Переименовать", + "save": "Сохранить", + "search": "Найти", + "yes": "Да" + }, + "device": { + "continue": "Продолжить", + "unknown": "Неизвестно" + }, + "expiryBucket": { + "expired": "Истёк", + "noExpiry": "Без срока", + "today": "Сегодня", + "tomorrow": "Завтра" + }, + "nextcloud": { + "error_title": "Ошибка", + "sign_in_with": "Войти через {{provider}}" + }, + "search": { + "size_label": "Размер", + "title": "Найти", + "type": { + "audio": "Аудио" + }, + "type_label": "Тип" + }, + "sizeBucket": { + "folders": "Папки" + }, + "view": { + "grid": "Сетка", + "list": "Список" + } } diff --git a/frontend/static/locales/zh-TW.json b/frontend/static/locales/zh-TW.json index f0efc548..a759beec 100644 --- a/frontend/static/locales/zh-TW.json +++ b/frontend/static/locales/zh-TW.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "此登入連結已不再有效", - "expired_body": "連結可能已過期或已被使用。我們可以為您發送一個新的 — 幾秒鐘內將會抵達您的收件匣。", - "resend_to": "發送新連結至 {{email}}", - "generic_unavailable": "此登入連結已不再有效。它可能已被使用或已過期。請在登入頁面請求新連結。", - "service_unavailable": "此伺服器未啟用魔法連結登入。", - "internal_error": "登入時發生錯誤。請重試。", - "resend_failure": "發送連結時發生錯誤。請重試。", - "cross_browser_title": "在此裝置上繼續登入?", - "cross_browser_body": "您在與請求時不同的瀏覽器或裝置上開啟了此登入連結。", - "cross_browser_warning": "如果是您請求了此連結,可以安全繼續。否則,請關閉此頁面 — 點擊「繼續」將使其他人登入您的帳戶。", - "cross_browser_continue": "繼續並登入", - "resend_confirmation_title": "請檢查您的收件匣", - "resend_confirmation_body": "如果登入連結屬於活躍帳戶,新連結剛剛已發送。請檢查您的收件匣。", - "return_link": "返回 OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n點擊下方連結開啟:\n{{link}}\n\n該連結僅可使用一次,並將在 {{ttl_hours}} 小時後過期。\n如果您未預期收到此邀請,可以忽略此訊息。\n\n— OxiCloud" - }, - "login": { - "subject": "登入 OxiCloud", - "body": "您好,\n\n使用下方連結登入 OxiCloud。該連結僅可使用一次,並將在 {{ttl_minutes}} 分鐘後過期。請在請求時使用的同一裝置上開啟。\n\n{{link}}\n\n如果您未請求此登入連結,可以忽略此訊息 — 無需進一步操作。\n\n— OxiCloud" - }, - "kind_file": "檔案", - "kind_folder": "資料夾", - "english_fallback_divider": "--- 以下為英文版本 ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "此登入連結已不再有效", + "expired_body": "連結可能已過期或已被使用。我們可以為您發送一個新的 — 幾秒鐘內將會抵達您的收件匣。", + "resend_to": "發送新連結至 {{email}}", + "generic_unavailable": "此登入連結已不再有效。它可能已被使用或已過期。請在登入頁面請求新連結。", + "service_unavailable": "此伺服器未啟用魔法連結登入。", + "internal_error": "登入時發生錯誤。請重試。", + "resend_failure": "發送連結時發生錯誤。請重試。", + "cross_browser_title": "在此裝置上繼續登入?", + "cross_browser_body": "您在與請求時不同的瀏覽器或裝置上開啟了此登入連結。", + "cross_browser_warning": "如果是您請求了此連結,可以安全繼續。否則,請關閉此頁面 — 點擊「繼續」將使其他人登入您的帳戶。", + "cross_browser_continue": "繼續並登入", + "resend_confirmation_title": "請檢查您的收件匣", + "resend_confirmation_body": "如果登入連結屬於活躍帳戶,新連結剛剛已發送。請檢查您的收件匣。", + "return_link": "返回 OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n點擊下方連結開啟:\n{{link}}\n\n該連結僅可使用一次,並將在 {{ttl_hours}} 小時後過期。\n如果您未預期收到此邀請,可以忽略此訊息。\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n開啟 OxiCloud 檢視您的新分享:\n{{login_link}}\n\n您可能還有來自 {{inviter}} 的其他新分享 — 登入以檢視所有與您分享的項目。\n\n— OxiCloud\n\n您收到此訊息是因為您擁有 OxiCloud 帳戶且分享通知偏好已開啟。您可以在個人資料中關閉它(當有人與我分享時透過電子郵件通知我)。" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "極簡雲端儲存系統" - }, - "nav": { - "files": "檔案", - "shared": "共享", - "recent": "最近", - "favorites": "收藏", - "photos": "照片", - "music": "音樂", - "trash": "回收站", - "sharedwithme": "與我共享" - }, - "photos": { - "empty_state": "還沒有照片", - "empty_hint": "上傳圖片或影片即可在此檢視", - "items_selected": "已選擇", - "view_daily": "日", - "view_monthly": "月", - "view_yearly": "年" - }, - "music": { - "create_playlist": "建立播放列表", - "playlists": "播放列表", - "no_playlists": "還沒有播放列表", - "select_playlist": "選擇一個播放列表", - "select_hint": "從側邊欄選擇播放列表或建立新播放列表", - "add_tracks": "新增曲目", - "no_tracks": "此播放列表中沒有曲目", - "unknown_artist": "未知藝術家", - "unknown_title": "未知", - "confirm_delete": "刪除此播放列表?", - "playlist_name": "播放列表名稱", - "create": "建立", - "delete": "刪除", - "share": "分享", - "edit": "編輯", - "play_all": "全部播放", - "shuffle": "隨機播放", - "repeat": "重複", - "repeat_one": "單曲迴圈", - "queue": "播放佇列", - "queue_empty": "播放佇列為空", - "not_playing": "未播放", - "play": "播放", - "pause": "暫停", - "previous": "上一首", - "next": "下一首", - "volume": "音量", - "mute": "靜音", - "unmute": "取消靜音", - "title": "標題", - "artist": "藝術家", - "album": "專輯", - "tracks": "首曲目", - "add": "新增", - "added": "已新增!", - "added_to_playlist": "已新增到播放列表", - "add_to_playlist": "新增到播放列表", - "load_error": "載入播放列表出錯", - "add_error": "無法將曲目新增到播放列表", - "no_playlists_yet": "暫無播放列表。請先建立一個!", - "selected_files": "已選擇:", - "error": "錯誤", - "search_audio": "搜尋音訊檔案…", - "no_audio_files": "未找到音訊檔案", - "selected": "已選擇", - "loading": "載入中…", - "search_error": "無法載入音訊檔案", - "adding": "新增中…", - "can_write": "可以編輯", - "cover_updated": "封面已更新", - "empty_hint": "建立你的第一個播放列表來開始整理你的音樂", - "make_private": "設為私人", - "make_public": "設為公開", - "manage_shares": "管理共享", - "no_shares": "尚未共享", - "playback_error": "播放失敗", - "private": "私人", - "public": "公開", - "read_only": "唯讀", - "remove": "移除", - "remove_share": "移除共享", - "set_cover": "設定封面", - "share_with_user": "使用者 ID 或電子郵件", - "toggle_public": "可見性", - "track_removed": "曲目已移除" - }, - "actions": { - "search": "搜尋檔案...", - "new_folder": "新建資料夾", - "upload": "上傳", - "upload_files": "上傳檔案", - "upload_folder": "上傳資料夾", - "upload.uploading": "上傳中...", - "upload.complete": "{count} / {total} 已上傳", - "upload.files": "檔案", - "rename": "重新命名", - "move": "移動到...", - "move_to": "移動到", - "delete": "刪除", - "download": "下載", - "view": "檢視", - "cancel": "取消", - "confirm": "確認", - "share": "共享", - "favorite": "新增到收藏", - "unfavorite": "取消收藏", - "copy": "複製", - "notify": "通知", - "send": "傳送", - "clear_recent": "清除最近", - "logout": "退出登入", - "create": "建立", - "search_btn": "搜尋", - "close": "關閉", - "delete_permanently": "永久刪除", - "empty_trash": "清空回收站", - "open_parent_folder": "轉到父資料夾", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "外觀", - "about": "關於 OxiCloud", - "about_description": "基於 Rust 和整潔架構構建的雲端儲存平臺。快速、安全、私密。", - "admin_panel": "管理面板", - "profile": "我的資料", - "role_user": "使用者", - "theme": { - "light": "淺色", - "dark": "深色", - "auto": "跟隨系統" + "login": { + "subject": "登入 OxiCloud", + "body": "您好,\n\n使用下方連結登入 OxiCloud。該連結僅可使用一次,並將在 {{ttl_minutes}} 分鐘後過期。請在請求時使用的同一裝置上開啟。\n\n{{link}}\n\n如果您未請求此登入連結,可以忽略此訊息 — 無需進一步操作。\n\n— OxiCloud" }, - "manage_groups": "管理群組" + "kind_file": "檔案", + "kind_folder": "資料夾", + "english_fallback_divider": "--- 以下為英文版本 ---" + } }, - "share": { - "dialogTitle": "共享連結", - "linkLabel": "共享連結:", - "copyLink": "複製", - "permissions": "許可權:", - "permissionRead": "讀取", - "permissionWrite": "寫入", - "permissionReshare": "再共享", - "password": "密碼保護:", - "generatePassword": "生成", - "expiration": "過期日期:", - "update": "更新共享", - "remove": "移除共享", - "notifyTitle": "傳送通知", - "notifyEmailLabel": "電子郵件地址:", - "notifyMessageLabel": "訊息(可選):", - "notifySend": "傳送通知", - "shareWithOthers": "與他人共享", - "sharePublicly": "公開共享", - "shareSettings": "共享設定", - "shareCopied": "連結已複製到剪貼簿", - "shareCreated": "共享連結建立成功", - "shareUpdated": "共享設定更新成功", - "shareRemoved": "共享已移除", - "inviteByEmail": "透過郵件邀請 — 將傳送邀請", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "共享連結", - "share_linkLabel": "共享連結:", - "share_copyLink": "複製", - "share_permissions": "許可權:", - "share_permissionRead": "讀取", - "share_permissionWrite": "寫入", - "share_permissionReshare": "再共享", - "share_password": "密碼保護:", - "share_generatePassword": "生成", - "share_expiration": "過期日期:", - "share_update": "更新共享", - "share_remove": "移除共享", - "share_notifyTitle": "傳送通知", - "share_notifyEmailLabel": "電子郵件地址:", - "share_notifyMessageLabel": "訊息(可選):", - "share_notifySend": "傳送通知", - "shared": { - "backToFiles": "返回檔案", - "pageTitle": "共享資源", - "pageDescription": "管理你的共享檔案和資料夾", - "filterType": "型別:", - "filterAll": "全部", - "filterFiles": "檔案", - "filterFolders": "資料夾", - "sortBy": "排序依據:", - "sortByName": "名稱", - "sortByDate": "共享日期", - "sortByExpiration": "過期日期", - "search": "搜尋", - "colName": "名稱", - "colType": "型別", - "colDateShared": "共享日期", - "colExpiration": "過期日期", - "colPermissions": "許可權", - "colPassword": "密碼", - "colActions": "操作", - "emptyStateTitle": "尚未有共享資源", - "emptyStateDesc": "當你共享檔案或資料夾時,它們會出現在這裡", - "goToFiles": "前往檔案", - "typeFile": "檔案", - "typeFolder": "資料夾", - "noExpiration": "無過期", - "hasPassword": "有", - "noPassword": "無", - "editShare": "編輯共享", - "notifyShare": "通知某人", - "copyLink": "複製連結", - "removeShare": "移除共享", - "linkCopied": "連結已複製到剪貼簿!", - "linkCopyFailed": "複製連結失敗", - "itemUpdated": "共享設定更新成功", - "itemRemoved": "共享已移除成功", - "invalidEmail": "請輸入有效的電子郵件地址", - "notificationSent": "通知已成功傳送", - "notificationFailed": "傳送通知失敗", - "shared_backToFiles": "返回檔案", - "shared_colActions": "操作", - "shared_colDateShared": "共享日期", - "shared_colExpiration": "過期日期", - "shared_colName": "名稱", - "shared_colPassword": "密碼", - "shared_colPermissions": "許可權", - "shared_colType": "類型", - "shared_copyLink": "複製連結", - "shared_editShare": "編輯共享", - "shared_emptyStateDesc": "當你共享檔案或資料夾時,它們會顯示在此", - "shared_emptyStateTitle": "尚無共享資源", - "shared_filterAll": "全部", - "shared_filterFiles": "檔案", - "shared_filterFolders": "資料夾", - "shared_filterType": "類型:", - "shared_goToFiles": "前往檔案", - "shared_hasPassword": "是", - "shared_invalidEmail": "請輸入有效的電子郵件地址", - "shared_itemRemoved": "共享已成功移除", - "shared_itemUpdated": "共享設定已成功更新", - "shared_linkCopied": "連結已複製到剪貼簿!", - "shared_linkCopyFailed": "複製連結失敗", - "shared_noExpiration": "永不過期", - "shared_noPassword": "否", - "shared_notificationFailed": "傳送通知失敗", - "shared_notificationSent": "通知已成功傳送", - "shared_notifyShare": "通知對方", - "shared_pageDescription": "管理你的共享檔案與資料夾", - "shared_pageTitle": "共享資源", - "shared_removeShare": "移除共享", - "shared_search": "搜尋", - "shared_sortBy": "排序方式:", - "shared_sortByDate": "共享日期", - "shared_sortByExpiration": "過期日期", - "shared_sortByName": "名稱", - "shared_typeFile": "檔案", - "shared_typeFolder": "資料夾" - }, - "files": { - "name": "名稱", - "type": "型別", - "size": "大小", - "modified": "修改日期", - "no_files": "此資料夾中沒有檔案", - "empty_hint": "上傳檔案或建立資料夾以開始使用", - "loading": "正在載入檔案…", - "view_grid": "網格檢視", - "view_list": "列表檢視", - "file_types": { - "document": "文件", - "image": "圖片", - "video": "影片", - "audio": "音訊", - "pdf": "PDF", - "text": "文字", - "folder": "資料夾", - "spreadsheet": "電子表格", - "presentation": "簡報", - "archive": "壓縮檔案", - "installer": "安裝程式", - "code": "程式碼" - }, - "owner": "擁有者" - }, - "dialogs": { - "rename_folder": "重新命名資料夾", - "new_name": "新名稱", - "new_folder_title": "新建資料夾", - "folder_name": "資料夾名稱", - "folder_placeholder": "我的資料夾", - "rename_title": "重新命名", - "move_file": "移動檔案", - "select_destination": "選擇目標資料夾", - "root": "根目錄", - "delete_confirmation": "你確定要刪除", - "and_contents": "及其所有內容", - "no_undo": "此操作無法撤銷", - "share_file": "共享檔案", - "share_folder": "共享資料夾", - "existing_shares": "現有共享", - "share_options": "共享選項", - "password": "密碼", - "expiration": "過期日期", - "permissions": "許可權", - "generated_link": "生成的連結", - "notify": "傳送通知", - "recipient": "收件人", - "message": "訊息", - "confirm_delete": "移至回收站", - "confirm_delete_file": "確定要將檔案「{{name}}」移至回收站嗎?", - "confirm_delete_folder": "確定要將資料夾「{{name}}」及其所有內容移至回收站嗎?", - "confirm_delete_share": "刪除共享連結", - "confirm_delete_share_msg": "確定要刪除此共享連結嗎?", - "confirm_empty_trash": "清空回收站", - "confirm_permanent_delete": "永久刪除", - "confirm_permanent_delete_msg": "確定要永久刪除此項目嗎?此操作無法復原。", - "confirm_title": "確認操作", - "go_to_parent": ".. (上層資料夾)", - "move_folder": "移動資料夾", - "no_subfolders": "沒有子資料夾", - "rename_file": "重新命名檔案", - "select_this_folder": "選擇此資料夾", - "move_to_home": "移動到主資料夾" - }, - "dropzone": { - "drag_files": "將檔案拖到這裡,或點選選擇", - "drop_files": "釋放檔案以上傳" - }, - "permissions": { - "read": "讀取", - "write": "寫入", - "reshare": "再共享" - }, - "errors": { - "file_not_found": "檔案未找到", - "folder_not_found": "資料夾未找到", - "delete_error": "刪除時出錯", - "upload_error": "上傳檔案時出錯", - "rename_error": "重新命名時出錯", - "move_error": "移動時出錯", - "empty_name": "名稱不能為空", - "name_exists": "已存在同名檔案或資料夾", - "generic_error": "發生錯誤", - "group_name_invalid": "群組名稱必須符合電子郵件前綴格式(字母、數字、點、連字符、下劃線;1–64 個字元)。", - "group_cycle": "此成員會在群組之間形成循環參照。", - "group_depth_exceeded": "嵌套深度超過允許的最大值(8)。", - "group_virtual_immutable": "「Internal」群組由系統管理,無法修改。", - "group_not_found": "找不到群組。", - "group_name_taken": "已存在同名群組。" - }, - "breadcrumb": { - "home": "主頁" - }, - "trash": { - "empty_trash": "清空回收站", - "empty_state": "回收站為空", - "original_location": "原始位置", - "deleted_date": "刪除日期", - "remaining": "剩餘", - "actions": "操作", - "restore": "恢復", - "delete_permanently": "永久刪除", - "empty_confirm": "你確定要清空回收站嗎?這將永久刪除所有專案。", - "groupby": { - "remaining_days": "剩餘天數", - "trashed_time": "刪除時間" - } - }, - "daysRemaining": { - "expired": "已過期", - "today": "今天", - "tomorrow": "明天", - "inDays": "{{count}} 天" - }, - "expiryChip": { - "never": "永不過期", - "expired": "已過期", - "today": "今天到期", - "tomorrow": "明天到期", - "inDays": "{{count}} 天後到期", - "onDate": "於 {{date}} 到期" - }, - "auth": { - "login_title": "登入", - "username": "使用者名稱", - "username_placeholder": "輸入你的使用者名稱", - "login_identifier": "使用者名稱或電子郵件", - "login_identifier_placeholder": "請輸入使用者名稱或電子郵件", - "password": "密碼", - "password_placeholder": "輸入你的密碼", - "login_button": "登入", - "no_account": "沒有賬號?", - "register": "註冊", - "admin_setup": "首次使用?", - "setup": "設定管理員", - "register_title": "建立賬號", - "email": "電子郵件", - "email_placeholder": "輸入你的電子郵件", - "confirm_password": "確認密碼", - "confirm_password_placeholder": "確認你的密碼", - "register_button": "建立賬號", - "have_account": "已有賬號?", - "login": "登入", - "setup_title": "初始設定", - "setup_step1": "管理員", - "setup_step2": "系統", - "setup_step3": "完成", - "admin_username": "管理員使用者名稱", - "admin_email": "管理員電子郵件", - "admin_password": "管理員密碼", - "create_admin": "建立管理員", - "back_to_login": "已設定完成?", - "admin_success": "管理員賬號建立成功!您現在可以登入。", - "account_success": "賬號建立成功!您現在可以登入。", - "passwords_mismatch": "密碼不匹配", - "admin_create_error": "建立管理員賬號時出錯", - "or": "或", - "sso_login": "使用 SSO 登入", - "sso_login_provider": "使用 {{provider}} 登入", - "magicLinkHint": "沒有密碼?輸入您的電子郵件,我們將向您發送一次性登入連結。", - "magicLinkEmailLabel": "電子郵件地址", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "傳送登入連結", - "magicLinkSent": "若該電子郵件存在帳號,登入連結已發送。請查收您的收件匣。", - "magicLinkUnavailable": "此伺服器不支援電子郵件登入。", - "magicLinkNetworkError": "無法連線到伺服器:{{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "儲存空間", - "calculating": "計算中...", - "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "無法預覽此檔案型別。", - "download_file": "下載檔案", - "zoom_in": "放大", - "zoom_out": "縮小", - "zoom_reset": "重置縮放" - }, - "language_selector": { - "title": "歡迎!", - "subtitle": "選擇您的語言以繼續", - "continue": "繼續", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "還沒有收藏", - "empty_hint": "為檔案或資料夾新增星標以將其新增到收藏夾", - "add": "新增到收藏夾", - "remove": "從收藏夾移除", - "added_title": "已新增到收藏", - "added_msg": "已新增到收藏", - "removed_title": "已從收藏移除", - "removed_msg": "已從收藏移除" - }, - "recent": { - "title": "最近", - "clear": "清除最近", - "accessed": "訪問於", - "empty_state": "沒有最近檔案", - "empty_hint": "您開啟的檔案將顯示在這裡", - "loadMore": "載入更多" - }, - "batch": { - "one_selected": "已選擇 1 個專案", - "n_selected": "已選擇 {{count}} 個專案", - "confirm_delete": "確定要將 {{count}} 個專案移至回收站嗎?", - "move_title": "移動 {{count}} 個專案", - "add_favorites": "新增到收藏夾", - "move_copy": "移動或複製" - }, - "admin": { - "page_title": "管理面板", - "back_to_app": "返回 OxiCloud", - "loading": "載入中…", - "access_denied": "拒絕訪問", - "access_denied_desc": "需要管理員許可權。", - "sign_in": "登入", - "tab_dashboard": "儀表盤", - "tab_users": "使用者", - "tab_oidc": "SSO / OIDC", - "total_users": "使用者總數", - "active_users": "活躍使用者", - "admins": "管理員", - "version": "版本", - "storage_overview": "儲存概覽", - "used": "已使用", - "total_quota": "總配額", - "usage_pct": "使用率", - "users_over_80": "超過80%配額", - "users_over_quota": "超過配額", - "system": "系統", - "auth_label": "認證", - "oidc_label": "OIDC", - "quotas_label": "配額", - "enabled": "已啟用", - "disabled": "已禁用", - "active": "活躍", - "off": "關閉", - "allow_registration": "允許公開自助註冊", - "registration_warning": "公開註冊已禁用。只有管理員可以建立新使用者。", - "user_management": "使用者管理", - "create_user": "建立使用者", - "col_user": "使用者", - "col_role": "角色", - "col_auth": "認證", - "col_status": "狀態", - "col_storage": "儲存", - "col_last_login": "最後登入", - "col_actions": "操作", - "loading_users": "正在載入使用者…", - "failed_load_users": "載入失敗", - "no_users_found": "未找到使用者", - "showing_users": "顯示 {{from}}-{{to}} / {{total}}", - "prev": "上一頁", - "next": "下一頁", - "inactive": "未啟用", - "you_badge": "(你)", - "local": "本地", - "never": "從未", - "just_now": "剛剛", - "minutes_ago": "{{n}}分鐘前", - "hours_ago": "{{n}}小時前", - "days_ago": "{{n}}天前", - "edit_quota_title": "編輯配額", - "reset_password_title": "重置密碼", - "toggle_role_title": "切換角色", - "deactivate_title": "停用", - "activate_title": "啟用", - "delete_title": "刪除", - "sso_title": "單點登入 (OIDC / SSO)", - "enable_sso": "啟用 SSO 認證", - "provider_name": "提供商名稱", - "issuer_url": "發行者 URL", - "issuer_url_hint": "您的身份提供商的 OpenID Connect 發行者 URL", - "auto_discover": "自動發現", - "discovering": "發現中…", - "client_id": "客戶端 ID", - "client_secret": "客戶端金鑰", - "client_secret_placeholder": "留空以保留當前值", - "secret_configured": "已配置客戶端金鑰", - "callback_url": "回撥 URL", - "callback_url_hint": "(在您的 IdP 中註冊)", - "advanced_settings": "高階設定", - "scopes": "範圍", - "auto_provision": "首次登入時自動配置使用者", - "admin_groups": "管理組", - "admin_groups_hint": "對映到管理員角色的逗號分隔 OIDC 組名", - "disable_password": "禁用密碼登入 (僅 OIDC)", - "password_warning": "這將阻止所有基於密碼的登入!", - "test_btn": "測試", - "save_btn": "儲存", - "saving": "儲存中…", - "settings_saved": "設定已儲存 — OIDC 現在 {{status}}", - "quota_modal_title": "更新儲存配額", - "quota_user_label": "使用者:", - "new_quota": "新配額", - "quota_unlimited_hint": "0表示無限制", - "cancel": "取消", - "create_user_title": "建立新使用者", - "username_label": "使用者名稱", - "username_placeholder": "zhangsan", - "username_hint": "3–32個字元", - "password_label": "密碼", - "password_placeholder": "至少8個字元", - "email_label": "郵箱", - "email_optional": "(可選)", - "email_placeholder": "user@example.com (留空自動生成)", - "role_label": "角色", - "role_user": "使用者", - "role_admin": "管理員", - "quota_label": "配額", - "creating": "建立中…", - "reset_pw_title": "重置密碼", - "new_password_label": "新密碼", - "resetting": "重置中…", - "reset_btn": "重置", - "confirm_role_change": "將角色更改為 {{role}}?", - "confirm_deactivate": "確定要停用此使用者嗎?", - "confirm_activate": "確定要啟用此使用者嗎?", - "confirm_delete_user": "刪除使用者 \"{{name}}\"?此操作無法撤消!", - "confirm_action": "確認操作", - "confirm_yes": "確認", - "confirm_no": "取消", - "error_username_short": "使用者名稱至少需要3個字元", - "error_password_short": "密碼至少需要8個字元", - "error_generic": "失敗", - "error_network": "網路錯誤:{{message}}", - "error_create_user": "建立使用者失敗", - "tab_storage": "儲存", - "storage_title": "儲存配置", - "storage_current_backend": "當前後端", - "storage_total_blobs": "總塊數", - "storage_total_size": "總大小", - "storage_dedup_ratio": "去重比率", - "storage_backend": "後端", - "storage_local": "本地", - "storage_s3": "S3 相容", - "storage_provider_preset": "提供商預設", - "storage_preset_custom": "自定義", - "storage_endpoint_url": "端點 URL", - "storage_endpoint_hint": "AWS S3 請留空", - "storage_bucket": "儲存桶", - "storage_region": "地區", - "storage_access_key": "訪問金鑰", - "storage_secret_key": "金鑰", - "storage_secret_configured": "金鑰已配置", - "storage_key_placeholder": "輸入新金鑰", - "storage_path_style": "強制路徑風格", - "storage_path_style_hint": "MinIO 及某些 S3 相容服務需要此選項", - "storage_test_connection": "測試連線", - "storage_test_success": "連線成功", - "storage_test_failure": "連線失敗", - "storage_save": "儲存配置", - "storage_saved": "配置已儲存", - "storage_migration": "資料遷移", - "storage_migration_coming_soon": "遷移工具即將推出", - "migration_status_label": "遷移狀態", - "migration_start": "開始遷移", - "migration_pause": "暫停", - "migration_resume": "繼續", - "migration_verify": "驗證", - "migration_complete": "完成", - "migration_started": "遷移已開始", - "migration_paused_msg": "遷移已暫停", - "migration_resumed_msg": "遷移已繼續", - "migration_completed_msg": "遷移成功完成", - "migration_verifying": "正在驗證...", - "migration_verify_passed": "驗證透過", - "migration_verify_failed": "驗證失敗", - "migration_failed_blobs": "失敗的塊", - "testing": "正在測試...", - "smtp_disabled": "已停用(未設定主機)", - "smtp_enabled": "已啟用", - "smtp_enabled_label": "狀態", - "smtp_intro": "SMTP 僅透過環境變數(OXICLOUD_SMTP_*)設定。下方數值是從運行中的伺服器讀取的 — 如需修改,請編輯環境變數並重新啟動 OxiCloud。", - "smtp_not_configured": "此伺服器未設定 SMTP。", - "smtp_send_failed": "傳送失敗。", - "smtp_send_test": "傳送測試郵件", - "smtp_sending": "傳送中…", - "smtp_sent": "測試郵件已傳送。", - "smtp_server_code": "伺服器回應", - "smtp_test_intro": "向下方收件者傳送預設的診斷訊息,並回報 SMTP 伺服器的回應,以便您與轉發日誌進行對照。", - "smtp_test_missing_to": "請輸入收件者地址。", - "smtp_test_title": "傳送測試郵件", - "smtp_test_to": "收件者地址", - "smtp_title": "外寄郵件 (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "個人資料", - "back_to_app": "返回 OxiCloud", - "loading": "載入中…", - "not_authenticated": "未認證", - "not_authenticated_desc": "請登入以檢視您的個人資料。", - "sign_in": "登入", - "role_admin": "管理員", - "role_user": "使用者", - "account_details": "賬戶詳情", - "username": "使用者名稱", - "email": "郵箱", - "role": "角色", - "last_login": "最後登入", - "storage": "儲存", - "used": "已使用", - "quota": "配額", - "usage": "使用率", - "unlimited": "無限制", - "app_passwords": "應用密碼", - "app_pw_desc": "為 WebDAV、CalDAV 和 CardDAV 客戶端生成密碼。每個密碼只顯示一次。", - "app_pw_label_placeholder": "標籤(如 Thunderbird、macOS)", - "generate": "生成", - "generating": "生成中…", - "new_password_for": "新密碼用於", - "copy_warning": "請立即複製此密碼,之後將無法再次檢視。", - "copy_to_clipboard": "複製到剪貼簿", - "col_label": "標籤", - "col_created": "建立時間", - "col_last_used": "最後使用", - "col_status": "狀態", - "active": "活躍", - "revoked": "已撤銷", - "revoke_title": "撤銷", - "no_app_passwords": "暫無應用密碼。", - "client_sessions": "客戶端會話", - "client_sessions_desc": "連線 Nextcloud 相容客戶端時自動生成。", - "col_client": "客戶端", - "never": "從未", - "just_now": "剛剛", - "minutes_ago": "{{n}}分鐘前", - "hours_ago": "{{n}}小時前", - "days_ago": "{{n}}天前", - "edit_profile": "編輯個人資料", - "edit_oidc_managed": "要更改您的資訊(姓名、名字、頭像等),請前往您的身分提供者更新。變更將在您下次登入時顯示。", - "username_claim_hint": "2-64 個字元,字母 / 數字 / 點 / 短橫線 / 底線。一旦選定,使用者名稱將無法更改(DAV/NextCloud 用戶端依賴它)。", - "username_already_claimed": "使用者名稱已設定,不可更改(DAV/NextCloud 用戶端依賴它)。", - "given_name": "名", - "family_name": "姓", - "notify_on_share": "當有人與我分享時透過電子郵件通知我", - "notify_on_share_hint": "取消勾選後,分享項目仍會顯示在您的帳戶中 — 只是不會收到相關郵件通知。", - "save_profile": "儲存變更", - "profile_saved": "個人資料已更新", - "profile_no_changes": "沒有變更可儲存。", - "profile_save_failed": "儲存失敗", - "username_taken_error": "該使用者名稱已被使用。", - "username_immutable_error": "您的使用者名稱已設定,無法在此更改。如需重新命名,請聯絡管理員。", - "change_password": "修改密碼", - "current_password": "當前密碼", - "new_password": "新密碼", - "min_8_chars": "至少8個字元", - "confirm_password": "確認新密碼", - "update_password": "更新密碼", - "updating": "更新中…", - "password_updated": "密碼更新成功", - "passwords_no_match": "密碼不匹配", - "password_too_short": "密碼至少需要8個字元", - "password_change_failed": "修改密碼失敗", - "error_network": "網路錯誤:{{message}}", - "error_label_required": "請輸入標籤", - "error_create_pw": "建立應用密碼失敗", - "confirm_revoke": "撤銷應用密碼\"{{label}}\"?使用此密碼的客戶端將停止工作。", - "error_revoke": "撤銷失敗", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "notifications": { - "file_renamed": "檔案已重新命名", - "file_renamed_to": "檔案已重新命名為\"{{name}}\"", - "folder_renamed": "資料夾已重新命名", - "folder_renamed_to": "資料夾已重新命名為\"{{name}}\"", - "file_uploaded": "檔案已上傳", - "file_deleted": "檔案已移至回收站", - "folder_deleted": "資料夾已移至回收站", - "item_deleted_permanently": "專案已永久刪除", - "trash_emptied": "回收站已清空", - "title": "通知", - "empty": "暫無通知", - "link_created": "連結已建立", - "share_success": "分享連結建立成功", - "upload_files_section_title": "此處不支援上傳", - "upload_files_section_body": "請前往檔案部分上傳檔案" - }, - "upload": { - "uploading": "正在上傳...", - "files": "個檔案", - "complete": "已上傳 {{count}} / {{total}}" - }, - "storage_quota_exceeded": "儲存配額已超限", - "sharedwithme": { - "pageTitle": "與我共享", - "pageDescription": "其他使用者與您共享的檔案和資料夾", - "emptyStateTitle": "目前沒有內容與您共享", - "emptyStateDesc": "其他使用者與您共享的項目將顯示在這裡", - "loadMore": "載入更多", - "sharedBy": "共享者", - "colName": "名稱", - "colType": "類型", - "colSharedBy": "共享者", - "colDate": "共享日期", - "colPermissions": "權限" - }, - "groupby": { - "none": "無", - "title": "分組方式", - "owner": "擁有者", - "shareDate": "分享日期", - "type": "類型", - "type.folders": "資料夾", - "accessedAt": "存取日期", - "modifiedAt": "修改日期", - "createdAt": "建立日期", - "size": "大小", - "favoriteDate": "收藏日期", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "新增" - }, - "dateBucket": { - "today": "今天", - "last7days": "近7天", - "last30days": "近30天" - }, - "groups": { - "title": "管理群組", - "create_button": "建立群組", - "create_dialog_title": "新群組", - "edit_dialog_title": "重新命名群組", - "name_label": "名稱", - "name_placeholder": "engineering", - "description_label": "描述(選填)", - "members_section": "成員", - "add_member_placeholder": "新增使用者或群組…", - "no_members": "尚無成員。", - "remove_member": "移除", - "delete_group": "刪除群組", - "delete_confirm": "刪除群組「{name}」?引用此群組的所有授權將被撤銷。", - "empty_state": "尚無群組。", - "load_more": "載入更多", - "back_to_list": "返回", - "loading": "載入中…", - "virtual_badge": "系統", - "member_count_zero": "無成員", - "member_count_one": "1 個成員", - "member_count_other": "{count} 個成員", - "delete_confirm_label": "請輸入群組名稱以確認:", - "delete_confirm_mismatch": "請準確輸入群組名稱以確認。", - "virtual_internal_name": "內部", - "members_loading": "正在載入成員…", - "members_empty": "無成員", - "virtual_internal_explanation": "本伺服器上的所有內部使用者" - }, - "myshares": { - "copyLink": "複製連結", - "deleteLink": "刪除連結", - "notifyByEmail": "透過郵件通知", - "notifyFailed": "無法傳送通知。", - "notifyGroupMembers": "通知群組成員", - "notifyRateLimited": "對此收件者的通知過多 — 請稍後重試。", - "removeAccess": "移除存取權限", - "resendInvitation": "重新傳送邀請郵件" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n開啟 OxiCloud 檢視您的新分享:\n{{login_link}}\n\n您可能還有來自 {{inviter}} 的其他新分享 — 登入以檢視所有與您分享的項目。\n\n— OxiCloud\n\n您收到此訊息是因為您擁有 OxiCloud 帳戶且分享通知偏好已開啟。您可以在個人資料中關閉它(當有人與我分享時透過電子郵件通知我)。" + } } + }, + "app": { + "title": "OxiCloud", + "description": "極簡雲端儲存系統" + }, + "nav": { + "files": "檔案", + "shared": "共享", + "recent": "最近", + "favorites": "收藏", + "photos": "照片", + "music": "音樂", + "trash": "回收站", + "sharedwithme": "與我共享", + "profile": "個人資料", + "shared_with_me": "與我共享" + }, + "photos": { + "empty_state": "還沒有照片", + "empty_hint": "上傳圖片或影片即可在此檢視", + "items_selected": "已選擇", + "view_daily": "日", + "view_monthly": "月", + "view_yearly": "年", + "group_by": "分組方式" + }, + "music": { + "create_playlist": "建立播放列表", + "playlists": "播放列表", + "no_playlists": "還沒有播放列表", + "select_playlist": "選擇一個播放列表", + "select_hint": "從側邊欄選擇播放列表或建立新播放列表", + "add_tracks": "新增曲目", + "no_tracks": "此播放列表中沒有曲目", + "unknown_artist": "未知藝術家", + "unknown_title": "未知", + "confirm_delete": "刪除此播放列表?", + "playlist_name": "播放列表名稱", + "create": "建立", + "delete": "刪除", + "share": "分享", + "edit": "編輯", + "play_all": "全部播放", + "shuffle": "隨機播放", + "repeat": "重複", + "repeat_one": "單曲迴圈", + "queue": "播放佇列", + "queue_empty": "播放佇列為空", + "not_playing": "未播放", + "play": "播放", + "pause": "暫停", + "previous": "上一首", + "next": "下一首", + "volume": "音量", + "mute": "靜音", + "unmute": "取消靜音", + "title": "標題", + "artist": "藝術家", + "album": "專輯", + "tracks": "首曲目", + "add": "新增", + "added": "已新增!", + "added_to_playlist": "已新增到播放列表", + "add_to_playlist": "新增到播放列表", + "load_error": "載入播放列表出錯", + "add_error": "無法將曲目新增到播放列表", + "no_playlists_yet": "暫無播放列表。請先建立一個!", + "selected_files": "已選擇:", + "error": "錯誤", + "search_audio": "搜尋音訊檔案…", + "no_audio_files": "未找到音訊檔案", + "selected": "已選擇", + "loading": "載入中…", + "search_error": "無法載入音訊檔案", + "adding": "新增中…", + "can_write": "可以編輯", + "cover_updated": "封面已更新", + "empty_hint": "建立你的第一個播放列表來開始整理你的音樂", + "make_private": "設為私人", + "make_public": "設為公開", + "manage_shares": "管理共享", + "no_shares": "尚未共享", + "playback_error": "播放失敗", + "private": "私人", + "public": "公開", + "read_only": "唯讀", + "remove": "移除", + "remove_share": "移除共享", + "set_cover": "設定封面", + "share_with_user": "使用者 ID 或電子郵件", + "toggle_public": "可見性", + "track_removed": "曲目已移除", + "prev": "上一首" + }, + "actions": { + "search": "搜尋檔案...", + "new_folder": "新建資料夾", + "upload": "上傳", + "upload_files": "上傳檔案", + "upload_folder": "上傳資料夾", + "upload.uploading": "上傳中...", + "upload.complete": "{count} / {total} 已上傳", + "upload.files": "檔案", + "rename": "重新命名", + "move": "移動到...", + "move_to": "移動到", + "delete": "刪除", + "download": "下載", + "view": "檢視", + "cancel": "取消", + "confirm": "確認", + "share": "共享", + "favorite": "新增到收藏", + "unfavorite": "取消收藏", + "copy": "複製", + "notify": "通知", + "send": "傳送", + "clear_recent": "清除最近", + "logout": "退出登入", + "create": "建立", + "search_btn": "搜尋", + "close": "關閉", + "delete_permanently": "永久刪除", + "empty_trash": "清空回收站", + "open_parent_folder": "轉到父資料夾", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "外觀", + "about": "關於 OxiCloud", + "about_description": "基於 Rust 和整潔架構構建的雲端儲存平臺。快速、安全、私密。", + "admin_panel": "管理面板", + "profile": "我的資料", + "role_user": "使用者", + "theme": { + "light": "淺色", + "dark": "深色", + "auto": "跟隨系統" + }, + "manage_groups": "管理群組", + "admin": "管理員" + }, + "share": { + "dialogTitle": "共享連結", + "linkLabel": "共享連結:", + "copyLink": "複製", + "permissions": "許可權:", + "permissionRead": "讀取", + "permissionWrite": "寫入", + "permissionReshare": "再共享", + "password": "密碼保護:", + "generatePassword": "生成", + "expiration": "過期日期:", + "update": "更新共享", + "remove": "移除共享", + "notifyTitle": "傳送通知", + "notifyEmailLabel": "電子郵件地址:", + "notifyMessageLabel": "訊息(可選):", + "notifySend": "傳送通知", + "shareWithOthers": "與他人共享", + "sharePublicly": "公開共享", + "shareSettings": "共享設定", + "shareCopied": "連結已複製到剪貼簿", + "shareCreated": "共享連結建立成功", + "shareUpdated": "共享設定更新成功", + "shareRemoved": "共享已移除", + "inviteByEmail": "透過郵件邀請 — 將傳送邀請", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "複製", + "copy_failed": "Could not copy link", + "download": "下載", + "files": "檔案", + "folders": "資料夾", + "link_name": "Link name (optional)", + "notifyByEmail": "透過郵件通知", + "revoke": "移除", + "role_label": "角色" + }, + "share_dialogTitle": "共享連結", + "share_linkLabel": "共享連結:", + "share_copyLink": "複製", + "share_permissions": "許可權:", + "share_permissionRead": "讀取", + "share_permissionWrite": "寫入", + "share_permissionReshare": "再共享", + "share_password": "密碼保護:", + "share_generatePassword": "生成", + "share_expiration": "過期日期:", + "share_update": "更新共享", + "share_remove": "移除共享", + "share_notifyTitle": "傳送通知", + "share_notifyEmailLabel": "電子郵件地址:", + "share_notifyMessageLabel": "訊息(可選):", + "share_notifySend": "傳送通知", + "shared": { + "backToFiles": "返回檔案", + "pageTitle": "共享資源", + "pageDescription": "管理你的共享檔案和資料夾", + "filterType": "型別:", + "filterAll": "全部", + "filterFiles": "檔案", + "filterFolders": "資料夾", + "sortBy": "排序依據:", + "sortByName": "名稱", + "sortByDate": "共享日期", + "sortByExpiration": "過期日期", + "search": "搜尋", + "colName": "名稱", + "colType": "型別", + "colDateShared": "共享日期", + "colExpiration": "過期日期", + "colPermissions": "許可權", + "colPassword": "密碼", + "colActions": "操作", + "emptyStateTitle": "尚未有共享資源", + "emptyStateDesc": "當你共享檔案或資料夾時,它們會出現在這裡", + "goToFiles": "前往檔案", + "typeFile": "檔案", + "typeFolder": "資料夾", + "noExpiration": "無過期", + "hasPassword": "有", + "noPassword": "無", + "editShare": "編輯共享", + "notifyShare": "通知某人", + "copyLink": "複製連結", + "removeShare": "移除共享", + "linkCopied": "連結已複製到剪貼簿!", + "linkCopyFailed": "複製連結失敗", + "itemUpdated": "共享設定更新成功", + "itemRemoved": "共享已移除成功", + "invalidEmail": "請輸入有效的電子郵件地址", + "notificationSent": "通知已成功傳送", + "notificationFailed": "傳送通知失敗", + "shared_backToFiles": "返回檔案", + "shared_colActions": "操作", + "shared_colDateShared": "共享日期", + "shared_colExpiration": "過期日期", + "shared_colName": "名稱", + "shared_colPassword": "密碼", + "shared_colPermissions": "許可權", + "shared_colType": "類型", + "shared_copyLink": "複製連結", + "shared_editShare": "編輯共享", + "shared_emptyStateDesc": "當你共享檔案或資料夾時,它們會顯示在此", + "shared_emptyStateTitle": "尚無共享資源", + "shared_filterAll": "全部", + "shared_filterFiles": "檔案", + "shared_filterFolders": "資料夾", + "shared_filterType": "類型:", + "shared_goToFiles": "前往檔案", + "shared_hasPassword": "是", + "shared_invalidEmail": "請輸入有效的電子郵件地址", + "shared_itemRemoved": "共享已成功移除", + "shared_itemUpdated": "共享設定已成功更新", + "shared_linkCopied": "連結已複製到剪貼簿!", + "shared_linkCopyFailed": "複製連結失敗", + "shared_noExpiration": "永不過期", + "shared_noPassword": "否", + "shared_notificationFailed": "傳送通知失敗", + "shared_notificationSent": "通知已成功傳送", + "shared_notifyShare": "通知對方", + "shared_pageDescription": "管理你的共享檔案與資料夾", + "shared_pageTitle": "共享資源", + "shared_removeShare": "移除共享", + "shared_search": "搜尋", + "shared_sortBy": "排序方式:", + "shared_sortByDate": "共享日期", + "shared_sortByExpiration": "過期日期", + "shared_sortByName": "名稱", + "shared_typeFile": "檔案", + "shared_typeFolder": "資料夾" + }, + "files": { + "name": "名稱", + "type": "型別", + "size": "大小", + "modified": "修改日期", + "no_files": "此資料夾中沒有檔案", + "empty_hint": "上傳檔案或建立資料夾以開始使用", + "loading": "正在載入檔案…", + "view_grid": "網格檢視", + "view_list": "列表檢視", + "file_types": { + "document": "文件", + "image": "圖片", + "video": "影片", + "audio": "音訊", + "pdf": "PDF", + "text": "文字", + "folder": "資料夾", + "spreadsheet": "電子表格", + "presentation": "簡報", + "archive": "壓縮檔案", + "installer": "安裝程式", + "code": "程式碼" + }, + "owner": "擁有者", + "add_favorites": "新增到收藏", + "added_favorites": "已新增到收藏", + "col_name": "名稱", + "col_owner": "擁有者", + "col_size": "大小", + "col_type": "型別", + "copy": "複製", + "edit": "編輯", + "file": "檔案", + "folder": "資料夾", + "new_folder": "新建資料夾", + "share": "分享", + "view": "檢視" + }, + "dialogs": { + "rename_folder": "重新命名資料夾", + "new_name": "新名稱", + "new_folder_title": "新建資料夾", + "folder_name": "資料夾名稱", + "folder_placeholder": "我的資料夾", + "rename_title": "重新命名", + "move_file": "移動檔案", + "select_destination": "選擇目標資料夾", + "root": "根目錄", + "delete_confirmation": "你確定要刪除", + "and_contents": "及其所有內容", + "no_undo": "此操作無法撤銷", + "share_file": "共享檔案", + "share_folder": "共享資料夾", + "existing_shares": "現有共享", + "share_options": "共享選項", + "password": "密碼", + "expiration": "過期日期", + "permissions": "許可權", + "generated_link": "生成的連結", + "notify": "傳送通知", + "recipient": "收件人", + "message": "訊息", + "confirm_delete": "移至回收站", + "confirm_delete_file": "確定要將檔案「{{name}}」移至回收站嗎?", + "confirm_delete_folder": "確定要將資料夾「{{name}}」及其所有內容移至回收站嗎?", + "confirm_delete_share": "刪除共享連結", + "confirm_delete_share_msg": "確定要刪除此共享連結嗎?", + "confirm_empty_trash": "清空回收站", + "confirm_permanent_delete": "永久刪除", + "confirm_permanent_delete_msg": "確定要永久刪除此項目嗎?此操作無法復原。", + "confirm_title": "確認操作", + "go_to_parent": ".. (上層資料夾)", + "move_folder": "移動資料夾", + "no_subfolders": "沒有子資料夾", + "rename_file": "重新命名檔案", + "select_this_folder": "選擇此資料夾", + "move_to_home": "移動到主資料夾" + }, + "dropzone": { + "drag_files": "將檔案拖到這裡,或點選選擇", + "drop_files": "釋放檔案以上傳" + }, + "permissions": { + "read": "讀取", + "write": "寫入", + "reshare": "再共享" + }, + "errors": { + "file_not_found": "檔案未找到", + "folder_not_found": "資料夾未找到", + "delete_error": "刪除時出錯", + "upload_error": "上傳檔案時出錯", + "rename_error": "重新命名時出錯", + "move_error": "移動時出錯", + "empty_name": "名稱不能為空", + "name_exists": "已存在同名檔案或資料夾", + "generic_error": "發生錯誤", + "group_name_invalid": "群組名稱必須符合電子郵件前綴格式(字母、數字、點、連字符、下劃線;1–64 個字元)。", + "group_cycle": "此成員會在群組之間形成循環參照。", + "group_depth_exceeded": "嵌套深度超過允許的最大值(8)。", + "group_virtual_immutable": "「Internal」群組由系統管理,無法修改。", + "group_not_found": "找不到群組。", + "group_name_taken": "已存在同名群組。" + }, + "breadcrumb": { + "home": "主頁" + }, + "trash": { + "empty_trash": "清空回收站", + "empty_state": "回收站為空", + "original_location": "原始位置", + "deleted_date": "刪除日期", + "remaining": "剩餘", + "actions": "操作", + "restore": "恢復", + "delete_permanently": "永久刪除", + "empty_confirm": "你確定要清空回收站嗎?這將永久刪除所有專案。", + "groupby": { + "remaining_days": "剩餘天數", + "trashed_time": "刪除時間" + }, + "delete": "永久刪除", + "empty_action": "清空回收站" + }, + "daysRemaining": { + "expired": "已過期", + "today": "今天", + "tomorrow": "明天", + "inDays": "{{count}} 天" + }, + "expiryChip": { + "never": "永不過期", + "expired": "已過期", + "today": "今天到期", + "tomorrow": "明天到期", + "inDays": "{{count}} 天後到期", + "onDate": "於 {{date}} 到期" + }, + "auth": { + "login_title": "登入", + "username": "使用者名稱", + "username_placeholder": "輸入你的使用者名稱", + "login_identifier": "使用者名稱或電子郵件", + "login_identifier_placeholder": "請輸入使用者名稱或電子郵件", + "password": "密碼", + "password_placeholder": "輸入你的密碼", + "login_button": "登入", + "no_account": "沒有賬號?", + "register": "註冊", + "admin_setup": "首次使用?", + "setup": "設定管理員", + "register_title": "建立賬號", + "email": "電子郵件", + "email_placeholder": "輸入你的電子郵件", + "confirm_password": "確認密碼", + "confirm_password_placeholder": "確認你的密碼", + "register_button": "建立賬號", + "have_account": "已有賬號?", + "login": "登入", + "setup_title": "初始設定", + "setup_step1": "管理員", + "setup_step2": "系統", + "setup_step3": "完成", + "admin_username": "管理員使用者名稱", + "admin_email": "管理員電子郵件", + "admin_password": "管理員密碼", + "create_admin": "建立管理員", + "back_to_login": "已設定完成?", + "admin_success": "管理員賬號建立成功!您現在可以登入。", + "account_success": "賬號建立成功!您現在可以登入。", + "passwords_mismatch": "密碼不匹配", + "admin_create_error": "建立管理員賬號時出錯", + "or": "或", + "sso_login": "使用 SSO 登入", + "sso_login_provider": "使用 {{provider}} 登入", + "magicLinkHint": "沒有密碼?輸入您的電子郵件,我們將向您發送一次性登入連結。", + "magicLinkEmailLabel": "電子郵件地址", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "傳送登入連結", + "magicLinkSent": "若該電子郵件存在帳號,登入連結已發送。請查收您的收件匣。", + "magicLinkUnavailable": "此伺服器不支援電子郵件登入。", + "magicLinkNetworkError": "無法連線到伺服器:{{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "電子郵件地址", + "magic_hint": "沒有密碼?輸入您的電子郵件,我們將向您發送一次性登入連結。", + "magic_unavailable": "此伺服器不支援電子郵件登入。", + "passwords_match": "Passwords match", + "sign_in": "登入" + }, + "storage": { + "title": "儲存空間", + "calculating": "計算中...", + "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "無法預覽此檔案型別。", + "download_file": "下載檔案", + "zoom_in": "放大", + "zoom_out": "縮小", + "zoom_reset": "重置縮放" + }, + "language_selector": { + "title": "歡迎!", + "subtitle": "選擇您的語言以繼續", + "continue": "繼續", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "還沒有收藏", + "empty_hint": "為檔案或資料夾新增星標以將其新增到收藏夾", + "add": "新增到收藏夾", + "remove": "從收藏夾移除", + "added_title": "已新增到收藏", + "added_msg": "已新增到收藏", + "removed_title": "已從收藏移除", + "removed_msg": "已從收藏移除" + }, + "recent": { + "title": "最近", + "clear": "清除最近", + "accessed": "訪問於", + "empty_state": "沒有最近檔案", + "empty_hint": "您開啟的檔案將顯示在這裡", + "loadMore": "載入更多" + }, + "batch": { + "one_selected": "已選擇 1 個專案", + "n_selected": "已選擇 {{count}} 個專案", + "confirm_delete": "確定要將 {{count}} 個專案移至回收站嗎?", + "move_title": "移動 {{count}} 個專案", + "add_favorites": "新增到收藏夾", + "move_copy": "移動或複製" + }, + "admin": { + "page_title": "管理面板", + "back_to_app": "返回 OxiCloud", + "loading": "載入中…", + "access_denied": "拒絕訪問", + "access_denied_desc": "需要管理員許可權。", + "sign_in": "登入", + "tab_dashboard": "儀表盤", + "tab_users": "使用者", + "tab_oidc": "SSO / OIDC", + "total_users": "使用者總數", + "active_users": "活躍使用者", + "admins": "管理員", + "version": "版本", + "storage_overview": "儲存概覽", + "used": "已使用", + "total_quota": "總配額", + "usage_pct": "使用率", + "users_over_80": "超過80%配額", + "users_over_quota": "超過配額", + "system": "系統", + "auth_label": "認證", + "oidc_label": "OIDC", + "quotas_label": "配額", + "enabled": "已啟用", + "disabled": "已禁用", + "active": "活躍", + "off": "關閉", + "allow_registration": "允許公開自助註冊", + "registration_warning": "公開註冊已禁用。只有管理員可以建立新使用者。", + "user_management": "使用者管理", + "create_user": "建立使用者", + "col_user": "使用者", + "col_role": "角色", + "col_auth": "認證", + "col_status": "狀態", + "col_storage": "儲存", + "col_last_login": "最後登入", + "col_actions": "操作", + "loading_users": "正在載入使用者…", + "failed_load_users": "載入失敗", + "no_users_found": "未找到使用者", + "showing_users": "顯示 {{from}}-{{to}} / {{total}}", + "prev": "上一頁", + "next": "下一頁", + "inactive": "未啟用", + "you_badge": "(你)", + "local": "本地", + "never": "從未", + "just_now": "剛剛", + "minutes_ago": "{{n}}分鐘前", + "hours_ago": "{{n}}小時前", + "days_ago": "{{n}}天前", + "edit_quota_title": "編輯配額", + "reset_password_title": "重置密碼", + "toggle_role_title": "切換角色", + "deactivate_title": "停用", + "activate_title": "啟用", + "delete_title": "刪除", + "sso_title": "單點登入 (OIDC / SSO)", + "enable_sso": "啟用 SSO 認證", + "provider_name": "提供商名稱", + "issuer_url": "發行者 URL", + "issuer_url_hint": "您的身份提供商的 OpenID Connect 發行者 URL", + "auto_discover": "自動發現", + "discovering": "發現中…", + "client_id": "客戶端 ID", + "client_secret": "客戶端金鑰", + "client_secret_placeholder": "留空以保留當前值", + "secret_configured": "已配置客戶端金鑰", + "callback_url": "回撥 URL", + "callback_url_hint": "(在您的 IdP 中註冊)", + "advanced_settings": "高階設定", + "scopes": "範圍", + "auto_provision": "首次登入時自動配置使用者", + "admin_groups": "管理組", + "admin_groups_hint": "對映到管理員角色的逗號分隔 OIDC 組名", + "disable_password": "禁用密碼登入 (僅 OIDC)", + "password_warning": "這將阻止所有基於密碼的登入!", + "test_btn": "測試", + "save_btn": "儲存", + "saving": "儲存中…", + "settings_saved": "設定已儲存 — OIDC 現在 {{status}}", + "quota_modal_title": "更新儲存配額", + "quota_user_label": "使用者:", + "new_quota": "新配額", + "quota_unlimited_hint": "0表示無限制", + "cancel": "取消", + "create_user_title": "建立新使用者", + "username_label": "使用者名稱", + "username_placeholder": "zhangsan", + "username_hint": "3–32個字元", + "password_label": "密碼", + "password_placeholder": "至少8個字元", + "email_label": "郵箱", + "email_optional": "(可選)", + "email_placeholder": "user@example.com (留空自動生成)", + "role_label": "角色", + "role_user": "使用者", + "role_admin": "管理員", + "quota_label": "配額", + "creating": "建立中…", + "reset_pw_title": "重置密碼", + "new_password_label": "新密碼", + "resetting": "重置中…", + "reset_btn": "重置", + "confirm_role_change": "將角色更改為 {{role}}?", + "confirm_deactivate": "確定要停用此使用者嗎?", + "confirm_activate": "確定要啟用此使用者嗎?", + "confirm_delete_user": "刪除使用者 \"{{name}}\"?此操作無法撤消!", + "confirm_action": "確認操作", + "confirm_yes": "確認", + "confirm_no": "取消", + "error_username_short": "使用者名稱至少需要3個字元", + "error_password_short": "密碼至少需要8個字元", + "error_generic": "失敗", + "error_network": "網路錯誤:{{message}}", + "error_create_user": "建立使用者失敗", + "tab_storage": "儲存", + "storage_title": "儲存配置", + "storage_current_backend": "當前後端", + "storage_total_blobs": "總塊數", + "storage_total_size": "總大小", + "storage_dedup_ratio": "去重比率", + "storage_backend": "後端", + "storage_local": "本地", + "storage_s3": "S3 相容", + "storage_provider_preset": "提供商預設", + "storage_preset_custom": "自定義", + "storage_endpoint_url": "端點 URL", + "storage_endpoint_hint": "AWS S3 請留空", + "storage_bucket": "儲存桶", + "storage_region": "地區", + "storage_access_key": "訪問金鑰", + "storage_secret_key": "金鑰", + "storage_secret_configured": "金鑰已配置", + "storage_key_placeholder": "輸入新金鑰", + "storage_path_style": "強制路徑風格", + "storage_path_style_hint": "MinIO 及某些 S3 相容服務需要此選項", + "storage_test_connection": "測試連線", + "storage_test_success": "連線成功", + "storage_test_failure": "連線失敗", + "storage_save": "儲存配置", + "storage_saved": "配置已儲存", + "storage_migration": "資料遷移", + "storage_migration_coming_soon": "遷移工具即將推出", + "migration_status_label": "遷移狀態", + "migration_start": "開始遷移", + "migration_pause": "暫停", + "migration_resume": "繼續", + "migration_verify": "驗證", + "migration_complete": "完成", + "migration_started": "遷移已開始", + "migration_paused_msg": "遷移已暫停", + "migration_resumed_msg": "遷移已繼續", + "migration_completed_msg": "遷移成功完成", + "migration_verifying": "正在驗證...", + "migration_verify_passed": "驗證透過", + "migration_verify_failed": "驗證失敗", + "migration_failed_blobs": "失敗的塊", + "testing": "正在測試...", + "smtp_disabled": "已停用(未設定主機)", + "smtp_enabled": "已啟用", + "smtp_enabled_label": "狀態", + "smtp_intro": "SMTP 僅透過環境變數(OXICLOUD_SMTP_*)設定。下方數值是從運行中的伺服器讀取的 — 如需修改,請編輯環境變數並重新啟動 OxiCloud。", + "smtp_not_configured": "此伺服器未設定 SMTP。", + "smtp_send_failed": "傳送失敗。", + "smtp_send_test": "傳送測試郵件", + "smtp_sending": "傳送中…", + "smtp_sent": "測試郵件已傳送。", + "smtp_server_code": "伺服器回應", + "smtp_test_intro": "向下方收件者傳送預設的診斷訊息,並回報 SMTP 伺服器的回應,以便您與轉發日誌進行對照。", + "smtp_test_missing_to": "請輸入收件者地址。", + "smtp_test_title": "傳送測試郵件", + "smtp_test_to": "收件者地址", + "smtp_title": "外寄郵件 (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "管理員", + "confirm_role": "將角色更改為 {{role}}?", + "dashboard": "儀表盤", + "email": "郵箱", + "mig_complete": "完成", + "mig_pause": "暫停", + "mig_resume": "繼續", + "mig_verify_failed": "驗證失敗", + "mig_verify_passed": "驗證透過", + "mig_verifying": "正在驗證...", + "oidc_auto_provision": "首次登入時自動配置使用者", + "oidc_callback": "回撥 URL", + "oidc_client_id": "客戶端 ID", + "oidc_disable_pw": "禁用密碼登入 (僅 OIDC)", + "oidc_issuer": "發行者 URL", + "oidc_scopes": "範圍", + "password": "密碼", + "quotas": "配額", + "reset_pw_for": "新密碼用於", + "role": "角色", + "smtp_fail": "傳送失敗。", + "smtp_send": "傳送", + "smtp_test": "傳送測試郵件", + "smtp_user_state": "認證", + "status": "狀態", + "storage": "儲存", + "storage_endpoint": "端點 URL", + "storage_tab": "儲存", + "time_min_ago": "{{n}}分鐘前", + "title": "管理員", + "user": "使用者", + "username": "使用者名稱", + "users": "使用者" + }, + "profile": { + "page_title": "個人資料", + "back_to_app": "返回 OxiCloud", + "loading": "載入中…", + "not_authenticated": "未認證", + "not_authenticated_desc": "請登入以檢視您的個人資料。", + "sign_in": "登入", + "role_admin": "管理員", + "role_user": "使用者", + "account_details": "賬戶詳情", + "username": "使用者名稱", + "email": "郵箱", + "role": "角色", + "last_login": "最後登入", + "storage": "儲存", + "used": "已使用", + "quota": "配額", + "usage": "使用率", + "unlimited": "無限制", + "app_passwords": "應用密碼", + "app_pw_desc": "為 WebDAV、CalDAV 和 CardDAV 客戶端生成密碼。每個密碼只顯示一次。", + "app_pw_label_placeholder": "標籤(如 Thunderbird、macOS)", + "generate": "生成", + "generating": "生成中…", + "new_password_for": "新密碼用於", + "copy_warning": "請立即複製此密碼,之後將無法再次檢視。", + "copy_to_clipboard": "複製到剪貼簿", + "col_label": "標籤", + "col_created": "建立時間", + "col_last_used": "最後使用", + "col_status": "狀態", + "active": "活躍", + "revoked": "已撤銷", + "revoke_title": "撤銷", + "no_app_passwords": "暫無應用密碼。", + "client_sessions": "客戶端會話", + "client_sessions_desc": "連線 Nextcloud 相容客戶端時自動生成。", + "col_client": "客戶端", + "never": "從未", + "just_now": "剛剛", + "minutes_ago": "{{n}}分鐘前", + "hours_ago": "{{n}}小時前", + "days_ago": "{{n}}天前", + "edit_profile": "編輯個人資料", + "edit_oidc_managed": "要更改您的資訊(姓名、名字、頭像等),請前往您的身分提供者更新。變更將在您下次登入時顯示。", + "username_claim_hint": "2-64 個字元,字母 / 數字 / 點 / 短橫線 / 底線。一旦選定,使用者名稱將無法更改(DAV/NextCloud 用戶端依賴它)。", + "username_already_claimed": "使用者名稱已設定,不可更改(DAV/NextCloud 用戶端依賴它)。", + "given_name": "名", + "family_name": "姓", + "notify_on_share": "當有人與我分享時透過電子郵件通知我", + "notify_on_share_hint": "取消勾選後,分享項目仍會顯示在您的帳戶中 — 只是不會收到相關郵件通知。", + "save_profile": "儲存變更", + "profile_saved": "個人資料已更新", + "profile_no_changes": "沒有變更可儲存。", + "profile_save_failed": "儲存失敗", + "username_taken_error": "該使用者名稱已被使用。", + "username_immutable_error": "您的使用者名稱已設定,無法在此更改。如需重新命名,請聯絡管理員。", + "change_password": "修改密碼", + "current_password": "當前密碼", + "new_password": "新密碼", + "min_8_chars": "至少8個字元", + "confirm_password": "確認新密碼", + "update_password": "更新密碼", + "updating": "更新中…", + "password_updated": "密碼更新成功", + "passwords_no_match": "密碼不匹配", + "password_too_short": "密碼至少需要8個字元", + "password_change_failed": "修改密碼失敗", + "error_network": "網路錯誤:{{message}}", + "error_label_required": "請輸入標籤", + "error_create_pw": "建立應用密碼失敗", + "confirm_revoke": "撤銷應用密碼\"{{label}}\"?使用此密碼的客戶端將停止工作。", + "error_revoke": "撤銷失敗", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "密碼不匹配" + }, + "notifications": { + "file_renamed": "檔案已重新命名", + "file_renamed_to": "檔案已重新命名為\"{{name}}\"", + "folder_renamed": "資料夾已重新命名", + "folder_renamed_to": "資料夾已重新命名為\"{{name}}\"", + "file_uploaded": "檔案已上傳", + "file_deleted": "檔案已移至回收站", + "folder_deleted": "資料夾已移至回收站", + "item_deleted_permanently": "專案已永久刪除", + "trash_emptied": "回收站已清空", + "title": "通知", + "empty": "暫無通知", + "link_created": "連結已建立", + "share_success": "分享連結建立成功", + "upload_files_section_title": "此處不支援上傳", + "upload_files_section_body": "請前往檔案部分上傳檔案" + }, + "upload": { + "uploading": "正在上傳...", + "files": "個檔案", + "complete": "已上傳 {{count}} / {{total}}" + }, + "storage_quota_exceeded": "儲存配額已超限", + "sharedwithme": { + "pageTitle": "與我共享", + "pageDescription": "其他使用者與您共享的檔案和資料夾", + "emptyStateTitle": "目前沒有內容與您共享", + "emptyStateDesc": "其他使用者與您共享的項目將顯示在這裡", + "loadMore": "載入更多", + "sharedBy": "共享者", + "colName": "名稱", + "colType": "類型", + "colSharedBy": "共享者", + "colDate": "共享日期", + "colPermissions": "權限" + }, + "groupby": { + "none": "無", + "title": "分組方式", + "owner": "擁有者", + "shareDate": "分享日期", + "type": "類型", + "type.folders": "資料夾", + "accessedAt": "存取日期", + "modifiedAt": "修改日期", + "createdAt": "建立日期", + "size": "大小", + "favoriteDate": "收藏日期", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "新增", + "folders": "資料夾" + }, + "dateBucket": { + "today": "今天", + "last7days": "近7天", + "last30days": "近30天", + "unknown": "未知" + }, + "groups": { + "title": "管理群組", + "create_button": "建立群組", + "create_dialog_title": "新群組", + "edit_dialog_title": "重新命名群組", + "name_label": "名稱", + "name_placeholder": "engineering", + "description_label": "描述(選填)", + "members_section": "成員", + "add_member_placeholder": "新增使用者或群組…", + "no_members": "尚無成員。", + "remove_member": "移除", + "delete_group": "刪除群組", + "delete_confirm": "刪除群組「{name}」?引用此群組的所有授權將被撤銷。", + "empty_state": "尚無群組。", + "load_more": "載入更多", + "back_to_list": "返回", + "loading": "載入中…", + "virtual_badge": "系統", + "member_count_zero": "無成員", + "member_count_one": "1 個成員", + "member_count_other": "{count} 個成員", + "delete_confirm_label": "請輸入群組名稱以確認:", + "delete_confirm_mismatch": "請準確輸入群組名稱以確認。", + "virtual_internal_name": "內部", + "members_loading": "正在載入成員…", + "members_empty": "無成員", + "virtual_internal_explanation": "本伺服器上的所有內部使用者", + "create": "建立群組", + "empty": "尚無群組。", + "members": "成員" + }, + "myshares": { + "copyLink": "複製連結", + "deleteLink": "刪除連結", + "notifyByEmail": "透過郵件通知", + "notifyFailed": "無法傳送通知。", + "notifyGroupMembers": "通知群組成員", + "notifyRateLimited": "對此收件者的通知過多 — 請稍後重試。", + "removeAccess": "移除存取權限", + "resendInvitation": "重新傳送邀請郵件", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "音訊", + "code": "程式碼", + "text": "文字" + }, + "common": { + "add": "新增", + "cancel": "取消", + "clear": "Clear", + "close": "關閉", + "confirm": "確認", + "copy": "複製", + "create": "建立", + "delete": "刪除", + "download": "下載", + "load_more": "載入更多", + "loading": "載入中…", + "next": "下一首", + "no": "無", + "previous": "上一首", + "remove": "移除", + "rename": "重新命名", + "save": "儲存", + "search": "搜尋", + "yes": "有" + }, + "device": { + "continue": "繼續", + "unknown": "未知" + }, + "expiryBucket": { + "expired": "已過期", + "noExpiry": "無過期", + "today": "今天", + "tomorrow": "明天" + }, + "nextcloud": { + "error_title": "錯誤", + "sign_in_with": "使用 {{provider}} 登入" + }, + "search": { + "size_label": "大小", + "title": "搜尋", + "type": { + "audio": "音訊" + }, + "type_label": "型別" + }, + "sizeBucket": { + "folders": "資料夾" + }, + "view": { + "grid": "網格檢視", + "list": "列表檢視" + } } diff --git a/frontend/static/locales/zh.json b/frontend/static/locales/zh.json index a4af3ee3..4d048b2b 100644 --- a/frontend/static/locales/zh.json +++ b/frontend/static/locales/zh.json @@ -1,980 +1,1111 @@ { - "server": { - "magic_link": { - "page": { - "expired_title": "此登录链接已不再有效", - "expired_body": "链接可能已过期或已被使用。我们可以为您发送一个新的 — 几秒钟内它将到达您的收件箱。", - "resend_to": "发送新链接至 {{email}}", - "generic_unavailable": "此登录链接已不再有效。它可能已被使用或已过期。请在登录页面请求新链接。", - "service_unavailable": "此服务器未启用魔法链接登录。", - "internal_error": "登录时出错。请重试。", - "resend_failure": "发送链接时出错。请重试。", - "cross_browser_title": "在此设备上继续登录?", - "cross_browser_body": "您在与请求时不同的浏览器或设备上打开了此登录链接。", - "cross_browser_warning": "如果是您请求了此链接,可以安全继续。否则,请关闭此页面 — 点击「继续」将使其他人登录您的账户。", - "cross_browser_continue": "继续并登录", - "resend_confirmation_title": "请检查您的收件箱", - "resend_confirmation_body": "如果登录链接属于活跃账户,新链接刚刚已发送。请检查您的收件箱。", - "return_link": "返回 OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n点击下方链接打开:\n{{link}}\n\n该链接仅可使用一次,并将在 {{ttl_hours}} 小时后过期。\n如果您未预期收到此邀请,可以忽略此消息。\n\n— OxiCloud" - }, - "login": { - "subject": "登录 OxiCloud", - "body": "您好,\n\n使用下方链接登录 OxiCloud。该链接仅可使用一次,并将在 {{ttl_minutes}} 分钟后过期。请在请求时使用的同一设备上打开。\n\n{{link}}\n\n如果您未请求此登录链接,可以忽略此消息 — 无需进一步操作。\n\n— OxiCloud" - }, - "kind_file": "文件", - "kind_folder": "文件夹", - "english_fallback_divider": "--- 以下为英文版本 ---" - } + "server": { + "magic_link": { + "page": { + "expired_title": "此登录链接已不再有效", + "expired_body": "链接可能已过期或已被使用。我们可以为您发送一个新的 — 几秒钟内它将到达您的收件箱。", + "resend_to": "发送新链接至 {{email}}", + "generic_unavailable": "此登录链接已不再有效。它可能已被使用或已过期。请在登录页面请求新链接。", + "service_unavailable": "此服务器未启用魔法链接登录。", + "internal_error": "登录时出错。请重试。", + "resend_failure": "发送链接时出错。请重试。", + "cross_browser_title": "在此设备上继续登录?", + "cross_browser_body": "您在与请求时不同的浏览器或设备上打开了此登录链接。", + "cross_browser_warning": "如果是您请求了此链接,可以安全继续。否则,请关闭此页面 — 点击「继续」将使其他人登录您的账户。", + "cross_browser_continue": "继续并登录", + "resend_confirmation_title": "请检查您的收件箱", + "resend_confirmation_body": "如果登录链接属于活跃账户,新链接刚刚已发送。请检查您的收件箱。", + "return_link": "返回 OxiCloud" + }, + "email": { + "invitation": { + "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n点击下方链接打开:\n{{link}}\n\n该链接仅可使用一次,并将在 {{ttl_hours}} 小时后过期。\n如果您未预期收到此邀请,可以忽略此消息。\n\n— OxiCloud" }, - "notification": { - "share": { - "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n打开 OxiCloud 查看您的新共享:\n{{login_link}}\n\n您可能还有来自 {{inviter}} 的其他新共享 — 登录以查看所有共享给您的项目。\n\n— OxiCloud\n\n您收到此消息是因为您拥有 OxiCloud 账户且共享通知偏好已开启。您可以在个人资料中关闭它(当有人与我共享时通过电子邮件通知我)。" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "极简云存储系统" - }, - "nav": { - "files": "文件", - "shared": "共享", - "recent": "最近", - "favorites": "收藏", - "photos": "照片", - "music": "音乐", - "trash": "回收站", - "sharedwithme": "与我共享" - }, - "photos": { - "empty_state": "还没有照片", - "empty_hint": "上传图片或视频即可在此查看", - "items_selected": "已选择", - "view_daily": "日", - "view_monthly": "月", - "view_yearly": "年" - }, - "music": { - "create_playlist": "创建播放列表", - "playlists": "播放列表", - "no_playlists": "还没有播放列表", - "select_playlist": "选择一个播放列表", - "select_hint": "从侧边栏选择播放列表或创建新播放列表", - "add_tracks": "添加曲目", - "no_tracks": "此播放列表中没有曲目", - "unknown_artist": "未知艺术家", - "unknown_title": "未知", - "confirm_delete": "删除此播放列表?", - "playlist_name": "播放列表名称", - "create": "创建", - "delete": "删除", - "share": "分享", - "edit": "编辑", - "play_all": "全部播放", - "shuffle": "随机播放", - "repeat": "重复", - "repeat_one": "单曲循环", - "queue": "播放队列", - "queue_empty": "播放队列为空", - "not_playing": "未播放", - "play": "播放", - "pause": "暂停", - "previous": "上一首", - "next": "下一首", - "volume": "音量", - "mute": "静音", - "unmute": "取消静音", - "title": "标题", - "artist": "艺术家", - "album": "专辑", - "tracks": "首曲目", - "add": "添加", - "added": "已添加!", - "added_to_playlist": "已添加到播放列表", - "add_to_playlist": "添加到播放列表", - "load_error": "加载播放列表出错", - "add_error": "无法将曲目添加到播放列表", - "no_playlists_yet": "暂无播放列表。请先创建一个!", - "selected_files": "已选择:", - "error": "错误", - "search_audio": "搜索音频文件…", - "no_audio_files": "未找到音频文件", - "selected": "已选择", - "loading": "加载中…", - "search_error": "无法加载音频文件", - "adding": "添加中…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "搜索文件...", - "new_folder": "新建文件夹", - "upload": "上传", - "upload_files": "上传文件", - "upload_folder": "上传文件夹", - "upload.uploading": "上传中...", - "upload.complete": "{count} / {total} 已上传", - "upload.files": "文件", - "rename": "重命名", - "move": "移动到...", - "move_to": "移动到", - "delete": "删除", - "download": "下载", - "view": "查看", - "cancel": "取消", - "confirm": "确认", - "share": "共享", - "favorite": "添加到收藏", - "unfavorite": "取消收藏", - "copy": "复制", - "notify": "通知", - "send": "发送", - "clear_recent": "清除最近", - "logout": "退出登录", - "create": "创建", - "search_btn": "搜索", - "close": "关闭", - "delete_permanently": "Delete permanently", - "empty_trash": "Empty trash", - "open_parent_folder": "转到父文件夹", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "外观", - "about": "关于 OxiCloud", - "about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。", - "admin_panel": "管理面板", - "profile": "我的资料", - "role_user": "用户", - "theme": { - "light": "浅色", - "dark": "深色", - "auto": "跟随系统" + "login": { + "subject": "登录 OxiCloud", + "body": "您好,\n\n使用下方链接登录 OxiCloud。该链接仅可使用一次,并将在 {{ttl_minutes}} 分钟后过期。请在请求时使用的同一设备上打开。\n\n{{link}}\n\n如果您未请求此登录链接,可以忽略此消息 — 无需进一步操作。\n\n— OxiCloud" }, - "manage_groups": "管理群组" + "kind_file": "文件", + "kind_folder": "文件夹", + "english_fallback_divider": "--- 以下为英文版本 ---" + } }, - "share": { - "dialogTitle": "共享链接", - "linkLabel": "共享链接:", - "copyLink": "复制", - "permissions": "权限:", - "permissionRead": "读取", - "permissionWrite": "写入", - "permissionReshare": "再共享", - "password": "密码保护:", - "generatePassword": "生成", - "expiration": "过期日期:", - "update": "更新共享", - "remove": "移除共享", - "notifyTitle": "发送通知", - "notifyEmailLabel": "电子邮件地址:", - "notifyMessageLabel": "消息(可选):", - "notifySend": "发送通知", - "shareWithOthers": "与他人共享", - "sharePublicly": "公开共享", - "shareSettings": "共享设置", - "shareCopied": "链接已复制到剪贴板", - "shareCreated": "共享链接创建成功", - "shareUpdated": "共享设置更新成功", - "shareRemoved": "共享已移除", - "inviteByEmail": "通过邮件邀请 — 将发送邀请", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "共享链接", - "share_linkLabel": "共享链接:", - "share_copyLink": "复制", - "share_permissions": "权限:", - "share_permissionRead": "读取", - "share_permissionWrite": "写入", - "share_permissionReshare": "再共享", - "share_password": "密码保护:", - "share_generatePassword": "生成", - "share_expiration": "过期日期:", - "share_update": "更新共享", - "share_remove": "移除共享", - "share_notifyTitle": "发送通知", - "share_notifyEmailLabel": "电子邮件地址:", - "share_notifyMessageLabel": "消息(可选):", - "share_notifySend": "发送通知", - "shared": { - "backToFiles": "返回文件", - "pageTitle": "共享资源", - "pageDescription": "管理你的共享文件和文件夹", - "filterType": "类型:", - "filterAll": "全部", - "filterFiles": "文件", - "filterFolders": "文件夹", - "sortBy": "排序依据:", - "sortByName": "名称", - "sortByDate": "共享日期", - "sortByExpiration": "过期日期", - "search": "搜索", - "colName": "名称", - "colType": "类型", - "colDateShared": "共享日期", - "colExpiration": "过期日期", - "colPermissions": "权限", - "colPassword": "密码", - "colActions": "操作", - "emptyStateTitle": "尚未有共享资源", - "emptyStateDesc": "当你共享文件或文件夹时,它们会出现在这里", - "goToFiles": "前往文件", - "typeFile": "文件", - "typeFolder": "文件夹", - "noExpiration": "无过期", - "hasPassword": "有", - "noPassword": "无", - "editShare": "编辑共享", - "notifyShare": "通知某人", - "copyLink": "复制链接", - "removeShare": "移除共享", - "linkCopied": "链接已复制到剪贴板!", - "linkCopyFailed": "复制链接失败", - "itemUpdated": "共享设置更新成功", - "itemRemoved": "共享已移除成功", - "invalidEmail": "请输入有效的电子邮件地址", - "notificationSent": "通知已成功发送", - "notificationFailed": "发送通知失败", - "shared_backToFiles": "Back to Files", - "shared_colActions": "Actions", - "shared_colDateShared": "Date Shared", - "shared_colExpiration": "Expiration", - "shared_colName": "Name", - "shared_colPassword": "Password", - "shared_colPermissions": "Permissions", - "shared_colType": "Type", - "shared_copyLink": "Copy Link", - "shared_editShare": "Edit Share", - "shared_emptyStateDesc": "When you share files or folders, they will appear here", - "shared_emptyStateTitle": "No shared resources yet", - "shared_filterAll": "All", - "shared_filterFiles": "Files", - "shared_filterFolders": "Folders", - "shared_filterType": "Type:", - "shared_goToFiles": "Go to Files", - "shared_hasPassword": "Yes", - "shared_invalidEmail": "Please enter a valid email address", - "shared_itemRemoved": "Share removed successfully", - "shared_itemUpdated": "Share settings updated successfully", - "shared_linkCopied": "Link copied to clipboard!", - "shared_linkCopyFailed": "Failed to copy link", - "shared_noExpiration": "No expiration", - "shared_noPassword": "No", - "shared_notificationFailed": "Failed to send notification", - "shared_notificationSent": "Notification sent successfully", - "shared_notifyShare": "Notify Someone", - "shared_pageDescription": "Manage your shared files and folders", - "shared_pageTitle": "Shared Resources", - "shared_removeShare": "Remove Share", - "shared_search": "Search", - "shared_sortBy": "Sort by:", - "shared_sortByDate": "Date shared", - "shared_sortByExpiration": "Expiration", - "shared_sortByName": "Name", - "shared_typeFile": "File", - "shared_typeFolder": "Folder" - }, - "files": { - "name": "名称", - "type": "类型", - "size": "大小", - "modified": "修改日期", - "no_files": "此文件夹中没有文件", - "empty_hint": "上传文件或创建文件夹以开始使用", - "loading": "正在加载文件…", - "view_grid": "网格视图", - "view_list": "列表视图", - "file_types": { - "document": "文档", - "image": "图片", - "video": "视频", - "audio": "音频", - "pdf": "PDF", - "text": "文本", - "folder": "文件夹", - "spreadsheet": "电子表格", - "presentation": "演示文稿", - "archive": "压缩文件", - "installer": "安装程序", - "code": "代码" - }, - "owner": "所有者" - }, - "dialogs": { - "rename_folder": "重命名文件夹", - "new_name": "新名称", - "new_folder_title": "新建文件夹", - "folder_name": "文件夹名称", - "folder_placeholder": "我的文件夹", - "rename_title": "重命名", - "move_file": "移动文件", - "select_destination": "选择目标文件夹", - "root": "根目录", - "delete_confirmation": "你确定要删除", - "and_contents": "及其所有内容", - "no_undo": "此操作无法撤销", - "share_file": "共享文件", - "share_folder": "共享文件夹", - "existing_shares": "现有共享", - "share_options": "共享选项", - "password": "密码", - "expiration": "过期日期", - "permissions": "权限", - "generated_link": "生成的链接", - "notify": "发送通知", - "recipient": "收件人", - "message": "消息", - "confirm_delete": "Move to trash", - "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", - "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", - "confirm_delete_share": "Delete share link", - "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", - "confirm_empty_trash": "Empty trash", - "confirm_permanent_delete": "Delete permanently", - "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", - "confirm_title": "Confirm action", - "go_to_parent": ".. (parent folder)", - "move_folder": "Move folder", - "no_subfolders": "No subfolders", - "rename_file": "Rename file", - "select_this_folder": "Select this folder", - "move_to_home": "移动到主文件夹" - }, - "dropzone": { - "drag_files": "将文件拖到这里,或点击选择", - "drop_files": "释放文件以上传" - }, - "permissions": { - "read": "读取", - "write": "写入", - "reshare": "再共享" - }, - "errors": { - "file_not_found": "文件未找到", - "folder_not_found": "文件夹未找到", - "delete_error": "删除时出错", - "upload_error": "上传文件时出错", - "rename_error": "重命名时出错", - "move_error": "移动时出错", - "empty_name": "名称不能为空", - "name_exists": "已存在同名文件或文件夹", - "generic_error": "发生错误", - "group_name_invalid": "组名必须符合邮件前缀格式(字母、数字、点、连字符、下划线;1–64个字符)。", - "group_cycle": "此成员会在组之间形成循环引用。", - "group_depth_exceeded": "嵌套深度超出允许的最大值(8)。", - "group_virtual_immutable": "“Internal”组由系统管理,无法修改。", - "group_not_found": "未找到组。", - "group_name_taken": "同名组已存在。" - }, - "breadcrumb": { - "home": "主页" - }, - "trash": { - "empty_trash": "清空回收站", - "empty_state": "回收站为空", - "original_location": "原始位置", - "deleted_date": "删除日期", - "remaining": "剩余", - "actions": "操作", - "restore": "恢复", - "delete_permanently": "永久删除", - "empty_confirm": "你确定要清空回收站吗?这将永久删除所有项目。", - "groupby": { - "remaining_days": "剩余天数", - "trashed_time": "删除时间" - } - }, - "daysRemaining": { - "expired": "已过期", - "today": "今天", - "tomorrow": "明天", - "inDays": "{{count}} 天" - }, - "expiryChip": { - "never": "永不过期", - "expired": "已过期", - "today": "今天到期", - "tomorrow": "明天到期", - "inDays": "{{count}} 天后到期", - "onDate": "于 {{date}} 到期" - }, - "auth": { - "login_title": "登录", - "username": "用户名", - "username_placeholder": "输入你的用户名", - "login_identifier": "用户名或邮箱", - "login_identifier_placeholder": "请输入用户名或邮箱", - "password": "密码", - "password_placeholder": "输入你的密码", - "login_button": "登录", - "no_account": "没有账号?", - "register": "注册", - "admin_setup": "首次使用?", - "setup": "设置管理员", - "register_title": "创建账号", - "email": "电子邮件", - "email_placeholder": "输入你的电子邮件", - "confirm_password": "确认密码", - "confirm_password_placeholder": "确认你的密码", - "register_button": "创建账号", - "have_account": "已有账号?", - "login": "登录", - "setup_title": "初始设置", - "setup_step1": "管理员", - "setup_step2": "系统", - "setup_step3": "完成", - "admin_username": "管理员用户名", - "admin_email": "管理员电子邮件", - "admin_password": "管理员密码", - "create_admin": "创建管理员", - "back_to_login": "已设置完成?", - "admin_success": "管理员账号创建成功!您现在可以登录。", - "account_success": "账号创建成功!您现在可以登录。", - "passwords_mismatch": "密码不匹配", - "admin_create_error": "创建管理员账号时出错", - "or": "或", - "sso_login": "使用 SSO 登录", - "sso_login_provider": "使用 {{provider}} 登录", - "magicLinkHint": "没有密码?输入您的邮箱,我们将向您发送一次性登录链接。", - "magicLinkEmailLabel": "邮箱地址", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "发送登录链接", - "magicLinkSent": "如果该邮箱存在账户,登录链接已发送。请查收您的收件箱。", - "magicLinkUnavailable": "此服务器不支持邮箱登录。", - "magicLinkNetworkError": "无法连接到服务器:{{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "存储空间", - "calculating": "计算中...", - "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "无法预览此文件类型。", - "download_file": "下载文件", - "zoom_in": "放大", - "zoom_out": "缩小", - "zoom_reset": "重置缩放" - }, - "language_selector": { - "title": "欢迎!", - "subtitle": "选择您的语言以继续", - "continue": "继续", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "还没有收藏", - "empty_hint": "为文件或文件夹添加星标以将其添加到收藏夹", - "add": "添加到收藏夹", - "remove": "从收藏夹移除", - "added_title": "已添加到收藏", - "added_msg": "已添加到收藏", - "removed_title": "已从收藏移除", - "removed_msg": "已从收藏移除" - }, - "recent": { - "title": "最近", - "clear": "清除最近", - "accessed": "访问于", - "empty_state": "没有最近文件", - "empty_hint": "您打开的文件将显示在这里", - "loadMore": "加载更多" - }, - "batch": { - "one_selected": "已选择 1 个项目", - "n_selected": "已选择 {{count}} 个项目", - "confirm_delete": "确定要将 {{count}} 个项目移至回收站吗?", - "move_title": "移动 {{count}} 个项目", - "add_favorites": "添加到收藏夹", - "move_copy": "移动或复制" - }, - "admin": { - "page_title": "管理面板", - "back_to_app": "返回 OxiCloud", - "loading": "加载中…", - "access_denied": "拒绝访问", - "access_denied_desc": "需要管理员权限。", - "sign_in": "登录", - "tab_dashboard": "仪表盘", - "tab_users": "用户", - "tab_oidc": "SSO / OIDC", - "total_users": "用户总数", - "active_users": "活跃用户", - "admins": "管理员", - "version": "版本", - "storage_overview": "存储概览", - "used": "已使用", - "total_quota": "总配额", - "usage_pct": "使用率", - "users_over_80": "超过80%配额", - "users_over_quota": "超过配额", - "system": "系统", - "auth_label": "认证", - "oidc_label": "OIDC", - "quotas_label": "配额", - "enabled": "已启用", - "disabled": "已禁用", - "active": "活跃", - "off": "关闭", - "allow_registration": "允许公开自助注册", - "registration_warning": "公开注册已禁用。只有管理员可以创建新用户。", - "user_management": "用户管理", - "create_user": "创建用户", - "col_user": "用户", - "col_role": "角色", - "col_auth": "认证", - "col_status": "状态", - "col_storage": "存储", - "col_last_login": "最后登录", - "col_actions": "操作", - "loading_users": "正在加载用户…", - "failed_load_users": "加载失败", - "no_users_found": "未找到用户", - "showing_users": "显示 {{from}}-{{to}} / {{total}}", - "prev": "上一页", - "next": "下一页", - "inactive": "未激活", - "you_badge": "(你)", - "local": "本地", - "never": "从未", - "just_now": "刚刚", - "minutes_ago": "{{n}}分钟前", - "hours_ago": "{{n}}小时前", - "days_ago": "{{n}}天前", - "edit_quota_title": "编辑配额", - "reset_password_title": "重置密码", - "toggle_role_title": "切换角色", - "deactivate_title": "停用", - "activate_title": "启用", - "delete_title": "删除", - "sso_title": "单点登录 (OIDC / SSO)", - "enable_sso": "启用 SSO 认证", - "provider_name": "提供商名称", - "issuer_url": "发行者 URL", - "issuer_url_hint": "您的身份提供商的 OpenID Connect 发行者 URL", - "auto_discover": "自动发现", - "discovering": "发现中…", - "client_id": "客户端 ID", - "client_secret": "客户端密钥", - "client_secret_placeholder": "留空以保留当前值", - "secret_configured": "已配置客户端密钥", - "callback_url": "回调 URL", - "callback_url_hint": "(在您的 IdP 中注册)", - "advanced_settings": "高级设置", - "scopes": "范围", - "auto_provision": "首次登录时自动配置用户", - "admin_groups": "管理组", - "admin_groups_hint": "映射到管理员角色的逗号分隔 OIDC 组名", - "disable_password": "禁用密码登录 (仅 OIDC)", - "password_warning": "这将阻止所有基于密码的登录!", - "test_btn": "测试", - "save_btn": "保存", - "saving": "保存中…", - "settings_saved": "设置已保存 — OIDC 现在 {{status}}", - "quota_modal_title": "更新存储配额", - "quota_user_label": "用户:", - "new_quota": "新配额", - "quota_unlimited_hint": "0表示无限制", - "cancel": "取消", - "create_user_title": "创建新用户", - "username_label": "用户名", - "username_placeholder": "zhangsan", - "username_hint": "3–32个字符", - "password_label": "密码", - "password_placeholder": "至少8个字符", - "email_label": "邮箱", - "email_optional": "(可选)", - "email_placeholder": "user@example.com (留空自动生成)", - "role_label": "角色", - "role_user": "用户", - "role_admin": "管理员", - "quota_label": "配额", - "creating": "创建中…", - "reset_pw_title": "重置密码", - "new_password_label": "新密码", - "resetting": "重置中…", - "reset_btn": "重置", - "confirm_role_change": "将角色更改为 {{role}}?", - "confirm_deactivate": "确定要停用此用户吗?", - "confirm_activate": "确定要启用此用户吗?", - "confirm_delete_user": "删除用户 \"{{name}}\"?此操作无法撤消!", - "confirm_action": "确认操作", - "confirm_yes": "确认", - "confirm_no": "取消", - "error_username_short": "用户名至少需要3个字符", - "error_password_short": "密码至少需要8个字符", - "error_generic": "失败", - "error_network": "网络错误:{{message}}", - "error_create_user": "创建用户失败", - "tab_storage": "存储", - "storage_title": "存储配置", - "storage_current_backend": "当前后端", - "storage_total_blobs": "总块数", - "storage_total_size": "总大小", - "storage_dedup_ratio": "去重比率", - "storage_backend": "后端", - "storage_local": "本地", - "storage_s3": "S3 兼容", - "storage_provider_preset": "提供商预设", - "storage_preset_custom": "自定义", - "storage_endpoint_url": "端点 URL", - "storage_endpoint_hint": "AWS S3 请留空", - "storage_bucket": "存储桶", - "storage_region": "地区", - "storage_access_key": "访问密钥", - "storage_secret_key": "密钥", - "storage_secret_configured": "密钥已配置", - "storage_key_placeholder": "输入新密钥", - "storage_path_style": "强制路径风格", - "storage_path_style_hint": "MinIO 及某些 S3 兼容服务需要此选项", - "storage_test_connection": "测试连接", - "storage_test_success": "连接成功", - "storage_test_failure": "连接失败", - "storage_save": "保存配置", - "storage_saved": "配置已保存", - "storage_migration": "数据迁移", - "storage_migration_coming_soon": "迁移工具即将推出", - "migration_status_label": "迁移状态", - "migration_start": "开始迁移", - "migration_pause": "暂停", - "migration_resume": "继续", - "migration_verify": "验证", - "migration_complete": "完成", - "migration_started": "迁移已开始", - "migration_paused_msg": "迁移已暂停", - "migration_resumed_msg": "迁移已继续", - "migration_completed_msg": "迁移成功完成", - "migration_verifying": "正在验证...", - "migration_verify_passed": "验证通过", - "migration_verify_failed": "验证失败", - "migration_failed_blobs": "失败的块", - "testing": "正在测试...", - "smtp_disabled": "已禁用(未设置主机)", - "smtp_enabled": "已启用", - "smtp_enabled_label": "状态", - "smtp_intro": "SMTP 仅通过环境变量(OXICLOUD_SMTP_*)配置。以下值是从运行中的服务器读取的 — 如需修改,请编辑环境变量并重启 OxiCloud。", - "smtp_not_configured": "此服务器未配置 SMTP。", - "smtp_send_failed": "发送失败。", - "smtp_send_test": "发送测试邮件", - "smtp_sending": "发送中…", - "smtp_sent": "测试邮件已发送。", - "smtp_server_code": "服务器回复", - "smtp_test_intro": "向下方收件人发送预设的诊断消息,并报告 SMTP 服务器的响应,以便您与中继日志进行核对。", - "smtp_test_missing_to": "请输入收件人地址。", - "smtp_test_title": "发送测试邮件", - "smtp_test_to": "收件人地址", - "smtp_title": "出站邮件 (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "个人资料", - "back_to_app": "返回 OxiCloud", - "loading": "加载中…", - "not_authenticated": "未认证", - "not_authenticated_desc": "请登录以查看您的个人资料。", - "sign_in": "登录", - "role_admin": "管理员", - "role_user": "用户", - "account_details": "账户详情", - "username": "用户名", - "email": "邮箱", - "role": "角色", - "last_login": "最后登录", - "storage": "存储", - "used": "已使用", - "quota": "配额", - "usage": "使用率", - "unlimited": "无限制", - "app_passwords": "应用密码", - "app_pw_desc": "为 WebDAV、CalDAV 和 CardDAV 客户端生成密码。每个密码只显示一次。", - "app_pw_label_placeholder": "标签(如 Thunderbird、macOS)", - "generate": "生成", - "generating": "生成中…", - "new_password_for": "新密码用于", - "copy_warning": "请立即复制此密码,之后将无法再次查看。", - "copy_to_clipboard": "复制到剪贴板", - "col_label": "标签", - "col_created": "创建时间", - "col_last_used": "最后使用", - "col_status": "状态", - "active": "活跃", - "revoked": "已撤销", - "revoke_title": "撤销", - "no_app_passwords": "暂无应用密码。", - "client_sessions": "客户端会话", - "client_sessions_desc": "连接 Nextcloud 兼容客户端时自动生成。", - "col_client": "客户端", - "never": "从未", - "just_now": "刚刚", - "minutes_ago": "{{n}}分钟前", - "hours_ago": "{{n}}小时前", - "days_ago": "{{n}}天前", - "edit_profile": "编辑个人资料", - "edit_oidc_managed": "要更改您的信息(姓名、名字、头像等),请前往您的身份提供商更新。变更将在您下次登录时显示。", - "username_claim_hint": "2-64 个字符,字母 / 数字 / 点 / 短横线 / 下划线。一旦选定,用户名将无法更改(DAV/NextCloud 客户端依赖它)。", - "username_already_claimed": "用户名已设置,不可更改(DAV/NextCloud 客户端依赖它)。", - "given_name": "名", - "family_name": "姓", - "notify_on_share": "当有人与我共享时通过电子邮件通知我", - "notify_on_share_hint": "取消勾选后,共享项目仍会显示在您的账户中 — 只是不会收到相关邮件通知。", - "save_profile": "保存更改", - "profile_saved": "个人资料已更新", - "profile_no_changes": "无更改可保存。", - "profile_save_failed": "保存失败", - "username_taken_error": "该用户名已被占用。", - "username_immutable_error": "您的用户名已设置,无法在此更改。如需重命名,请联系管理员。", - "change_password": "修改密码", - "current_password": "当前密码", - "new_password": "新密码", - "min_8_chars": "至少8个字符", - "confirm_password": "确认新密码", - "update_password": "更新密码", - "updating": "更新中…", - "password_updated": "密码更新成功", - "passwords_no_match": "密码不匹配", - "password_too_short": "密码至少需要8个字符", - "password_change_failed": "修改密码失败", - "error_network": "网络错误:{{message}}", - "error_label_required": "请输入标签", - "error_create_pw": "创建应用密码失败", - "confirm_revoke": "撤销应用密码\"{{label}}\"?使用此密码的客户端将停止工作。", - "error_revoke": "撤销失败", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "notifications": { - "file_renamed": "文件已重命名", - "file_renamed_to": "文件已重命名为\"{{name}}\"", - "folder_renamed": "文件夹已重命名", - "folder_renamed_to": "文件夹已重命名为\"{{name}}\"", - "file_uploaded": "文件已上传", - "file_deleted": "文件已移至回收站", - "folder_deleted": "文件夹已移至回收站", - "item_deleted_permanently": "项目已永久删除", - "trash_emptied": "回收站已清空", - "title": "通知", - "empty": "暂无通知", - "link_created": "链接已创建", - "share_success": "分享链接创建成功", - "upload_files_section_title": "此处不支持上传", - "upload_files_section_body": "请前往文件部分上传文件" - }, - "upload": { - "uploading": "正在上传...", - "files": "个文件", - "complete": "已上传 {{count}} / {{total}}" - }, - "storage_quota_exceeded": "存储配额已超限", - "sharedwithme": { - "pageTitle": "与我共享", - "pageDescription": "其他用户与您共享的文件和文件夹", - "emptyStateTitle": "暂无内容与您共享", - "emptyStateDesc": "其他用户与您共享的项目将显示在此处", - "loadMore": "加载更多", - "sharedBy": "共享者", - "colName": "名称", - "colType": "类型", - "colSharedBy": "共享者", - "colDate": "共享日期", - "colPermissions": "权限" - }, - "groupby": { - "none": "无", - "title": "分组方式", - "owner": "所有者", - "shareDate": "分享日期", - "type": "类型", - "type.folders": "文件夹", - "accessedAt": "访问日期", - "modifiedAt": "修改日期", - "createdAt": "创建日期", - "size": "大小", - "favoriteDate": "收藏日期", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "新建" - }, - "dateBucket": { - "today": "今天", - "last7days": "近7天", - "last30days": "近30天" - }, - "groups": { - "title": "管理群组", - "create_button": "创建群组", - "create_dialog_title": "新群组", - "edit_dialog_title": "重命名群组", - "name_label": "名称", - "name_placeholder": "engineering", - "description_label": "描述(可选)", - "members_section": "成员", - "add_member_placeholder": "添加用户或群组…", - "no_members": "暂无成员。", - "remove_member": "移除", - "delete_group": "删除群组", - "delete_confirm": "删除群组 \"{name}\"?引用此群组的所有权限将被撤销。", - "empty_state": "暂无群组。", - "load_more": "加载更多", - "back_to_list": "返回", - "loading": "加载中…", - "virtual_badge": "系统", - "member_count_zero": "无成员", - "member_count_one": "1 个成员", - "member_count_other": "{count} 个成员", - "delete_confirm_label": "请输入群组名称以确认:", - "delete_confirm_mismatch": "请准确输入群组名称以确认。", - "virtual_internal_name": "内部", - "members_loading": "正在加载成员…", - "members_empty": "无成员", - "virtual_internal_explanation": "本服务器上的所有内部用户" - }, - "myshares": { - "copyLink": "复制链接", - "deleteLink": "删除链接", - "notifyByEmail": "通过邮件通知", - "notifyFailed": "无法发送通知。", - "notifyGroupMembers": "通知群组成员", - "notifyRateLimited": "对此收件人的通知过多 — 请稍后重试。", - "removeAccess": "移除访问权限", - "resendInvitation": "重新发送邀请邮件" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" + "notification": { + "share": { + "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", + "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n打开 OxiCloud 查看您的新共享:\n{{login_link}}\n\n您可能还有来自 {{inviter}} 的其他新共享 — 登录以查看所有共享给您的项目。\n\n— OxiCloud\n\n您收到此消息是因为您拥有 OxiCloud 账户且共享通知偏好已开启。您可以在个人资料中关闭它(当有人与我共享时通过电子邮件通知我)。" + } } + }, + "app": { + "title": "OxiCloud", + "description": "极简云存储系统" + }, + "nav": { + "files": "文件", + "shared": "共享", + "recent": "最近", + "favorites": "收藏", + "photos": "照片", + "music": "音乐", + "trash": "回收站", + "sharedwithme": "与我共享", + "profile": "个人资料", + "shared_with_me": "与我共享" + }, + "photos": { + "empty_state": "还没有照片", + "empty_hint": "上传图片或视频即可在此查看", + "items_selected": "已选择", + "view_daily": "日", + "view_monthly": "月", + "view_yearly": "年", + "group_by": "分组方式" + }, + "music": { + "create_playlist": "创建播放列表", + "playlists": "播放列表", + "no_playlists": "还没有播放列表", + "select_playlist": "选择一个播放列表", + "select_hint": "从侧边栏选择播放列表或创建新播放列表", + "add_tracks": "添加曲目", + "no_tracks": "此播放列表中没有曲目", + "unknown_artist": "未知艺术家", + "unknown_title": "未知", + "confirm_delete": "删除此播放列表?", + "playlist_name": "播放列表名称", + "create": "创建", + "delete": "删除", + "share": "分享", + "edit": "编辑", + "play_all": "全部播放", + "shuffle": "随机播放", + "repeat": "重复", + "repeat_one": "单曲循环", + "queue": "播放队列", + "queue_empty": "播放队列为空", + "not_playing": "未播放", + "play": "播放", + "pause": "暂停", + "previous": "上一首", + "next": "下一首", + "volume": "音量", + "mute": "静音", + "unmute": "取消静音", + "title": "标题", + "artist": "艺术家", + "album": "专辑", + "tracks": "首曲目", + "add": "添加", + "added": "已添加!", + "added_to_playlist": "已添加到播放列表", + "add_to_playlist": "添加到播放列表", + "load_error": "加载播放列表出错", + "add_error": "无法将曲目添加到播放列表", + "no_playlists_yet": "暂无播放列表。请先创建一个!", + "selected_files": "已选择:", + "error": "错误", + "search_audio": "搜索音频文件…", + "no_audio_files": "未找到音频文件", + "selected": "已选择", + "loading": "加载中…", + "search_error": "无法加载音频文件", + "adding": "添加中…", + "can_write": "Can edit", + "cover_updated": "Cover updated", + "empty_hint": "Create your first playlist to start organizing your music", + "make_private": "Make private", + "make_public": "Make public", + "manage_shares": "Manage Shares", + "no_shares": "No shares yet", + "playback_error": "Playback failed", + "private": "Private", + "public": "Public", + "read_only": "Read only", + "remove": "Remove", + "remove_share": "Remove share", + "set_cover": "Set cover", + "share_with_user": "User ID or email", + "toggle_public": "Visibility", + "track_removed": "Track removed", + "prev": "上一首" + }, + "actions": { + "search": "搜索文件...", + "new_folder": "新建文件夹", + "upload": "上传", + "upload_files": "上传文件", + "upload_folder": "上传文件夹", + "upload.uploading": "上传中...", + "upload.complete": "{count} / {total} 已上传", + "upload.files": "文件", + "rename": "重命名", + "move": "移动到...", + "move_to": "移动到", + "delete": "删除", + "download": "下载", + "view": "查看", + "cancel": "取消", + "confirm": "确认", + "share": "共享", + "favorite": "添加到收藏", + "unfavorite": "取消收藏", + "copy": "复制", + "notify": "通知", + "send": "发送", + "clear_recent": "清除最近", + "logout": "退出登录", + "create": "创建", + "search_btn": "搜索", + "close": "关闭", + "delete_permanently": "Delete permanently", + "empty_trash": "Empty trash", + "open_parent_folder": "转到父文件夹", + "add": "Add", + "apply": "Apply", + "clear": "Clear", + "remove": "Remove" + }, + "user_menu": { + "appearance": "外观", + "about": "关于 OxiCloud", + "about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。", + "admin_panel": "管理面板", + "profile": "我的资料", + "role_user": "用户", + "theme": { + "light": "浅色", + "dark": "深色", + "auto": "跟随系统" + }, + "manage_groups": "管理群组", + "admin": "管理员" + }, + "share": { + "dialogTitle": "共享链接", + "linkLabel": "共享链接:", + "copyLink": "复制", + "permissions": "权限:", + "permissionRead": "读取", + "permissionWrite": "写入", + "permissionReshare": "再共享", + "password": "密码保护:", + "generatePassword": "生成", + "expiration": "过期日期:", + "update": "更新共享", + "remove": "移除共享", + "notifyTitle": "发送通知", + "notifyEmailLabel": "电子邮件地址:", + "notifyMessageLabel": "消息(可选):", + "notifySend": "发送通知", + "shareWithOthers": "与他人共享", + "sharePublicly": "公开共享", + "shareSettings": "共享设置", + "shareCopied": "链接已复制到剪贴板", + "shareCreated": "共享链接创建成功", + "shareUpdated": "共享设置更新成功", + "shareRemoved": "共享已移除", + "inviteByEmail": "通过邮件邀请 — 将发送邀请", + "directoryUnavailable": "User directory unavailable", + "linkNamePlaceholder": "Link name (optional)", + "newLink": "New link", + "noExpiry": "No expiry", + "pending": "Pending", + "people": "People", + "publicLinks": "Public links", + "role": { + "canEdit": "Can edit", + "canManage": "Can manage", + "canView": "Can view" + }, + "searchPlaceholder": "Search people…", + "shareOf": "Share of:", + "sharedLink": "Shared link", + "copied": "Link copied", + "copy": "复制", + "copy_failed": "Could not copy link", + "download": "下载", + "files": "文件", + "folders": "文件夹", + "link_name": "Link name (optional)", + "notifyByEmail": "通过邮件通知", + "revoke": "Remove", + "role_label": "角色" + }, + "share_dialogTitle": "共享链接", + "share_linkLabel": "共享链接:", + "share_copyLink": "复制", + "share_permissions": "权限:", + "share_permissionRead": "读取", + "share_permissionWrite": "写入", + "share_permissionReshare": "再共享", + "share_password": "密码保护:", + "share_generatePassword": "生成", + "share_expiration": "过期日期:", + "share_update": "更新共享", + "share_remove": "移除共享", + "share_notifyTitle": "发送通知", + "share_notifyEmailLabel": "电子邮件地址:", + "share_notifyMessageLabel": "消息(可选):", + "share_notifySend": "发送通知", + "shared": { + "backToFiles": "返回文件", + "pageTitle": "共享资源", + "pageDescription": "管理你的共享文件和文件夹", + "filterType": "类型:", + "filterAll": "全部", + "filterFiles": "文件", + "filterFolders": "文件夹", + "sortBy": "排序依据:", + "sortByName": "名称", + "sortByDate": "共享日期", + "sortByExpiration": "过期日期", + "search": "搜索", + "colName": "名称", + "colType": "类型", + "colDateShared": "共享日期", + "colExpiration": "过期日期", + "colPermissions": "权限", + "colPassword": "密码", + "colActions": "操作", + "emptyStateTitle": "尚未有共享资源", + "emptyStateDesc": "当你共享文件或文件夹时,它们会出现在这里", + "goToFiles": "前往文件", + "typeFile": "文件", + "typeFolder": "文件夹", + "noExpiration": "无过期", + "hasPassword": "有", + "noPassword": "无", + "editShare": "编辑共享", + "notifyShare": "通知某人", + "copyLink": "复制链接", + "removeShare": "移除共享", + "linkCopied": "链接已复制到剪贴板!", + "linkCopyFailed": "复制链接失败", + "itemUpdated": "共享设置更新成功", + "itemRemoved": "共享已移除成功", + "invalidEmail": "请输入有效的电子邮件地址", + "notificationSent": "通知已成功发送", + "notificationFailed": "发送通知失败", + "shared_backToFiles": "Back to Files", + "shared_colActions": "Actions", + "shared_colDateShared": "Date Shared", + "shared_colExpiration": "Expiration", + "shared_colName": "Name", + "shared_colPassword": "Password", + "shared_colPermissions": "Permissions", + "shared_colType": "Type", + "shared_copyLink": "Copy Link", + "shared_editShare": "Edit Share", + "shared_emptyStateDesc": "When you share files or folders, they will appear here", + "shared_emptyStateTitle": "No shared resources yet", + "shared_filterAll": "All", + "shared_filterFiles": "Files", + "shared_filterFolders": "Folders", + "shared_filterType": "Type:", + "shared_goToFiles": "Go to Files", + "shared_hasPassword": "Yes", + "shared_invalidEmail": "Please enter a valid email address", + "shared_itemRemoved": "Share removed successfully", + "shared_itemUpdated": "Share settings updated successfully", + "shared_linkCopied": "Link copied to clipboard!", + "shared_linkCopyFailed": "Failed to copy link", + "shared_noExpiration": "No expiration", + "shared_noPassword": "No", + "shared_notificationFailed": "Failed to send notification", + "shared_notificationSent": "Notification sent successfully", + "shared_notifyShare": "Notify Someone", + "shared_pageDescription": "Manage your shared files and folders", + "shared_pageTitle": "Shared Resources", + "shared_removeShare": "Remove Share", + "shared_search": "Search", + "shared_sortBy": "Sort by:", + "shared_sortByDate": "Date shared", + "shared_sortByExpiration": "Expiration", + "shared_sortByName": "Name", + "shared_typeFile": "File", + "shared_typeFolder": "Folder" + }, + "files": { + "name": "名称", + "type": "类型", + "size": "大小", + "modified": "修改日期", + "no_files": "此文件夹中没有文件", + "empty_hint": "上传文件或创建文件夹以开始使用", + "loading": "正在加载文件…", + "view_grid": "网格视图", + "view_list": "列表视图", + "file_types": { + "document": "文档", + "image": "图片", + "video": "视频", + "audio": "音频", + "pdf": "PDF", + "text": "文本", + "folder": "文件夹", + "spreadsheet": "电子表格", + "presentation": "演示文稿", + "archive": "压缩文件", + "installer": "安装程序", + "code": "代码" + }, + "owner": "所有者", + "add_favorites": "添加到收藏", + "added_favorites": "已添加到收藏", + "col_name": "名称", + "col_owner": "所有者", + "col_size": "大小", + "col_type": "类型", + "copy": "复制", + "edit": "编辑", + "file": "文件", + "folder": "文件夹", + "new_folder": "新建文件夹", + "share": "分享", + "view": "查看" + }, + "dialogs": { + "rename_folder": "重命名文件夹", + "new_name": "新名称", + "new_folder_title": "新建文件夹", + "folder_name": "文件夹名称", + "folder_placeholder": "我的文件夹", + "rename_title": "重命名", + "move_file": "移动文件", + "select_destination": "选择目标文件夹", + "root": "根目录", + "delete_confirmation": "你确定要删除", + "and_contents": "及其所有内容", + "no_undo": "此操作无法撤销", + "share_file": "共享文件", + "share_folder": "共享文件夹", + "existing_shares": "现有共享", + "share_options": "共享选项", + "password": "密码", + "expiration": "过期日期", + "permissions": "权限", + "generated_link": "生成的链接", + "notify": "发送通知", + "recipient": "收件人", + "message": "消息", + "confirm_delete": "Move to trash", + "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", + "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", + "confirm_delete_share": "Delete share link", + "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", + "confirm_empty_trash": "Empty trash", + "confirm_permanent_delete": "Delete permanently", + "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", + "confirm_title": "Confirm action", + "go_to_parent": ".. (parent folder)", + "move_folder": "Move folder", + "no_subfolders": "No subfolders", + "rename_file": "Rename file", + "select_this_folder": "Select this folder", + "move_to_home": "移动到主文件夹" + }, + "dropzone": { + "drag_files": "将文件拖到这里,或点击选择", + "drop_files": "释放文件以上传" + }, + "permissions": { + "read": "读取", + "write": "写入", + "reshare": "再共享" + }, + "errors": { + "file_not_found": "文件未找到", + "folder_not_found": "文件夹未找到", + "delete_error": "删除时出错", + "upload_error": "上传文件时出错", + "rename_error": "重命名时出错", + "move_error": "移动时出错", + "empty_name": "名称不能为空", + "name_exists": "已存在同名文件或文件夹", + "generic_error": "发生错误", + "group_name_invalid": "组名必须符合邮件前缀格式(字母、数字、点、连字符、下划线;1–64个字符)。", + "group_cycle": "此成员会在组之间形成循环引用。", + "group_depth_exceeded": "嵌套深度超出允许的最大值(8)。", + "group_virtual_immutable": "“Internal”组由系统管理,无法修改。", + "group_not_found": "未找到组。", + "group_name_taken": "同名组已存在。" + }, + "breadcrumb": { + "home": "主页" + }, + "trash": { + "empty_trash": "清空回收站", + "empty_state": "回收站为空", + "original_location": "原始位置", + "deleted_date": "删除日期", + "remaining": "剩余", + "actions": "操作", + "restore": "恢复", + "delete_permanently": "永久删除", + "empty_confirm": "你确定要清空回收站吗?这将永久删除所有项目。", + "groupby": { + "remaining_days": "剩余天数", + "trashed_time": "删除时间" + }, + "delete": "永久删除", + "empty_action": "Empty trash" + }, + "daysRemaining": { + "expired": "已过期", + "today": "今天", + "tomorrow": "明天", + "inDays": "{{count}} 天" + }, + "expiryChip": { + "never": "永不过期", + "expired": "已过期", + "today": "今天到期", + "tomorrow": "明天到期", + "inDays": "{{count}} 天后到期", + "onDate": "于 {{date}} 到期" + }, + "auth": { + "login_title": "登录", + "username": "用户名", + "username_placeholder": "输入你的用户名", + "login_identifier": "用户名或邮箱", + "login_identifier_placeholder": "请输入用户名或邮箱", + "password": "密码", + "password_placeholder": "输入你的密码", + "login_button": "登录", + "no_account": "没有账号?", + "register": "注册", + "admin_setup": "首次使用?", + "setup": "设置管理员", + "register_title": "创建账号", + "email": "电子邮件", + "email_placeholder": "输入你的电子邮件", + "confirm_password": "确认密码", + "confirm_password_placeholder": "确认你的密码", + "register_button": "创建账号", + "have_account": "已有账号?", + "login": "登录", + "setup_title": "初始设置", + "setup_step1": "管理员", + "setup_step2": "系统", + "setup_step3": "完成", + "admin_username": "管理员用户名", + "admin_email": "管理员电子邮件", + "admin_password": "管理员密码", + "create_admin": "创建管理员", + "back_to_login": "已设置完成?", + "admin_success": "管理员账号创建成功!您现在可以登录。", + "account_success": "账号创建成功!您现在可以登录。", + "passwords_mismatch": "密码不匹配", + "admin_create_error": "创建管理员账号时出错", + "or": "或", + "sso_login": "使用 SSO 登录", + "sso_login_provider": "使用 {{provider}} 登录", + "magicLinkHint": "没有密码?输入您的邮箱,我们将向您发送一次性登录链接。", + "magicLinkEmailLabel": "邮箱地址", + "magicLinkEmailPlaceholder": "you@example.com", + "magicLinkSubmit": "发送登录链接", + "magicLinkSent": "如果该邮箱存在账户,登录链接已发送。请查收您的收件箱。", + "magicLinkUnavailable": "此服务器不支持邮箱登录。", + "magicLinkNetworkError": "无法连接到服务器:{{message}}", + "magicLinkToggle": "No password? Email me a sign-in link", + "passwordsMatch": "Passwords match", + "capsLock": "Caps Lock is on", + "caps_lock": "Caps Lock is on", + "magic_email_label": "邮箱地址", + "magic_hint": "没有密码?输入您的邮箱,我们将向您发送一次性登录链接。", + "magic_unavailable": "此服务器不支持邮箱登录。", + "passwords_match": "Passwords match", + "sign_in": "登录" + }, + "storage": { + "title": "存储空间", + "calculating": "计算中...", + "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" + }, + "viewer": { + "unsupported_file": "无法预览此文件类型。", + "download_file": "下载文件", + "zoom_in": "放大", + "zoom_out": "缩小", + "zoom_reset": "重置缩放" + }, + "language_selector": { + "title": "欢迎!", + "subtitle": "选择您的语言以继续", + "continue": "继续", + "languages": { + "en": "English", + "es": "Español", + "zh": "中文", + "fa": "فارسی", + "fr": "Français", + "de": "Deutsch", + "pt": "Português", + "ar": "العربية", + "hi": "हिन्दी", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "nl": "Nederlands", + "ru": "Русский" + } + }, + "favorites": { + "empty_state": "还没有收藏", + "empty_hint": "为文件或文件夹添加星标以将其添加到收藏夹", + "add": "添加到收藏夹", + "remove": "从收藏夹移除", + "added_title": "已添加到收藏", + "added_msg": "已添加到收藏", + "removed_title": "已从收藏移除", + "removed_msg": "已从收藏移除" + }, + "recent": { + "title": "最近", + "clear": "清除最近", + "accessed": "访问于", + "empty_state": "没有最近文件", + "empty_hint": "您打开的文件将显示在这里", + "loadMore": "加载更多" + }, + "batch": { + "one_selected": "已选择 1 个项目", + "n_selected": "已选择 {{count}} 个项目", + "confirm_delete": "确定要将 {{count}} 个项目移至回收站吗?", + "move_title": "移动 {{count}} 个项目", + "add_favorites": "添加到收藏夹", + "move_copy": "移动或复制" + }, + "admin": { + "page_title": "管理面板", + "back_to_app": "返回 OxiCloud", + "loading": "加载中…", + "access_denied": "拒绝访问", + "access_denied_desc": "需要管理员权限。", + "sign_in": "登录", + "tab_dashboard": "仪表盘", + "tab_users": "用户", + "tab_oidc": "SSO / OIDC", + "total_users": "用户总数", + "active_users": "活跃用户", + "admins": "管理员", + "version": "版本", + "storage_overview": "存储概览", + "used": "已使用", + "total_quota": "总配额", + "usage_pct": "使用率", + "users_over_80": "超过80%配额", + "users_over_quota": "超过配额", + "system": "系统", + "auth_label": "认证", + "oidc_label": "OIDC", + "quotas_label": "配额", + "enabled": "已启用", + "disabled": "已禁用", + "active": "活跃", + "off": "关闭", + "allow_registration": "允许公开自助注册", + "registration_warning": "公开注册已禁用。只有管理员可以创建新用户。", + "user_management": "用户管理", + "create_user": "创建用户", + "col_user": "用户", + "col_role": "角色", + "col_auth": "认证", + "col_status": "状态", + "col_storage": "存储", + "col_last_login": "最后登录", + "col_actions": "操作", + "loading_users": "正在加载用户…", + "failed_load_users": "加载失败", + "no_users_found": "未找到用户", + "showing_users": "显示 {{from}}-{{to}} / {{total}}", + "prev": "上一页", + "next": "下一页", + "inactive": "未激活", + "you_badge": "(你)", + "local": "本地", + "never": "从未", + "just_now": "刚刚", + "minutes_ago": "{{n}}分钟前", + "hours_ago": "{{n}}小时前", + "days_ago": "{{n}}天前", + "edit_quota_title": "编辑配额", + "reset_password_title": "重置密码", + "toggle_role_title": "切换角色", + "deactivate_title": "停用", + "activate_title": "启用", + "delete_title": "删除", + "sso_title": "单点登录 (OIDC / SSO)", + "enable_sso": "启用 SSO 认证", + "provider_name": "提供商名称", + "issuer_url": "发行者 URL", + "issuer_url_hint": "您的身份提供商的 OpenID Connect 发行者 URL", + "auto_discover": "自动发现", + "discovering": "发现中…", + "client_id": "客户端 ID", + "client_secret": "客户端密钥", + "client_secret_placeholder": "留空以保留当前值", + "secret_configured": "已配置客户端密钥", + "callback_url": "回调 URL", + "callback_url_hint": "(在您的 IdP 中注册)", + "advanced_settings": "高级设置", + "scopes": "范围", + "auto_provision": "首次登录时自动配置用户", + "admin_groups": "管理组", + "admin_groups_hint": "映射到管理员角色的逗号分隔 OIDC 组名", + "disable_password": "禁用密码登录 (仅 OIDC)", + "password_warning": "这将阻止所有基于密码的登录!", + "test_btn": "测试", + "save_btn": "保存", + "saving": "保存中…", + "settings_saved": "设置已保存 — OIDC 现在 {{status}}", + "quota_modal_title": "更新存储配额", + "quota_user_label": "用户:", + "new_quota": "新配额", + "quota_unlimited_hint": "0表示无限制", + "cancel": "取消", + "create_user_title": "创建新用户", + "username_label": "用户名", + "username_placeholder": "zhangsan", + "username_hint": "3–32个字符", + "password_label": "密码", + "password_placeholder": "至少8个字符", + "email_label": "邮箱", + "email_optional": "(可选)", + "email_placeholder": "user@example.com (留空自动生成)", + "role_label": "角色", + "role_user": "用户", + "role_admin": "管理员", + "quota_label": "配额", + "creating": "创建中…", + "reset_pw_title": "重置密码", + "new_password_label": "新密码", + "resetting": "重置中…", + "reset_btn": "重置", + "confirm_role_change": "将角色更改为 {{role}}?", + "confirm_deactivate": "确定要停用此用户吗?", + "confirm_activate": "确定要启用此用户吗?", + "confirm_delete_user": "删除用户 \"{{name}}\"?此操作无法撤消!", + "confirm_action": "确认操作", + "confirm_yes": "确认", + "confirm_no": "取消", + "error_username_short": "用户名至少需要3个字符", + "error_password_short": "密码至少需要8个字符", + "error_generic": "失败", + "error_network": "网络错误:{{message}}", + "error_create_user": "创建用户失败", + "tab_storage": "存储", + "storage_title": "存储配置", + "storage_current_backend": "当前后端", + "storage_total_blobs": "总块数", + "storage_total_size": "总大小", + "storage_dedup_ratio": "去重比率", + "storage_backend": "后端", + "storage_local": "本地", + "storage_s3": "S3 兼容", + "storage_provider_preset": "提供商预设", + "storage_preset_custom": "自定义", + "storage_endpoint_url": "端点 URL", + "storage_endpoint_hint": "AWS S3 请留空", + "storage_bucket": "存储桶", + "storage_region": "地区", + "storage_access_key": "访问密钥", + "storage_secret_key": "密钥", + "storage_secret_configured": "密钥已配置", + "storage_key_placeholder": "输入新密钥", + "storage_path_style": "强制路径风格", + "storage_path_style_hint": "MinIO 及某些 S3 兼容服务需要此选项", + "storage_test_connection": "测试连接", + "storage_test_success": "连接成功", + "storage_test_failure": "连接失败", + "storage_save": "保存配置", + "storage_saved": "配置已保存", + "storage_migration": "数据迁移", + "storage_migration_coming_soon": "迁移工具即将推出", + "migration_status_label": "迁移状态", + "migration_start": "开始迁移", + "migration_pause": "暂停", + "migration_resume": "继续", + "migration_verify": "验证", + "migration_complete": "完成", + "migration_started": "迁移已开始", + "migration_paused_msg": "迁移已暂停", + "migration_resumed_msg": "迁移已继续", + "migration_completed_msg": "迁移成功完成", + "migration_verifying": "正在验证...", + "migration_verify_passed": "验证通过", + "migration_verify_failed": "验证失败", + "migration_failed_blobs": "失败的块", + "testing": "正在测试...", + "smtp_disabled": "已禁用(未设置主机)", + "smtp_enabled": "已启用", + "smtp_enabled_label": "状态", + "smtp_intro": "SMTP 仅通过环境变量(OXICLOUD_SMTP_*)配置。以下值是从运行中的服务器读取的 — 如需修改,请编辑环境变量并重启 OxiCloud。", + "smtp_not_configured": "此服务器未配置 SMTP。", + "smtp_send_failed": "发送失败。", + "smtp_send_test": "发送测试邮件", + "smtp_sending": "发送中…", + "smtp_sent": "测试邮件已发送。", + "smtp_server_code": "服务器回复", + "smtp_test_intro": "向下方收件人发送预设的诊断消息,并报告 SMTP 服务器的响应,以便您与中继日志进行核对。", + "smtp_test_missing_to": "请输入收件人地址。", + "smtp_test_title": "发送测试邮件", + "smtp_test_to": "收件人地址", + "smtp_title": "出站邮件 (SMTP)", + "tab_smtp": "SMTP", + "admin_users": "管理员", + "confirm_role": "将角色更改为 {{role}}?", + "dashboard": "仪表盘", + "email": "邮箱", + "mig_complete": "完成", + "mig_pause": "暂停", + "mig_resume": "继续", + "mig_verify_failed": "验证失败", + "mig_verify_passed": "验证通过", + "mig_verifying": "正在验证...", + "oidc_auto_provision": "首次登录时自动配置用户", + "oidc_callback": "回调 URL", + "oidc_client_id": "客户端 ID", + "oidc_disable_pw": "禁用密码登录 (仅 OIDC)", + "oidc_issuer": "发行者 URL", + "oidc_scopes": "范围", + "password": "密码", + "quotas": "配额", + "reset_pw_for": "新密码用于", + "role": "角色", + "smtp_fail": "发送失败。", + "smtp_send": "发送", + "smtp_test": "发送测试邮件", + "smtp_user_state": "认证", + "status": "状态", + "storage": "存储", + "storage_endpoint": "端点 URL", + "storage_tab": "存储", + "time_min_ago": "{{n}}分钟前", + "title": "管理员", + "user": "用户", + "username": "用户名", + "users": "用户" + }, + "profile": { + "page_title": "个人资料", + "back_to_app": "返回 OxiCloud", + "loading": "加载中…", + "not_authenticated": "未认证", + "not_authenticated_desc": "请登录以查看您的个人资料。", + "sign_in": "登录", + "role_admin": "管理员", + "role_user": "用户", + "account_details": "账户详情", + "username": "用户名", + "email": "邮箱", + "role": "角色", + "last_login": "最后登录", + "storage": "存储", + "used": "已使用", + "quota": "配额", + "usage": "使用率", + "unlimited": "无限制", + "app_passwords": "应用密码", + "app_pw_desc": "为 WebDAV、CalDAV 和 CardDAV 客户端生成密码。每个密码只显示一次。", + "app_pw_label_placeholder": "标签(如 Thunderbird、macOS)", + "generate": "生成", + "generating": "生成中…", + "new_password_for": "新密码用于", + "copy_warning": "请立即复制此密码,之后将无法再次查看。", + "copy_to_clipboard": "复制到剪贴板", + "col_label": "标签", + "col_created": "创建时间", + "col_last_used": "最后使用", + "col_status": "状态", + "active": "活跃", + "revoked": "已撤销", + "revoke_title": "撤销", + "no_app_passwords": "暂无应用密码。", + "client_sessions": "客户端会话", + "client_sessions_desc": "连接 Nextcloud 兼容客户端时自动生成。", + "col_client": "客户端", + "never": "从未", + "just_now": "刚刚", + "minutes_ago": "{{n}}分钟前", + "hours_ago": "{{n}}小时前", + "days_ago": "{{n}}天前", + "edit_profile": "编辑个人资料", + "edit_oidc_managed": "要更改您的信息(姓名、名字、头像等),请前往您的身份提供商更新。变更将在您下次登录时显示。", + "username_claim_hint": "2-64 个字符,字母 / 数字 / 点 / 短横线 / 下划线。一旦选定,用户名将无法更改(DAV/NextCloud 客户端依赖它)。", + "username_already_claimed": "用户名已设置,不可更改(DAV/NextCloud 客户端依赖它)。", + "given_name": "名", + "family_name": "姓", + "notify_on_share": "当有人与我共享时通过电子邮件通知我", + "notify_on_share_hint": "取消勾选后,共享项目仍会显示在您的账户中 — 只是不会收到相关邮件通知。", + "save_profile": "保存更改", + "profile_saved": "个人资料已更新", + "profile_no_changes": "无更改可保存。", + "profile_save_failed": "保存失败", + "username_taken_error": "该用户名已被占用。", + "username_immutable_error": "您的用户名已设置,无法在此更改。如需重命名,请联系管理员。", + "change_password": "修改密码", + "current_password": "当前密码", + "new_password": "新密码", + "min_8_chars": "至少8个字符", + "confirm_password": "确认新密码", + "update_password": "更新密码", + "updating": "更新中…", + "password_updated": "密码更新成功", + "passwords_no_match": "密码不匹配", + "password_too_short": "密码至少需要8个字符", + "password_change_failed": "修改密码失败", + "error_network": "网络错误:{{message}}", + "error_label_required": "请输入标签", + "error_create_pw": "创建应用密码失败", + "confirm_revoke": "撤销应用密码\"{{label}}\"?使用此密码的客户端将停止工作。", + "error_revoke": "撤销失败", + "edit_photo": "Edit photo", + "photo_tab_url": "URL", + "photo_tab_upload": "Upload", + "photo_url_placeholder": "https://example.com/photo.jpg", + "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", + "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", + "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", + "photo_save": "Save photo", + "photo_remove": "Remove photo", + "photo_cancel": "Cancel", + "photo_save_failed": "Failed to save photo", + "photo_no_file": "Please select a file first", + "photo_managed_by_oidc": "Photo managed by your identity provider.", + "password_mismatch": "密码不匹配" + }, + "notifications": { + "file_renamed": "文件已重命名", + "file_renamed_to": "文件已重命名为\"{{name}}\"", + "folder_renamed": "文件夹已重命名", + "folder_renamed_to": "文件夹已重命名为\"{{name}}\"", + "file_uploaded": "文件已上传", + "file_deleted": "文件已移至回收站", + "folder_deleted": "文件夹已移至回收站", + "item_deleted_permanently": "项目已永久删除", + "trash_emptied": "回收站已清空", + "title": "通知", + "empty": "暂无通知", + "link_created": "链接已创建", + "share_success": "分享链接创建成功", + "upload_files_section_title": "此处不支持上传", + "upload_files_section_body": "请前往文件部分上传文件" + }, + "upload": { + "uploading": "正在上传...", + "files": "个文件", + "complete": "已上传 {{count}} / {{total}}" + }, + "storage_quota_exceeded": "存储配额已超限", + "sharedwithme": { + "pageTitle": "与我共享", + "pageDescription": "其他用户与您共享的文件和文件夹", + "emptyStateTitle": "暂无内容与您共享", + "emptyStateDesc": "其他用户与您共享的项目将显示在此处", + "loadMore": "加载更多", + "sharedBy": "共享者", + "colName": "名称", + "colType": "类型", + "colSharedBy": "共享者", + "colDate": "共享日期", + "colPermissions": "权限" + }, + "groupby": { + "none": "无", + "title": "分组方式", + "owner": "所有者", + "shareDate": "分享日期", + "type": "类型", + "type.folders": "文件夹", + "accessedAt": "访问日期", + "modifiedAt": "修改日期", + "createdAt": "创建日期", + "size": "大小", + "favoriteDate": "收藏日期", + "byFiles": "By files", + "sharedWith": "Shared with", + "justAdded": "新建", + "folders": "文件夹" + }, + "dateBucket": { + "today": "今天", + "last7days": "近7天", + "last30days": "近30天", + "unknown": "未知" + }, + "groups": { + "title": "管理群组", + "create_button": "创建群组", + "create_dialog_title": "新群组", + "edit_dialog_title": "重命名群组", + "name_label": "名称", + "name_placeholder": "engineering", + "description_label": "描述(可选)", + "members_section": "成员", + "add_member_placeholder": "添加用户或群组…", + "no_members": "暂无成员。", + "remove_member": "移除", + "delete_group": "删除群组", + "delete_confirm": "删除群组 \"{name}\"?引用此群组的所有权限将被撤销。", + "empty_state": "暂无群组。", + "load_more": "加载更多", + "back_to_list": "返回", + "loading": "加载中…", + "virtual_badge": "系统", + "member_count_zero": "无成员", + "member_count_one": "1 个成员", + "member_count_other": "{count} 个成员", + "delete_confirm_label": "请输入群组名称以确认:", + "delete_confirm_mismatch": "请准确输入群组名称以确认。", + "virtual_internal_name": "内部", + "members_loading": "正在加载成员…", + "members_empty": "无成员", + "virtual_internal_explanation": "本服务器上的所有内部用户", + "create": "创建群组", + "empty": "暂无群组。", + "members": "成员" + }, + "myshares": { + "copyLink": "复制链接", + "deleteLink": "删除链接", + "notifyByEmail": "通过邮件通知", + "notifyFailed": "无法发送通知。", + "notifyGroupMembers": "通知群组成员", + "notifyRateLimited": "对此收件人的通知过多 — 请稍后重试。", + "removeAccess": "移除访问权限", + "resendInvitation": "重新发送邀请邮件", + "publicLinks": "Public links" + }, + "sort": { + "asc": "ascending", + "desc": "descending" + }, + "notif": { + "errorTitle": "Error", + "searchError": "Error performing search", + "cleanupCompleted": "Cleanup completed", + "cleanupCompletedBody": "Recent files history has been cleared", + "batchCopy": "Batch copy", + "batchCopyBody": "{{success}} copied, {{errors}} failed", + "itemsCopied": "Items copied", + "itemsCopiedBody": "{{count}} items copied successfully", + "batchMove": "Batch move", + "batchMoveBody": "{{success}} moved, {{errors}} failed", + "itemsMoved": "Items moved", + "itemsMovedBody": "{{count}} items moved successfully", + "batchDelete": "Batch delete", + "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", + "movedToTrash": "Moved to trash", + "movedToTrashBody": "{{count}} items moved to trash", + "trashItemsError": "Could not move items to trash", + "preparingDownload": "Preparing download", + "preparingDownloadBody": "Preparing your download…", + "downloadItemsError": "Could not download selected items", + "favoritesAddError": "Could not add items to favorites", + "invalidEmail": "Please enter a valid email address", + "notificationSendError": "Could not send notification", + "folderCreated": "Folder created", + "folderCreatedBody": "\"{{name}}\" created successfully", + "fileMoved": "File moved", + "fileMovedBody": "File moved successfully", + "fileMoveError": "Error moving the file: {{error}}", + "fileMoveErrorGeneric": "Error moving the file", + "folderMoved": "Folder moved", + "folderMovedBody": "Folder moved successfully", + "folderMoveError": "Error moving the folder: {{error}}", + "folderMoveErrorGeneric": "Error moving the folder", + "fileCopied": "File copied", + "fileCopiedBody": "File copied successfully", + "fileCopyError": "Error copying the file: {{error}}", + "fileCopyErrorGeneric": "Error copying the file", + "folderRenamed": "Folder renamed", + "folderRenamedBody": "Folder renamed to \"{{name}}\"", + "fileTrashed": "File moved to trash", + "fileTrashedBody": "\"{{name}}\" moved to trash", + "fileDeleted": "File deleted", + "fileDeletedBody": "\"{{name}}\" deleted successfully", + "fileDeleteError": "Error deleting the file", + "folderTrashed": "Folder moved to trash", + "folderTrashedBody": "\"{{name}}\" moved to trash", + "folderDeleted": "Folder deleted", + "folderDeletedBody": "\"{{name}}\" deleted successfully", + "folderDeleteError": "Error deleting the folder", + "itemRestored": "Item restored", + "itemRestoredBody": "Item restored successfully", + "itemRestoreError": "Error restoring the item", + "itemDeleted": "Item deleted", + "itemDeletedBody": "Item permanently deleted", + "itemDeleteError": "Error deleting the item", + "trashEmptied": "Trash emptied", + "trashEmptiedBody": "The trash has been emptied successfully", + "trashEmptyError": "Error emptying the trash", + "cacheCleared": "Cache cleared", + "cacheClearedBody": "Search cache cleared successfully", + "cacheClearError": "Error clearing search cache", + "wopiOpenError": "Could not open the document editor.", + "linkCopied": "Link copied", + "linkCopiedBody": "Link copied to clipboard", + "linkCopyError": "Could not copy link", + "notificationSent": "Notification sent", + "notificationSentBody": "Notification sent to {{email}}" + }, + "category": { + "audio": "音频", + "code": "代码", + "text": "文本" + }, + "common": { + "add": "添加", + "cancel": "取消", + "clear": "Clear", + "close": "关闭", + "confirm": "确认", + "copy": "复制", + "create": "创建", + "delete": "删除", + "download": "下载", + "load_more": "加载更多", + "loading": "加载中…", + "next": "下一首", + "no": "无", + "previous": "上一首", + "remove": "Remove", + "rename": "重命名", + "save": "保存", + "search": "搜索", + "yes": "有" + }, + "device": { + "continue": "继续", + "unknown": "未知" + }, + "expiryBucket": { + "expired": "已过期", + "noExpiry": "无过期", + "today": "今天", + "tomorrow": "明天" + }, + "nextcloud": { + "error_title": "错误", + "sign_in_with": "使用 {{provider}} 登录" + }, + "search": { + "size_label": "大小", + "title": "搜索", + "type": { + "audio": "音频" + }, + "type_label": "类型" + }, + "sizeBucket": { + "folders": "文件夹" + }, + "view": { + "grid": "网格视图", + "list": "列表视图" + } } diff --git a/static/locales b/static/locales new file mode 120000 index 00000000..1e65b455 --- /dev/null +++ b/static/locales @@ -0,0 +1 @@ +../frontend/static/locales \ No newline at end of file diff --git a/static/locales/ar.json b/static/locales/ar.json deleted file mode 100644 index a1884660..00000000 --- a/static/locales/ar.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "لم يعد رابط تسجيل الدخول صالحًا", - "expired_body": "ربما انتهت صلاحية الرابط أو تم استخدامه بالفعل. يمكننا إرسال رابط جديد لك — سيصل إلى صندوق الوارد خلال ثوانٍ.", - "resend_to": "أرسل رابطًا جديدًا إلى {{email}}", - "generic_unavailable": "لم يعد رابط تسجيل الدخول صالحًا. ربما تم استخدامه بالفعل أو انتهت صلاحيته. اطلب رابطًا جديدًا من صفحة تسجيل الدخول.", - "service_unavailable": "تسجيل الدخول عبر الرابط السحري غير مفعّل على هذا الخادم.", - "internal_error": "حدث خطأ أثناء تسجيل الدخول. يُرجى المحاولة مرة أخرى.", - "resend_failure": "حدث خطأ أثناء إرسال الرابط. يُرجى المحاولة مرة أخرى.", - "cross_browser_title": "هل تريد متابعة تسجيل الدخول على هذا الجهاز؟", - "cross_browser_body": "لقد فتحت رابط تسجيل الدخول في متصفح أو جهاز مختلف عن الجهاز الذي طلبته منه.", - "cross_browser_warning": "إذا كنت قد طلبت هذا الرابط، فمن الآمن المتابعة. إذا لم تطلبه، أغلق هذه الصفحة — النقر على «متابعة» سيُسجّل دخول شخص آخر إلى حسابك.", - "cross_browser_continue": "متابعة وتسجيل الدخول", - "resend_confirmation_title": "تحقق من صندوق الوارد", - "resend_confirmation_body": "إذا كان رابط تسجيل الدخول ينتمي إلى حساب نشط، فقد تم للتو إرسال رابط جديد. يُرجى التحقق من صندوق الوارد.", - "return_link": "العودة إلى OxiCloud" - }, - "email": { - "invitation": { - "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", - "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتحه بالنقر على الرابط أدناه:\n{{link}}\n\nيعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_hours}} ساعة.\nإذا لم تكن تتوقع هذه الدعوة، يمكنك تجاهل هذه الرسالة.\n\n— OxiCloud" - }, - "login": { - "subject": "تسجيل الدخول إلى OxiCloud", - "body": "مرحبًا،\n\nاستخدم الرابط أدناه لتسجيل الدخول إلى OxiCloud. يعمل الرابط مرة واحدة وتنتهي صلاحيته خلال {{ttl_minutes}} دقيقة. افتحه على الجهاز نفسه الذي طلبت منه الرابط.\n\n{{link}}\n\nإذا لم تطلب رابط تسجيل الدخول هذا، يمكنك تجاهل هذه الرسالة — لا حاجة لأي إجراء إضافي.\n\n— OxiCloud" - }, - "kind_file": "ملف", - "kind_folder": "مجلد", - "english_fallback_divider": "--- النسخة الإنجليزية أدناه ---" - } - }, - "notification": { - "share": { - "subject": "شارك {{inviter}} معك {{kind}} على OxiCloud", - "body": "شارك {{inviter_full}} معك {{kind}} على OxiCloud.\n\nافتح OxiCloud لعرض مشاركتك الجديدة:\n{{login_link}}\n\nقد تكون لديك مشاركات جديدة أخرى من {{inviter}} — سجّل الدخول لرؤية جميع العناصر المشاركة معك.\n\n— OxiCloud\n\nأنت تتلقى هذه الرسالة لأن لديك حسابًا في OxiCloud وتفضيل إشعارات المشاركة مُفعّل. يمكنك تعطيله من ملفك الشخصي (راسلني عندما يشاركني شخص ما)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "نظام تخزين سحابي بسيط" - }, - "nav": { - "files": "الملفات", - "shared": "مشاركاتي", - "recent": "الأخيرة", - "favorites": "المفضلة", - "photos": "الصور", - "music": "الموسيقى", - "trash": "سلة المهملات", - "sharedwithme": "مشتركة معي" - }, - "photos": { - "empty_state": "لا توجد صور بعد", - "empty_hint": "ارفع صوراً أو مقاطع فيديو لعرضها هنا", - "items_selected": "محدد", - "view_daily": "يوم", - "view_monthly": "شهر", - "view_yearly": "سنة" - }, - "music": { - "create_playlist": "إنشاء قائمة تشغيل", - "playlists": "قوائم التشغيل", - "no_playlists": "لا توجد قوائم تشغيل بعد", - "select_playlist": "اختر قائمة تشغيل", - "select_hint": "اختر قائمة تشغيل من الشريط الجانبي أو أنشئ واحدة جديدة", - "add_tracks": "إضافة مقاطع", - "no_tracks": "لا توجد مقاطع في هذه القائمة", - "unknown_artist": "فنان غير معروف", - "unknown_title": "غير معروف", - "confirm_delete": "هل تريد حذف هذه القائمة؟", - "playlist_name": "اسم القائمة", - "create": "إنشاء", - "delete": "حذف", - "share": "مشاركة", - "edit": "تعديل", - "play_all": "تشغيل الكل", - "shuffle": "عشوائي", - "repeat": "تكرار", - "repeat_one": "تكرار واحد", - "queue": "قائمة الانتظار", - "queue_empty": "قائمة الانتظار فارغة", - "not_playing": "لا يتم التشغيل", - "play": "تشغيل", - "pause": "إيقاف مؤقت", - "previous": "السابق", - "next": "التالي", - "volume": "مستوى الصوت", - "mute": "كتم", - "unmute": "إلغاء الكتم", - "title": "العنوان", - "artist": "الفنان", - "album": "الألبوم", - "tracks": "مقاطع", - "add": "إضافة", - "added": "تمت الإضافة!", - "added_to_playlist": "تمت إضافته إلى القائمة", - "add_to_playlist": "إضافة إلى القائمة", - "load_error": "خطأ في تحميل القوائم", - "add_error": "تعذر إضافة المقاطع", - "no_playlists_yet": "لا توجد قوائم بعد. أنشئ واحدة أولاً!", - "selected_files": "محدد:", - "error": "خطأ", - "search_audio": "البحث عن ملفات صوتية…", - "no_audio_files": "لم يتم العثور على ملفات صوتية", - "selected": "محدد", - "loading": "جارٍ التحميل…", - "search_error": "تعذر تحميل الملفات الصوتية", - "adding": "جارٍ الإضافة…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "البحث في الملفات...", - "new_folder": "مجلد جديد", - "upload": "رفع", - "upload_files": "رفع ملفات", - "upload_folder": "رفع مجلد", - "upload.uploading": "جارٍ الرفع...", - "upload.complete": "{count} / {total} تم رفعها", - "upload.files": "ملفات", - "rename": "إعادة التسمية", - "move": "نقل إلى...", - "move_to": "نقل إلى", - "delete": "حذف", - "download": "تحميل", - "view": "عرض", - "cancel": "إلغاء", - "confirm": "تأكيد", - "share": "مشاركة", - "favorite": "إضافة للمفضلة", - "unfavorite": "إزالة من المفضلة", - "copy": "نسخ", - "notify": "إشعار", - "send": "إرسال", - "clear_recent": "مسح الأخيرة", - "logout": "تسجيل الخروج", - "create": "إنشاء", - "search_btn": "بحث", - "close": "إغلاق", - "delete_permanently": "حذف نهائياً", - "empty_trash": "تفريغ سلة المهملات", - "open_parent_folder": "الانتقال إلى المجلد الأصلي", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "المظهر", - "about": "حول OxiCloud", - "about_description": "منصة تخزين سحابي مبنية بـ Rust و Clean Architecture. سريعة وآمنة وخاصة.", - "admin_panel": "لوحة الإدارة", - "profile": "ملفي الشخصي", - "role_user": "مستخدم", - "theme": { - "light": "فاتح", - "dark": "داكن", - "auto": "مثل النظام" - }, - "manage_groups": "إدارة المجموعات" - }, - "share": { - "dialogTitle": "رابط المشاركة", - "linkLabel": "رابط المشاركة:", - "copyLink": "نسخ", - "permissions": "الصلاحيات:", - "permissionRead": "قراءة", - "permissionWrite": "كتابة", - "permissionReshare": "إعادة مشاركة", - "password": "حماية بكلمة مرور:", - "generatePassword": "توليد", - "expiration": "تاريخ انتهاء الصلاحية:", - "update": "تحديث المشاركة", - "remove": "إزالة المشاركة", - "notifyTitle": "إرسال إشعار", - "notifyEmailLabel": "عنوان البريد الإلكتروني:", - "notifyMessageLabel": "رسالة (اختياري):", - "notifySend": "إرسال الإشعار", - "shareWithOthers": "مشاركة مع آخرين", - "sharePublicly": "مشاركة عامة", - "shareSettings": "إعدادات المشاركة", - "shareCopied": "تم نسخ الرابط إلى الحافظة", - "shareCreated": "تم إنشاء رابط المشاركة بنجاح", - "shareUpdated": "تم تحديث إعدادات المشاركة بنجاح", - "shareRemoved": "تمت إزالة المشاركة بنجاح", - "inviteByEmail": "دعوة عبر البريد الإلكتروني — ستُرسل الدعوة", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "رابط المشاركة", - "share_linkLabel": "رابط المشاركة:", - "share_copyLink": "نسخ", - "share_permissions": "الصلاحيات:", - "share_permissionRead": "قراءة", - "share_permissionWrite": "كتابة", - "share_permissionReshare": "إعادة مشاركة", - "share_password": "حماية بكلمة مرور:", - "share_generatePassword": "توليد", - "share_expiration": "تاريخ انتهاء الصلاحية:", - "share_update": "تحديث المشاركة", - "share_remove": "إزالة المشاركة", - "share_notifyTitle": "إرسال إشعار", - "share_notifyEmailLabel": "عنوان البريد الإلكتروني:", - "share_notifyMessageLabel": "رسالة (اختياري):", - "share_notifySend": "إرسال الإشعار", - "shared": { - "backToFiles": "العودة إلى الملفات", - "pageTitle": "الموارد المشتركة", - "pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", - "filterType": "النوع:", - "filterAll": "الكل", - "filterFiles": "ملفات", - "filterFolders": "مجلدات", - "sortBy": "ترتيب حسب:", - "sortByName": "الاسم", - "sortByDate": "تاريخ المشاركة", - "sortByExpiration": "انتهاء الصلاحية", - "search": "بحث", - "colName": "الاسم", - "colType": "النوع", - "colDateShared": "تاريخ المشاركة", - "colExpiration": "انتهاء الصلاحية", - "colPermissions": "الصلاحيات", - "colPassword": "كلمة المرور", - "colActions": "الإجراءات", - "emptyStateTitle": "لا توجد موارد مشتركة بعد", - "emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", - "goToFiles": "الذهاب إلى الملفات", - "typeFile": "ملف", - "typeFolder": "مجلد", - "noExpiration": "بدون انتهاء صلاحية", - "hasPassword": "نعم", - "noPassword": "لا", - "editShare": "تعديل المشاركة", - "notifyShare": "إشعار شخص ما", - "copyLink": "نسخ الرابط", - "removeShare": "إزالة المشاركة", - "linkCopied": "تم نسخ الرابط إلى الحافظة!", - "linkCopyFailed": "فشل نسخ الرابط", - "itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", - "itemRemoved": "تمت إزالة المشاركة بنجاح", - "invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", - "notificationSent": "تم إرسال الإشعار بنجاح", - "notificationFailed": "فشل إرسال الإشعار", - "shared_backToFiles": "العودة إلى الملفات", - "shared_pageTitle": "الموارد المشتركة", - "shared_pageDescription": "إدارة ملفاتك ومجلداتك المشتركة", - "shared_filterType": "النوع:", - "shared_filterAll": "الكل", - "shared_filterFiles": "ملفات", - "shared_filterFolders": "مجلدات", - "shared_sortBy": "ترتيب حسب:", - "shared_sortByName": "الاسم", - "shared_sortByDate": "تاريخ المشاركة", - "shared_sortByExpiration": "انتهاء الصلاحية", - "shared_search": "بحث", - "shared_colName": "الاسم", - "shared_colType": "النوع", - "shared_colDateShared": "تاريخ المشاركة", - "shared_colExpiration": "انتهاء الصلاحية", - "shared_colPermissions": "الصلاحيات", - "shared_colPassword": "كلمة المرور", - "shared_colActions": "الإجراءات", - "shared_emptyStateTitle": "لا توجد موارد مشتركة بعد", - "shared_emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا", - "shared_goToFiles": "الذهاب إلى الملفات", - "shared_typeFile": "ملف", - "shared_typeFolder": "مجلد", - "shared_noExpiration": "بدون انتهاء صلاحية", - "shared_hasPassword": "نعم", - "shared_noPassword": "لا", - "shared_editShare": "تعديل المشاركة", - "shared_notifyShare": "إشعار شخص ما", - "shared_copyLink": "نسخ الرابط", - "shared_removeShare": "إزالة المشاركة", - "shared_linkCopied": "تم نسخ الرابط إلى الحافظة!", - "shared_linkCopyFailed": "فشل نسخ الرابط", - "shared_itemUpdated": "تم تحديث إعدادات المشاركة بنجاح", - "shared_itemRemoved": "تمت إزالة المشاركة بنجاح", - "shared_invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح", - "shared_notificationSent": "تم إرسال الإشعار بنجاح", - "shared_notificationFailed": "فشل إرسال الإشعار" - }, - "files": { - "name": "الاسم", - "type": "النوع", - "size": "الحجم", - "modified": "تاريخ التعديل", - "no_files": "لا توجد ملفات في هذا المجلد", - "empty_hint": "ارفع ملفات أو أنشئ مجلدات للبدء", - "loading": "جارٍ تحميل الملفات…", - "view_grid": "عرض شبكي", - "view_list": "عرض قائمة", - "file_types": { - "document": "مستند", - "image": "صورة", - "video": "فيديو", - "audio": "صوت", - "pdf": "PDF", - "text": "نص", - "folder": "مجلد", - "spreadsheet": "جدول بيانات", - "presentation": "عرض تقديمي", - "archive": "أرشيف", - "installer": "مثبّت", - "code": "كود" - }, - "owner": "المالك" - }, - "dialogs": { - "rename_folder": "إعادة تسمية المجلد", - "rename_file": "إعادة تسمية الملف", - "new_name": "الاسم الجديد", - "new_folder_title": "مجلد جديد", - "folder_name": "اسم المجلد", - "folder_placeholder": "مجلدي", - "rename_title": "إعادة التسمية", - "move_file": "نقل الملف", - "move_folder": "نقل المجلد", - "select_destination": "اختر المجلد الوجهة:", - "select_this_folder": "اختيار هذا المجلد", - "go_to_parent": ".. (المجلد الأعلى)", - "no_subfolders": "لا توجد مجلدات فرعية", - "root": "الجذر", - "delete_confirmation": "هل أنت متأكد أنك تريد حذف", - "and_contents": "وجميع محتوياته", - "no_undo": "لا يمكن التراجع عن هذا الإجراء", - "confirm_title": "تأكيد الإجراء", - "confirm_delete": "نقل إلى سلة المهملات", - "confirm_delete_file": "هل أنت متأكد أنك تريد نقل الملف \"{{name}}\" إلى سلة المهملات؟", - "confirm_delete_folder": "هل أنت متأكد أنك تريد نقل المجلد \"{{name}}\" وجميع محتوياته إلى سلة المهملات؟", - "confirm_permanent_delete": "حذف نهائي", - "confirm_permanent_delete_msg": "هل أنت متأكد أنك تريد حذف هذا العنصر نهائياً؟ لا يمكن التراجع عن هذا الإجراء.", - "confirm_empty_trash": "تفريغ سلة المهملات", - "confirm_delete_share": "حذف رابط المشاركة", - "confirm_delete_share_msg": "هل أنت متأكد أنك تريد حذف رابط المشاركة هذا؟", - "share_file": "مشاركة الملف", - "share_folder": "مشاركة المجلد", - "existing_shares": "المشاركات الحالية", - "share_options": "خيارات المشاركة", - "password": "كلمة المرور", - "expiration": "انتهاء الصلاحية", - "permissions": "الصلاحيات", - "generated_link": "الرابط المُنشأ", - "notify": "إرسال إشعار", - "recipient": "المستلم", - "message": "الرسالة", - "move_to_home": "نقل إلى المجلد الرئيسي" - }, - "dropzone": { - "drag_files": "اسحب الملفات هنا أو انقر للاختيار", - "drop_files": "أسقط الملفات للرفع" - }, - "permissions": { - "read": "قراءة", - "write": "كتابة", - "reshare": "إعادة مشاركة" - }, - "errors": { - "file_not_found": "الملف غير موجود", - "folder_not_found": "المجلد غير موجود", - "delete_error": "خطأ في الحذف", - "upload_error": "خطأ في رفع الملف", - "rename_error": "خطأ في إعادة التسمية", - "move_error": "خطأ في النقل", - "empty_name": "لا يمكن أن يكون الاسم فارغاً", - "name_exists": "ملف أو مجلد بهذا الاسم موجود بالفعل", - "generic_error": "حدث خطأ", - "group_name_invalid": "يجب أن يتطابق اسم المجموعة مع صيغة بادئة البريد الإلكتروني (حروف، أرقام، نقطة، شرطة، شرطة سفلية؛ 1–64 حرفًا).", - "group_cycle": "سينشئ هذا العضو مرجعًا دائريًا بين المجموعات.", - "group_depth_exceeded": "تتجاوز عمق التعشيش الحد الأقصى المسموح (8).", - "group_virtual_immutable": "مجموعة «Internal» تدار من قبل النظام ولا يمكن تعديلها.", - "group_not_found": "المجموعة غير موجودة.", - "group_name_taken": "توجد بالفعل مجموعة بهذا الاسم." - }, - "breadcrumb": { - "home": "الرئيسية" - }, - "trash": { - "empty_trash": "تفريغ سلة المهملات", - "empty_state": "سلة المهملات فارغة", - "original_location": "الموقع الأصلي", - "deleted_date": "تاريخ الحذف", - "remaining": "المتبقي", - "actions": "الإجراءات", - "restore": "استعادة", - "delete_permanently": "حذف نهائياً", - "empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً.", - "groupby": { - "remaining_days": "الأيام المتبقية", - "trashed_time": "وقت الحذف" - } - }, - "daysRemaining": { - "expired": "منتهية الصلاحية", - "today": "اليوم", - "tomorrow": "غدًا", - "inDays": "{{count}} يوم" - }, - "expiryChip": { - "never": "لا تنتهي الصلاحية", - "expired": "منتهية الصلاحية", - "today": "تنتهي الصلاحية اليوم", - "tomorrow": "تنتهي الصلاحية غدًا", - "inDays": "تنتهي الصلاحية خلال {{count}} يوم", - "onDate": "تنتهي الصلاحية في {{date}}" - }, - "auth": { - "login_title": "تسجيل الدخول", - "username": "اسم المستخدم", - "username_placeholder": "أدخل اسم المستخدم", - "login_identifier": "اسم المستخدم أو البريد الإلكتروني", - "login_identifier_placeholder": "أدخل اسم المستخدم أو البريد الإلكتروني", - "password": "كلمة المرور", - "password_placeholder": "أدخل كلمة المرور", - "login_button": "تسجيل الدخول", - "no_account": "ليس لديك حساب؟", - "register": "إنشاء حساب", - "admin_setup": "أول مرة؟", - "setup": "إعداد المسؤول", - "register_title": "إنشاء حساب", - "email": "البريد الإلكتروني", - "email_placeholder": "أدخل بريدك الإلكتروني", - "confirm_password": "تأكيد كلمة المرور", - "confirm_password_placeholder": "أكد كلمة المرور", - "register_button": "إنشاء حساب", - "have_account": "لديك حساب بالفعل؟", - "login": "تسجيل الدخول", - "setup_title": "الإعداد الأولي", - "setup_step1": "المسؤول", - "setup_step2": "النظام", - "setup_step3": "مكتمل", - "admin_username": "اسم مستخدم المسؤول", - "admin_email": "بريد المسؤول الإلكتروني", - "admin_password": "كلمة مرور المسؤول", - "create_admin": "إنشاء حساب المسؤول", - "back_to_login": "تم الإعداد مسبقاً؟", - "admin_success": "تم إنشاء حساب المسؤول بنجاح! يمكنك الآن تسجيل الدخول.", - "account_success": "تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول.", - "passwords_mismatch": "كلمات المرور غير متطابقة", - "admin_create_error": "خطأ في إنشاء حساب المسؤول", - "or": "أو", - "sso_login": "تسجيل الدخول عبر SSO", - "sso_login_provider": "تسجيل الدخول عبر {{provider}}", - "magicLinkHint": "ليس لديك كلمة مرور؟ أدخل بريدك الإلكتروني وسنرسل لك رابط تسجيل دخول لمرة واحدة.", - "magicLinkEmailLabel": "عنوان البريد الإلكتروني", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "إرسال رابط تسجيل الدخول", - "magicLinkSent": "إذا كان هناك حساب لهذا البريد الإلكتروني، فسيتم إرسال رابط تسجيل الدخول. تحقق من صندوق الوارد.", - "magicLinkUnavailable": "تسجيل الدخول عبر البريد الإلكتروني غير متاح على هذا الخادم.", - "magicLinkNetworkError": "تعذر الوصول إلى الخادم: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "التخزين", - "calculating": "جارٍ الحساب...", - "used": "{{percentage}}% مستخدم ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "لا يمكن معاينة هذا النوع من الملفات.", - "download_file": "تحميل الملف", - "zoom_in": "تكبير", - "zoom_out": "تصغير", - "zoom_reset": "إعادة تعيين التكبير" - }, - "language_selector": { - "title": "!مرحباً", - "subtitle": "اختر لغتك للمتابعة", - "continue": "متابعة", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "لا توجد مفضلات بعد", - "empty_hint": "ضع نجمة على الملفات أو المجلدات لإضافتها إلى المفضلة", - "add": "إضافة للمفضلة", - "remove": "إزالة من المفضلة", - "added_title": "أُضيف للمفضلة", - "added_msg": "أُضيف للمفضلة", - "removed_title": "أُزيل من المفضلة", - "removed_msg": "أُزيل من المفضلة" - }, - "recent": { - "title": "الأخيرة", - "clear": "مسح الأخيرة", - "accessed": "تم الوصول", - "empty_state": "لا توجد ملفات حديثة", - "empty_hint": "الملفات التي تفتحها ستظهر هنا", - "loadMore": "تحميل المزيد" - }, - "notifications": { - "file_renamed": "تمت إعادة تسمية الملف", - "file_renamed_to": "تمت إعادة تسمية الملف إلى \"{{name}}\"", - "folder_renamed": "تمت إعادة تسمية المجلد", - "folder_renamed_to": "تمت إعادة تسمية المجلد إلى \"{{name}}\"", - "file_uploaded": "تم رفع الملف", - "file_deleted": "تم نقل الملف إلى سلة المهملات", - "folder_deleted": "تم نقل المجلد إلى سلة المهملات", - "item_deleted_permanently": "تم حذف العنصر نهائياً", - "trash_emptied": "تم تفريغ سلة المهملات بنجاح", - "title": "الإشعارات", - "empty": "لا توجد إشعارات", - "link_created": "تم إنشاء الرابط", - "share_success": "تم إنشاء رابط المشاركة بنجاح", - "upload_files_section_title": "التحميل غير متاح هنا", - "upload_files_section_body": "انتقل إلى قسم الملفات لتحميل الملفات" - }, - "batch": { - "one_selected": "عنصر واحد محدد", - "n_selected": "{{count}} عناصر محددة", - "confirm_delete": "هل أنت متأكد أنك تريد نقل {{count}} عنصر إلى سلة المهملات؟", - "move_title": "نقل {{count}} عنصر", - "add_favorites": "إضافة للمفضلة", - "move_copy": "نقل أو نسخ" - }, - "admin": { - "page_title": "لوحة الإدارة", - "back_to_app": "العودة إلى OxiCloud", - "loading": "جارٍ التحميل…", - "access_denied": "الوصول مرفوض", - "access_denied_desc": "صلاحيات المسؤول مطلوبة.", - "sign_in": "تسجيل الدخول", - "tab_dashboard": "لوحة المعلومات", - "tab_users": "المستخدمون", - "tab_oidc": "SSO / OIDC", - "total_users": "إجمالي المستخدمين", - "active_users": "المستخدمون النشطون", - "admins": "المسؤولون", - "version": "الإصدار", - "storage_overview": "نظرة عامة على التخزين", - "used": "مستخدم", - "total_quota": "الحصة الإجمالية", - "usage_pct": "نسبة الاستخدام", - "users_over_80": "مستخدمون >80%", - "users_over_quota": "مستخدمون تجاوزوا الحصة", - "system": "النظام", - "auth_label": "المصادقة", - "oidc_label": "OIDC", - "quotas_label": "الحصص", - "enabled": "مفعّل", - "disabled": "معطّل", - "active": "نشط", - "off": "متوقف", - "allow_registration": "السماح بالتسجيل العام", - "registration_warning": "التسجيل العام معطّل. فقط المسؤولون يمكنهم إنشاء مستخدمين.", - "user_management": "إدارة المستخدمين", - "create_user": "إنشاء مستخدم", - "col_user": "المستخدم", - "col_role": "الدور", - "col_auth": "المصادقة", - "col_status": "الحالة", - "col_storage": "التخزين", - "col_last_login": "آخر دخول", - "col_actions": "الإجراءات", - "loading_users": "جارٍ تحميل المستخدمين…", - "failed_load_users": "فشل التحميل", - "no_users_found": "لم يتم العثور على مستخدمين", - "showing_users": "عرض {{from}}-{{to}} من {{total}}", - "prev": "السابق", - "next": "التالي", - "inactive": "غير نشط", - "you_badge": "(أنت)", - "local": "محلي", - "never": "أبداً", - "just_now": "الآن", - "minutes_ago": "منذ {{n}} دقيقة", - "hours_ago": "منذ {{n}} ساعة", - "days_ago": "منذ {{n}} يوم", - "edit_quota_title": "تعديل الحصة", - "reset_password_title": "إعادة تعيين كلمة المرور", - "toggle_role_title": "تبديل الدور", - "deactivate_title": "تعطيل", - "activate_title": "تفعيل", - "delete_title": "حذف", - "sso_title": "تسجيل الدخول الموحد (OIDC / SSO)", - "enable_sso": "تفعيل مصادقة SSO", - "provider_name": "اسم الموفر", - "issuer_url": "عنوان المُصدر", - "issuer_url_hint": "عنوان مُصدر OpenID Connect", - "auto_discover": "اكتشاف تلقائي", - "discovering": "جارٍ الاكتشاف…", - "client_id": "معرّف العميل", - "client_secret": "سر العميل", - "client_secret_placeholder": "اتركه فارغاً للاحتفاظ بالقيمة", - "secret_configured": "سر العميل مُهيأ بالفعل", - "callback_url": "عنوان الاستدعاء", - "callback_url_hint": "(سجّل في IdP)", - "advanced_settings": "إعدادات متقدمة", - "scopes": "النطاقات", - "auto_provision": "إنشاء تلقائي عند أول دخول", - "admin_groups": "مجموعات المسؤولين", - "admin_groups_hint": "أسماء مجموعات OIDC مفصولة بفواصل", - "disable_password": "تعطيل الدخول بكلمة المرور (OIDC فقط)", - "password_warning": "سيمنع جميع عمليات الدخول بالمرور!", - "test_btn": "اختبار", - "save_btn": "حفظ", - "saving": "جارٍ الحفظ…", - "settings_saved": "تم الحفظ — OIDC الآن {{status}}", - "quota_modal_title": "تحديث حصة التخزين", - "quota_user_label": "المستخدم:", - "new_quota": "حصة جديدة", - "quota_unlimited_hint": "0 لغير محدود", - "cancel": "إلغاء", - "create_user_title": "إنشاء مستخدم جديد", - "username_label": "اسم المستخدم", - "username_placeholder": "اسم_المستخدم", - "username_hint": "3–32 حرفاً", - "password_label": "كلمة المرور", - "password_placeholder": "8 أحرف على الأقل", - "email_label": "البريد", - "email_optional": "(اختياري)", - "email_placeholder": "user@example.com (يُنشأ تلقائياً)", - "role_label": "الدور", - "role_user": "مستخدم", - "role_admin": "مسؤول", - "quota_label": "الحصة", - "creating": "جارٍ الإنشاء…", - "reset_pw_title": "إعادة تعيين كلمة المرور", - "new_password_label": "كلمة مرور جديدة", - "resetting": "جارٍ إعادة التعيين…", - "reset_btn": "إعادة تعيين", - "confirm_role_change": "تغيير الدور إلى {{role}}؟", - "confirm_deactivate": "هل أنت متأكد من التعطيل؟", - "confirm_activate": "هل أنت متأكد من التفعيل؟", - "confirm_delete_user": "حذف المستخدم \"{{name}}\"؟ لا يمكن التراجع!", - "confirm_action": "تأكيد الإجراء", - "confirm_yes": "تأكيد", - "confirm_no": "إلغاء", - "error_username_short": "الاسم 3 أحرف على الأقل", - "error_password_short": "كلمة المرور 8 أحرف على الأقل", - "error_generic": "فشل", - "error_network": "خطأ في الشبكة: {{message}}", - "error_create_user": "فشل إنشاء المستخدم", - "tab_storage": "التخزين", - "storage_title": "إعداد التخزين", - "storage_current_backend": "الواجهة الخلفية الحالية", - "storage_total_blobs": "إجمالي الكتل", - "storage_total_size": "الحجم الإجمالي", - "storage_dedup_ratio": "نسبة إزالة التكرار", - "storage_backend": "الواجهة الخلفية", - "storage_local": "محلي", - "storage_s3": "متوافق مع S3", - "storage_provider_preset": "إعداد مسبق للمزود", - "storage_preset_custom": "مخصص", - "storage_endpoint_url": "رابط نقطة النهاية", - "storage_endpoint_hint": "اتركه فارغاً لـ AWS S3", - "storage_bucket": "الحاوية", - "storage_region": "المنطقة", - "storage_access_key": "مفتاح الوصول", - "storage_secret_key": "المفتاح السري", - "storage_secret_configured": "تم إعداد المفتاح", - "storage_key_placeholder": "أدخل مفتاحاً جديداً", - "storage_path_style": "فرض أسلوب المسار", - "storage_path_style_hint": "مطلوب لـ MinIO وبعض الخدمات المتوافقة مع S3", - "storage_test_connection": "اختبار الاتصال", - "storage_test_success": "نجح الاتصال", - "storage_test_failure": "فشل الاتصال", - "storage_save": "حفظ الإعداد", - "storage_saved": "تم حفظ الإعداد", - "storage_migration": "ترحيل البيانات", - "storage_migration_coming_soon": "أدوات الترحيل قريباً", - "migration_status_label": "حالة الترحيل", - "migration_start": "بدء الترحيل", - "migration_pause": "إيقاف مؤقت", - "migration_resume": "استئناف", - "migration_verify": "التحقق", - "migration_complete": "إكمال", - "migration_started": "بدأ الترحيل", - "migration_paused_msg": "الترحيل متوقف مؤقتاً", - "migration_resumed_msg": "استُؤنف الترحيل", - "migration_completed_msg": "اكتمل الترحيل بنجاح", - "migration_verifying": "جارٍ التحقق...", - "migration_verify_passed": "اجتاز التحقق", - "migration_verify_failed": "فشل التحقق", - "migration_failed_blobs": "كتل فاشلة", - "testing": "جارٍ الاختبار...", - "smtp_disabled": "معطّل (المضيف غير مضبوط)", - "smtp_enabled": "مفعّل", - "smtp_enabled_label": "الحالة", - "smtp_intro": "يتم تكوين SMTP حصريًا عبر متغيرات البيئة (OXICLOUD_SMTP_*). تُقرأ القيم أدناه من الخادم قيد التشغيل — لتغييرها، عدّل البيئة وأعد تشغيل OxiCloud.", - "smtp_not_configured": "SMTP غير مكوَّن على هذا الخادم.", - "smtp_send_failed": "فشل الإرسال.", - "smtp_send_test": "إرسال بريد اختباري", - "smtp_sending": "جارٍ الإرسال…", - "smtp_sent": "تم إرسال البريد الاختباري.", - "smtp_server_code": "رد الخادم", - "smtp_test_intro": "يرسل رسالة تشخيصية محددة مسبقًا إلى المستلم أدناه ويُبلِّغ عن استجابة خادم SMTP لتتمكن من مطابقتها مع سجلات المرحّل الخاص بك.", - "smtp_test_missing_to": "أدخل عنوان المستلم.", - "smtp_test_title": "إرسال بريد اختباري", - "smtp_test_to": "عنوان المستلم", - "smtp_title": "البريد الصادر (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "الملف الشخصي", - "back_to_app": "العودة إلى OxiCloud", - "loading": "جارٍ التحميل…", - "not_authenticated": "غير مُصادق", - "not_authenticated_desc": "سجّل الدخول لعرض ملفك الشخصي.", - "sign_in": "تسجيل الدخول", - "role_admin": "مسؤول", - "role_user": "مستخدم", - "account_details": "تفاصيل الحساب", - "username": "اسم المستخدم", - "email": "البريد الإلكتروني", - "role": "الدور", - "last_login": "آخر دخول", - "storage": "التخزين", - "used": "مستخدم", - "quota": "الحصة", - "usage": "الاستخدام", - "unlimited": "غير محدود", - "app_passwords": "كلمات مرور التطبيقات", - "app_pw_desc": "أنشئ كلمات مرور لعملاء WebDAV و CalDAV و CardDAV. تُعرض كل كلمة مرور مرة واحدة فقط.", - "app_pw_label_placeholder": "التسمية (مثلاً Thunderbird، macOS)", - "generate": "إنشاء", - "generating": "جارٍ الإنشاء…", - "new_password_for": "كلمة مرور جديدة لـ", - "copy_warning": "انسخ كلمة المرور الآن. لن تتمكن من رؤيتها مرة أخرى.", - "copy_to_clipboard": "نسخ إلى الحافظة", - "col_label": "التسمية", - "col_created": "تاريخ الإنشاء", - "col_last_used": "آخر استخدام", - "col_status": "الحالة", - "active": "نشط", - "revoked": "ملغى", - "revoke_title": "إلغاء", - "no_app_passwords": "لا توجد كلمات مرور تطبيقات بعد.", - "client_sessions": "جلسات العميل", - "client_sessions_desc": "تُنشأ تلقائيًا عند اتصال عميل متوافق مع Nextcloud.", - "col_client": "العميل", - "never": "أبداً", - "just_now": "الآن", - "minutes_ago": "منذ {{n}} دقيقة", - "hours_ago": "منذ {{n}} ساعة", - "days_ago": "منذ {{n}} يوم", - "edit_profile": "تعديل الملف الشخصي", - "edit_oidc_managed": "لتغيير معلوماتك (الاسم، الاسم الأول، صورة الملف الشخصي، …)، يرجى تحديثها لدى مزود الهوية. ستظهر تغييراتك عند تسجيل الدخول التالي.", - "username_claim_hint": "2-64 حرفًا، أحرف / أرقام / نقطة / شرطة / شرطة سفلية. بمجرد الاختيار، لا يمكن تغيير اسم المستخدم (عملاء DAV/NextCloud يعتمدون عليه).", - "username_already_claimed": "اسم المستخدم محدد ولا يمكن تغييره (عملاء DAV/NextCloud يعتمدون عليه).", - "given_name": "الاسم الأول", - "family_name": "اسم العائلة", - "notify_on_share": "أرسل لي بريدًا إلكترونيًا عندما يشاركني شخص ما", - "notify_on_share_hint": "عند إلغاء التحديد، ستظل المشاركات تظهر في حسابك — لن تتلقى فقط بريدًا إلكترونيًا بشأنها.", - "save_profile": "حفظ التغييرات", - "profile_saved": "تم تحديث الملف الشخصي", - "profile_no_changes": "لا توجد تغييرات لحفظها.", - "profile_save_failed": "فشل الحفظ", - "username_taken_error": "اسم المستخدم هذا مستخدم بالفعل.", - "username_immutable_error": "اسم المستخدم الخاص بك محدد بالفعل ولا يمكن تغييره هنا. اتصل بالمسؤول إذا كنت بحاجة إلى إعادة التسمية.", - "change_password": "تغيير كلمة المرور", - "current_password": "كلمة المرور الحالية", - "new_password": "كلمة المرور الجديدة", - "min_8_chars": "8 أحرف على الأقل", - "confirm_password": "تأكيد كلمة المرور الجديدة", - "update_password": "تحديث كلمة المرور", - "updating": "جارٍ التحديث…", - "password_updated": "تم تحديث كلمة المرور بنجاح", - "passwords_no_match": "كلمتا المرور غير متطابقتين", - "password_too_short": "يجب أن تكون كلمة المرور 8 أحرف على الأقل", - "password_change_failed": "فشل تغيير كلمة المرور", - "error_network": "خطأ في الشبكة: {{message}}", - "error_label_required": "أدخل تسمية", - "error_create_pw": "فشل إنشاء كلمة المرور", - "confirm_revoke": "إلغاء كلمة المرور \"{{label}}\"؟ ستتوقف العملاء عن العمل.", - "error_revoke": "فشل الإلغاء", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "جارٍ الرفع...", - "files": "ملفات", - "complete": "{{count}} / {{total}} تم الرفع" - }, - "storage_quota_exceeded": "تجاوز حصة التخزين", - "sharedwithme": { - "pageTitle": "مشترك معي", - "pageDescription": "الملفات والمجلدات التي شاركها معك مستخدمون آخرون", - "emptyStateTitle": "لم يُشارك معك أي شيء بعد", - "emptyStateDesc": "ستظهر هنا العناصر التي يشاركها معك مستخدمون آخرون", - "loadMore": "تحميل المزيد", - "sharedBy": "مشترك من قِبل", - "colName": "الاسم", - "colType": "النوع", - "colSharedBy": "مشترك من قِبل", - "colDate": "تاريخ المشاركة", - "colPermissions": "الصلاحيات" - }, - "groupby": { - "none": "لا شيء", - "title": "التجميع حسب", - "owner": "المالك", - "shareDate": "تاريخ المشاركة", - "type": "النوع", - "type.folders": "المجلدات", - "accessedAt": "تاريخ الوصول", - "modifiedAt": "تاريخ التعديل", - "createdAt": "تاريخ الإنشاء", - "size": "الحجم", - "favoriteDate": "تاريخ المفضلة", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "جديد" - }, - "dateBucket": { - "today": "اليوم", - "last7days": "آخر 7 أيام", - "last30days": "آخر 30 يومًا" - }, - "groups": { - "title": "إدارة المجموعات", - "create_button": "إنشاء مجموعة", - "create_dialog_title": "مجموعة جديدة", - "edit_dialog_title": "إعادة تسمية المجموعة", - "name_label": "الاسم", - "name_placeholder": "engineering", - "description_label": "الوصف (اختياري)", - "members_section": "الأعضاء", - "add_member_placeholder": "إضافة مستخدم أو مجموعة…", - "no_members": "لا يوجد أعضاء بعد.", - "remove_member": "إزالة", - "delete_group": "حذف المجموعة", - "delete_confirm": "حذف المجموعة \"{name}\"؟ سيتم إلغاء الصلاحيات المرتبطة بهذه المجموعة.", - "empty_state": "لا توجد مجموعات بعد.", - "load_more": "تحميل المزيد", - "back_to_list": "رجوع", - "loading": "جارٍ التحميل…", - "virtual_badge": "النظام", - "member_count_zero": "لا يوجد أعضاء", - "member_count_one": "عضو واحد", - "member_count_other": "{count} أعضاء", - "delete_confirm_label": "اكتب اسم المجموعة للتأكيد:", - "delete_confirm_mismatch": "اكتب اسم المجموعة كما هو للتأكيد.", - "virtual_internal_name": "داخلي", - "members_loading": "جارٍ تحميل الأعضاء…", - "members_empty": "لا يوجد أعضاء", - "virtual_internal_explanation": "كل مستخدم داخلي على هذا الخادم" - }, - "myshares": { - "copyLink": "نسخ الرابط", - "deleteLink": "حذف الرابط", - "notifyByEmail": "إشعار عبر البريد الإلكتروني", - "notifyFailed": "تعذّر إرسال الإشعار.", - "notifyGroupMembers": "إشعار أعضاء المجموعة", - "notifyRateLimited": "عدد كبير من الإشعارات لهذا المستلم — حاول لاحقًا.", - "removeAccess": "إزالة الوصول", - "resendInvitation": "إعادة إرسال بريد الدعوة" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/de.json b/static/locales/de.json deleted file mode 100644 index a4771f23..00000000 --- a/static/locales/de.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Dieser Anmeldelink ist nicht mehr gültig", - "expired_body": "Der Link ist möglicherweise abgelaufen oder wurde bereits verwendet. Wir können Ihnen einen neuen senden — er wird in wenigen Sekunden in Ihrem Posteingang sein.", - "resend_to": "Neuen Link an {{email}} senden", - "generic_unavailable": "Dieser Anmeldelink ist nicht mehr gültig. Er wurde möglicherweise bereits verwendet oder ist abgelaufen. Fordern Sie auf der Anmeldeseite einen neuen Link an.", - "service_unavailable": "Die Magic-Link-Anmeldung ist auf diesem Server nicht aktiviert.", - "internal_error": "Bei der Anmeldung ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", - "resend_failure": "Beim Senden des Links ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", - "cross_browser_title": "Anmeldung auf diesem Gerät fortsetzen?", - "cross_browser_body": "Sie haben diesen Anmeldelink in einem anderen Browser oder Gerät geöffnet als dem, von dem aus Sie ihn angefordert haben.", - "cross_browser_warning": "Wenn Sie diesen Link angefordert haben, können Sie sicher fortfahren. Falls nicht, schließen Sie diese Seite — ein Klick auf Weiter würde jemand anderen in Ihrem Konto anmelden.", - "cross_browser_continue": "Fortfahren und anmelden", - "resend_confirmation_title": "Prüfen Sie Ihren Posteingang", - "resend_confirmation_body": "Falls der Anmeldelink zu einem aktiven Konto gehörte, wurde gerade ein neuer Link gesendet. Bitte prüfen Sie Ihren Posteingang.", - "return_link": "Zurück zu OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", - "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie ihn, indem Sie auf den folgenden Link klicken:\n{{link}}\n\nDer Link kann nur einmal verwendet werden und läuft in {{ttl_hours}} Stunden ab.\nFalls Sie diese Einladung nicht erwartet haben, können Sie diese Nachricht ignorieren.\n\n— OxiCloud" - }, - "login": { - "subject": "Anmeldung bei OxiCloud", - "body": "Hallo,\n\nVerwenden Sie den Link unten, um sich bei OxiCloud anzumelden. Der Link kann nur einmal verwendet werden und läuft in {{ttl_minutes}} Minuten ab. Öffnen Sie ihn auf demselben Gerät, von dem aus Sie ihn angefordert haben.\n\n{{link}}\n\nFalls Sie diesen Anmeldelink nicht angefordert haben, können Sie diese Nachricht ignorieren — es ist keine weitere Aktion erforderlich.\n\n— OxiCloud" - }, - "kind_file": "Datei", - "kind_folder": "Ordner", - "english_fallback_divider": "--- Englische Version unten ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt", - "body": "{{inviter_full}} hat einen {{kind}} mit Ihnen auf OxiCloud geteilt.\n\nÖffnen Sie OxiCloud, um Ihre neue Freigabe zu sehen:\n{{login_link}}\n\nMöglicherweise gibt es weitere neue Freigaben von {{inviter}} — melden Sie sich an, um alle Ihre freigegebenen Elemente zu sehen.\n\n— OxiCloud\n\nSie erhalten diese Nachricht, weil Sie ein OxiCloud-Konto haben und die Benachrichtigung über Freigaben aktiviert ist. Sie können sie in Ihrem Profil deaktivieren (Per E-Mail benachrichtigen, wenn jemand mit mir teilt)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalistisches Cloud-Speichersystem" - }, - "nav": { - "files": "Dateien", - "shared": "Freigaben", - "recent": "Zuletzt verwendet", - "favorites": "Favoriten", - "photos": "Fotos", - "music": "Musik", - "trash": "Papierkorb", - "sharedwithme": "Mit mir geteilt" - }, - "photos": { - "empty_state": "Noch keine Fotos", - "empty_hint": "Laden Sie Bilder oder Videos hoch, um sie hier zu sehen", - "items_selected": "ausgewählt", - "view_daily": "Tag", - "view_monthly": "Monat", - "view_yearly": "Jahr" - }, - "music": { - "create_playlist": "Playlist erstellen", - "playlists": "Playlists", - "no_playlists": "Noch keine Playlists", - "select_playlist": "Playlist auswählen", - "select_hint": "Wählen Sie eine Playlist aus der Seitenleiste oder erstellen Sie eine neue", - "add_tracks": "Titel hinzufügen", - "no_tracks": "Keine Titel in dieser Playlist", - "unknown_artist": "Unbekannter Künstler", - "unknown_title": "Unbekannt", - "confirm_delete": "Diese Playlist löschen?", - "playlist_name": "Playlist-Name", - "create": "Erstellen", - "delete": "Löschen", - "share": "Teilen", - "edit": "Bearbeiten", - "play_all": "Alle abspielen", - "shuffle": "Zufällig", - "repeat": "Wiederholen", - "repeat_one": "Einen wiederholen", - "queue": "Warteschlange", - "queue_empty": "Warteschlange ist leer", - "not_playing": "Nicht abspielend", - "play": "Abspielen", - "pause": "Pause", - "previous": "Zurück", - "next": "Weiter", - "volume": "Lautstärke", - "mute": "Stumm", - "unmute": "Ton ein", - "title": "Titel", - "artist": "Künstler", - "album": "Album", - "tracks": "Titel", - "add": "Hinzufügen", - "added": "Hinzugefügt!", - "added_to_playlist": "zur Playlist hinzugefügt", - "add_to_playlist": "Zur Playlist hinzufügen", - "load_error": "Fehler beim Laden der Playlists", - "add_error": "Tracks konnten nicht hinzugefügt werden", - "no_playlists_yet": "Noch keine Playlists. Erstellen Sie zuerst eine!", - "selected_files": "Ausgewählt:", - "error": "Fehler", - "search_audio": "Audiodateien suchen…", - "no_audio_files": "Keine Audiodateien gefunden", - "selected": "ausgewählt", - "loading": "Wird geladen…", - "search_error": "Audiodateien konnten nicht geladen werden", - "adding": "Wird hinzugefügt…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Dateien suchen...", - "new_folder": "Neuer Ordner", - "upload": "Hochladen", - "upload_files": "Dateien hochladen", - "upload_folder": "Ordner hochladen", - "upload.uploading": "Wird hochgeladen...", - "upload.complete": "{count} / {total} hochgeladen", - "upload.files": "Dateien", - "rename": "Umbenennen", - "move": "Verschieben nach...", - "move_to": "Verschieben nach", - "delete": "Löschen", - "download": "Herunterladen", - "view": "Anzeigen", - "cancel": "Abbrechen", - "confirm": "Bestätigen", - "share": "Teilen", - "favorite": "Zu Favoriten hinzufügen", - "unfavorite": "Aus Favoriten entfernen", - "copy": "Kopieren", - "notify": "Benachrichtigen", - "send": "Senden", - "clear_recent": "Zuletzt verwendete löschen", - "logout": "Abmelden", - "create": "Erstellen", - "search_btn": "Suchen", - "close": "Schließen", - "delete_permanently": "Endgültig löschen", - "empty_trash": "Papierkorb leeren", - "open_parent_folder": "Zum übergeordneten Ordner", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Erscheinungsbild", - "about": "Über OxiCloud", - "about_description": "Cloud-Speicherplattform mit Rust und Clean Architecture. Schnell, sicher und privat.", - "admin_panel": "Admin-Panel", - "profile": "Mein Profil", - "role_user": "Benutzer", - "theme": { - "light": "Hell", - "dark": "Dunkel", - "auto": "Wie System" - }, - "manage_groups": "Gruppen verwalten" - }, - "share": { - "dialogTitle": "Link teilen", - "linkLabel": "Geteilter Link:", - "copyLink": "Kopieren", - "permissions": "Berechtigungen:", - "permissionRead": "Lesen", - "permissionWrite": "Schreiben", - "permissionReshare": "Weiterteilen", - "password": "Passwortschutz:", - "generatePassword": "Generieren", - "expiration": "Ablaufdatum:", - "update": "Freigabe aktualisieren", - "remove": "Freigabe entfernen", - "notifyTitle": "Benachrichtigung senden", - "notifyEmailLabel": "E-Mail-Adresse:", - "notifyMessageLabel": "Nachricht (optional):", - "notifySend": "Benachrichtigung senden", - "shareWithOthers": "Mit anderen teilen", - "sharePublicly": "Öffentlich teilen", - "shareSettings": "Freigabeeinstellungen", - "shareCopied": "Link in Zwischenablage kopiert", - "shareCreated": "Freigabelink erfolgreich erstellt", - "shareUpdated": "Freigabeeinstellungen aktualisiert", - "shareRemoved": "Freigabe erfolgreich entfernt", - "inviteByEmail": "Per E-Mail einladen — Einladung wird gesendet", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link teilen", - "share_linkLabel": "Geteilter Link:", - "share_copyLink": "Kopieren", - "share_permissions": "Berechtigungen:", - "share_permissionRead": "Lesen", - "share_permissionWrite": "Schreiben", - "share_permissionReshare": "Weiterteilen", - "share_password": "Passwortschutz:", - "share_generatePassword": "Generieren", - "share_expiration": "Ablaufdatum:", - "share_update": "Freigabe aktualisieren", - "share_remove": "Freigabe entfernen", - "share_notifyTitle": "Benachrichtigung senden", - "share_notifyEmailLabel": "E-Mail-Adresse:", - "share_notifyMessageLabel": "Nachricht (optional):", - "share_notifySend": "Benachrichtigung senden", - "shared": { - "backToFiles": "Zurück zu Dateien", - "pageTitle": "Geteilte Ressourcen", - "pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", - "filterType": "Typ:", - "filterAll": "Alle", - "filterFiles": "Dateien", - "filterFolders": "Ordner", - "sortBy": "Sortieren nach:", - "sortByName": "Name", - "sortByDate": "Freigabedatum", - "sortByExpiration": "Ablaufdatum", - "search": "Suchen", - "colName": "Name", - "colType": "Typ", - "colDateShared": "Freigabedatum", - "colExpiration": "Ablaufdatum", - "colPermissions": "Berechtigungen", - "colPassword": "Passwort", - "colActions": "Aktionen", - "emptyStateTitle": "Noch keine geteilten Ressourcen", - "emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", - "goToFiles": "Zu Dateien gehen", - "typeFile": "Datei", - "typeFolder": "Ordner", - "noExpiration": "Kein Ablaufdatum", - "hasPassword": "Ja", - "noPassword": "Nein", - "editShare": "Freigabe bearbeiten", - "notifyShare": "Jemanden benachrichtigen", - "copyLink": "Link kopieren", - "removeShare": "Freigabe entfernen", - "linkCopied": "Link in Zwischenablage kopiert!", - "linkCopyFailed": "Link konnte nicht kopiert werden", - "itemUpdated": "Freigabeeinstellungen aktualisiert", - "itemRemoved": "Freigabe erfolgreich entfernt", - "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", - "notificationSent": "Benachrichtigung erfolgreich gesendet", - "notificationFailed": "Benachrichtigung konnte nicht gesendet werden", - "shared_backToFiles": "Zurück zu Dateien", - "shared_pageTitle": "Geteilte Ressourcen", - "shared_pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner", - "shared_filterType": "Typ:", - "shared_filterAll": "Alle", - "shared_filterFiles": "Dateien", - "shared_filterFolders": "Ordner", - "shared_sortBy": "Sortieren nach:", - "shared_sortByName": "Name", - "shared_sortByDate": "Freigabedatum", - "shared_sortByExpiration": "Ablaufdatum", - "shared_search": "Suchen", - "shared_colName": "Name", - "shared_colType": "Typ", - "shared_colDateShared": "Freigabedatum", - "shared_colExpiration": "Ablaufdatum", - "shared_colPermissions": "Berechtigungen", - "shared_colPassword": "Passwort", - "shared_colActions": "Aktionen", - "shared_emptyStateTitle": "Noch keine geteilten Ressourcen", - "shared_emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt", - "shared_goToFiles": "Zu Dateien gehen", - "shared_typeFile": "Datei", - "shared_typeFolder": "Ordner", - "shared_noExpiration": "Kein Ablaufdatum", - "shared_hasPassword": "Ja", - "shared_noPassword": "Nein", - "shared_editShare": "Freigabe bearbeiten", - "shared_notifyShare": "Jemanden benachrichtigen", - "shared_copyLink": "Link kopieren", - "shared_removeShare": "Freigabe entfernen", - "shared_linkCopied": "Link in Zwischenablage kopiert!", - "shared_linkCopyFailed": "Link konnte nicht kopiert werden", - "shared_itemUpdated": "Freigabeeinstellungen aktualisiert", - "shared_itemRemoved": "Freigabe erfolgreich entfernt", - "shared_invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein", - "shared_notificationSent": "Benachrichtigung erfolgreich gesendet", - "shared_notificationFailed": "Benachrichtigung konnte nicht gesendet werden" - }, - "files": { - "name": "Name", - "type": "Typ", - "size": "Größe", - "modified": "Geändert", - "no_files": "Keine Dateien in diesem Ordner", - "empty_hint": "Laden Sie Dateien hoch oder erstellen Sie Ordner, um loszulegen", - "loading": "Dateien werden geladen…", - "view_grid": "Rasteransicht", - "view_list": "Listenansicht", - "file_types": { - "document": "Dokument", - "image": "Bild", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Text", - "folder": "Ordner", - "spreadsheet": "Tabelle", - "presentation": "Präsentation", - "archive": "Archiv", - "installer": "Installationsdatei", - "code": "Code" - }, - "owner": "Eigentümer" - }, - "dialogs": { - "rename_folder": "Ordner umbenennen", - "rename_file": "Datei umbenennen", - "new_name": "Neuer Name", - "new_folder_title": "Neuer Ordner", - "folder_name": "Ordnername", - "folder_placeholder": "Mein Ordner", - "rename_title": "Umbenennen", - "move_file": "Datei verschieben", - "move_folder": "Ordner verschieben", - "select_destination": "Zielordner auswählen:", - "root": "Stammverzeichnis", - "delete_confirmation": "Sind Sie sicher, dass Sie löschen möchten", - "and_contents": "und den gesamten Inhalt", - "no_undo": "Diese Aktion kann nicht rückgängig gemacht werden", - "confirm_title": "Aktion bestätigen", - "confirm_delete": "In Papierkorb verschieben", - "confirm_delete_file": "Sind Sie sicher, dass Sie die Datei \"{{name}}\" in den Papierkorb verschieben möchten?", - "confirm_delete_folder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und seinen gesamten Inhalt in den Papierkorb verschieben möchten?", - "confirm_permanent_delete": "Endgültig löschen", - "confirm_permanent_delete_msg": "Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", - "confirm_empty_trash": "Papierkorb leeren", - "confirm_delete_share": "Freigabelink löschen", - "confirm_delete_share_msg": "Sind Sie sicher, dass Sie diesen Freigabelink löschen möchten?", - "share_file": "Datei teilen", - "share_folder": "Ordner teilen", - "existing_shares": "Bestehende Freigaben", - "share_options": "Freigabeoptionen", - "password": "Passwort", - "expiration": "Ablaufdatum", - "permissions": "Berechtigungen", - "generated_link": "Generierter Link", - "notify": "Benachrichtigung senden", - "recipient": "Empfänger", - "message": "Nachricht", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "In den Home-Ordner verschieben" - }, - "dropzone": { - "drag_files": "Dateien hierher ziehen oder klicken zum Auswählen", - "drop_files": "Dateien zum Hochladen ablegen" - }, - "permissions": { - "read": "Lesen", - "write": "Schreiben", - "reshare": "Weiterteilen" - }, - "errors": { - "file_not_found": "Datei nicht gefunden", - "folder_not_found": "Ordner nicht gefunden", - "delete_error": "Fehler beim Löschen", - "upload_error": "Fehler beim Hochladen", - "rename_error": "Fehler beim Umbenennen", - "move_error": "Fehler beim Verschieben", - "empty_name": "Der Name darf nicht leer sein", - "name_exists": "Eine Datei oder ein Ordner mit diesem Namen existiert bereits", - "generic_error": "Ein Fehler ist aufgetreten", - "group_name_invalid": "Der Gruppenname muss dem E-Mail-Präfix-Format entsprechen (Buchstaben, Ziffern, Punkt, Bindestrich, Unterstrich; 1–64 Zeichen).", - "group_cycle": "Dieses Mitglied würde einen Gruppen-Zirkelbezug erzeugen.", - "group_depth_exceeded": "Die Verschachtelungstiefe überschreitet das zulässige Maximum (8).", - "group_virtual_immutable": "Die Gruppe „Internal“ wird vom System verwaltet und kann nicht geändert werden.", - "group_not_found": "Gruppe nicht gefunden.", - "group_name_taken": "Eine Gruppe mit diesem Namen existiert bereits." - }, - "breadcrumb": { - "home": "Startseite" - }, - "trash": { - "empty_trash": "Papierkorb leeren", - "empty_state": "Der Papierkorb ist leer", - "original_location": "Ursprünglicher Speicherort", - "deleted_date": "Löschdatum", - "remaining": "Verbleibend", - "actions": "Aktionen", - "restore": "Wiederherstellen", - "delete_permanently": "Endgültig löschen", - "empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht.", - "groupby": { - "remaining_days": "Verbleibende Tage", - "trashed_time": "Löschzeit" - } - }, - "daysRemaining": { - "expired": "Abgelaufen", - "today": "Heute", - "tomorrow": "Morgen", - "inDays": "{{count}} Tage" - }, - "expiryChip": { - "never": "Läuft nie ab", - "expired": "Abgelaufen", - "today": "Läuft heute ab", - "tomorrow": "Läuft morgen ab", - "inDays": "Läuft in {{count}} Tagen ab", - "onDate": "Läuft am {{date}} ab" - }, - "auth": { - "login_title": "Anmelden", - "username": "Benutzername", - "username_placeholder": "Geben Sie Ihren Benutzernamen ein", - "login_identifier": "Benutzername oder E-Mail", - "login_identifier_placeholder": "Geben Sie Ihren Benutzernamen oder Ihre E-Mail-Adresse ein", - "password": "Passwort", - "password_placeholder": "Geben Sie Ihr Passwort ein", - "login_button": "Anmelden", - "no_account": "Kein Konto?", - "register": "Registrieren", - "admin_setup": "Erstmalig?", - "setup": "Administrator einrichten", - "register_title": "Konto erstellen", - "email": "E-Mail", - "email_placeholder": "Geben Sie Ihre E-Mail ein", - "confirm_password": "Passwort bestätigen", - "confirm_password_placeholder": "Bestätigen Sie Ihr Passwort", - "register_button": "Konto erstellen", - "have_account": "Bereits ein Konto?", - "login": "Anmelden", - "setup_title": "Ersteinrichtung", - "setup_step1": "Admin", - "setup_step2": "System", - "setup_step3": "Abgeschlossen", - "admin_username": "Admin-Benutzername", - "admin_email": "Admin-E-Mail", - "admin_password": "Admin-Passwort", - "create_admin": "Administrator erstellen", - "back_to_login": "Bereits eingerichtet?", - "admin_success": "Administratorkonto erfolgreich erstellt! Sie können sich jetzt anmelden.", - "account_success": "Konto erfolgreich erstellt! Sie können sich jetzt anmelden.", - "passwords_mismatch": "Die Passwörter stimmen nicht überein", - "admin_create_error": "Fehler beim Erstellen des Administratorkontos", - "or": "oder", - "sso_login": "Mit SSO anmelden", - "sso_login_provider": "Mit {{provider}} anmelden", - "magicLinkHint": "Kein Passwort? Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen einmaligen Anmeldelink.", - "magicLinkEmailLabel": "E-Mail-Adresse", - "magicLinkEmailPlaceholder": "sie@beispiel.de", - "magicLinkSubmit": "Anmeldelink senden", - "magicLinkSent": "Wenn für diese E-Mail-Adresse ein Konto besteht, wurde ein Anmeldelink gesendet. Überprüfen Sie Ihren Posteingang.", - "magicLinkUnavailable": "Die Anmeldung per E-Mail ist auf diesem Server nicht verfügbar.", - "magicLinkNetworkError": "Server nicht erreichbar: {{message}}", - "magicLinkToggle": "Kein Passwort? Anmeldelink per E-Mail", - "passwordsMatch": "Passwörter stimmen überein", - "capsLock": "Feststelltaste aktiv" - }, - "storage": { - "title": "Speicher", - "calculating": "Berechnung...", - "used": "{{percentage}}% verwendet ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Dieser Dateityp kann nicht in der Vorschau angezeigt werden.", - "download_file": "Datei herunterladen", - "zoom_in": "Vergrößern", - "zoom_out": "Verkleinern", - "zoom_reset": "Zoom zurücksetzen" - }, - "language_selector": { - "title": "Willkommen!", - "subtitle": "Wählen Sie Ihre Sprache, um fortzufahren", - "continue": "Weiter", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Noch keine Favoriten", - "empty_hint": "Markieren Sie Dateien oder Ordner mit einem Stern, um sie zu Ihren Favoriten hinzuzufügen", - "add": "Zu Favoriten hinzufügen", - "remove": "Aus Favoriten entfernen", - "added_title": "Zu Favoriten hinzugefügt", - "added_msg": "zu Favoriten hinzugefügt", - "removed_title": "Aus Favoriten entfernt", - "removed_msg": "aus Favoriten entfernt" - }, - "recent": { - "title": "Zuletzt verwendet", - "clear": "Zuletzt verwendete löschen", - "accessed": "Zugegriffen", - "empty_state": "Keine zuletzt verwendeten Dateien", - "empty_hint": "Dateien, die Sie öffnen, werden hier angezeigt", - "loadMore": "Mehr laden" - }, - "notifications": { - "file_renamed": "Datei umbenannt", - "file_renamed_to": "Datei umbenannt in \"{{name}}\"", - "folder_renamed": "Ordner umbenannt", - "folder_renamed_to": "Ordner umbenannt in \"{{name}}\"", - "file_uploaded": "Datei hochgeladen", - "file_deleted": "Datei in Papierkorb verschoben", - "folder_deleted": "Ordner in Papierkorb verschoben", - "item_deleted_permanently": "Element endgültig gelöscht", - "trash_emptied": "Papierkorb erfolgreich geleert", - "title": "Benachrichtigungen", - "empty": "Keine Benachrichtigungen", - "link_created": "Link erstellt", - "share_success": "Freigabelink erfolgreich erstellt", - "upload_files_section_title": "Upload hier nicht verfügbar", - "upload_files_section_body": "Wechseln Sie zum Abschnitt Dateien, um Dateien hochzuladen" - }, - "batch": { - "one_selected": "1 Element ausgewählt", - "n_selected": "{{count}} Elemente ausgewählt", - "confirm_delete": "Möchten Sie wirklich {{count}} Elemente in den Papierkorb verschieben?", - "move_title": "{{count}} Element(e) verschieben", - "add_favorites": "Zu Favoriten hinzufügen", - "move_copy": "Verschieben oder kopieren" - }, - "admin": { - "page_title": "Admin-Panel", - "back_to_app": "Zurück zu OxiCloud", - "loading": "Laden…", - "access_denied": "Zugriff verweigert", - "access_denied_desc": "Administratorrechte erforderlich.", - "sign_in": "Anmelden", - "tab_dashboard": "Dashboard", - "tab_users": "Benutzer", - "tab_oidc": "SSO / OIDC", - "total_users": "Benutzer gesamt", - "active_users": "Aktive Benutzer", - "admins": "Admins", - "version": "Version", - "storage_overview": "Speicherübersicht", - "used": "Verwendet", - "total_quota": "Gesamtkontingent", - "usage_pct": "Nutzung %", - "users_over_80": "Benutzer >80% Kontingent", - "users_over_quota": "Benutzer über Kontingent", - "system": "System", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Kontingente", - "enabled": "Aktiviert", - "disabled": "Deaktiviert", - "active": "Aktiv", - "off": "Aus", - "allow_registration": "Öffentliche Selbstregistrierung erlauben", - "registration_warning": "Öffentliche Registrierung ist deaktiviert. Nur Admins können neue Benutzer erstellen.", - "user_management": "Benutzerverwaltung", - "create_user": "Benutzer erstellen", - "col_user": "Benutzer", - "col_role": "Rolle", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Speicher", - "col_last_login": "Letzter Login", - "col_actions": "Aktionen", - "loading_users": "Benutzer werden geladen…", - "failed_load_users": "Laden fehlgeschlagen", - "no_users_found": "Keine Benutzer gefunden", - "showing_users": "Zeige {{from}}-{{to}} von {{total}}", - "prev": "Zurück", - "next": "Weiter", - "inactive": "Inaktiv", - "you_badge": "(du)", - "local": "Lokal", - "never": "Nie", - "just_now": "Gerade eben", - "minutes_ago": "vor {{n}}Min", - "hours_ago": "vor {{n}}Std", - "days_ago": "vor {{n}}T", - "edit_quota_title": "Kontingent bearbeiten", - "reset_password_title": "Passwort zurücksetzen", - "toggle_role_title": "Rolle wechseln", - "deactivate_title": "Deaktivieren", - "activate_title": "Aktivieren", - "delete_title": "Löschen", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "SSO-Authentifizierung aktivieren", - "provider_name": "Anbietername", - "issuer_url": "Aussteller-URL", - "issuer_url_hint": "OpenID Connect Aussteller-URL Ihres Identitätsanbieters", - "auto_discover": "Auto-Erkennung", - "discovering": "Erkennung…", - "client_id": "Client-ID", - "client_secret": "Client-Secret", - "client_secret_placeholder": "Leer lassen für aktuellen Wert", - "secret_configured": "Ein Client-Secret ist bereits konfiguriert", - "callback_url": "Callback-URL", - "callback_url_hint": "(bei IdP registrieren)", - "advanced_settings": "Erweiterte Einstellungen", - "scopes": "Scopes", - "auto_provision": "Benutzer bei erstem Login automatisch anlegen", - "admin_groups": "Admin-Gruppen", - "admin_groups_hint": "Kommagetrennte OIDC-Gruppennamen für Admin-Rolle", - "disable_password": "Passwort-Login deaktivieren (nur OIDC)", - "password_warning": "Dies verhindert ALLE passwortbasierten Anmeldungen!", - "test_btn": "Testen", - "save_btn": "Speichern", - "saving": "Speichern…", - "settings_saved": "Einstellungen gespeichert — OIDC ist jetzt {{status}}", - "quota_modal_title": "Speicherkontingent aktualisieren", - "quota_user_label": "Benutzer:", - "new_quota": "Neues Kontingent", - "quota_unlimited_hint": "0 für unbegrenzt", - "cancel": "Abbrechen", - "create_user_title": "Neuen Benutzer erstellen", - "username_label": "Benutzername", - "username_placeholder": "maxmuster", - "username_hint": "3–32 Zeichen", - "password_label": "Passwort", - "password_placeholder": "Min. 8 Zeichen", - "email_label": "E-Mail", - "email_optional": "(optional)", - "email_placeholder": "benutzer@beispiel.de (automatisch wenn leer)", - "role_label": "Rolle", - "role_user": "Benutzer", - "role_admin": "Admin", - "quota_label": "Kontingent", - "creating": "Erstellen…", - "reset_pw_title": "Passwort zurücksetzen", - "new_password_label": "Neues Passwort", - "resetting": "Zurücksetzen…", - "reset_btn": "Zurücksetzen", - "confirm_role_change": "Rolle zu {{role}} ändern?", - "confirm_deactivate": "Diesen Benutzer wirklich deaktivieren?", - "confirm_activate": "Diesen Benutzer wirklich aktivieren?", - "confirm_delete_user": "Benutzer \"{{name}}\" LÖSCHEN? Kann nicht rückgängig gemacht werden!", - "confirm_action": "Aktion bestätigen", - "confirm_yes": "Bestätigen", - "confirm_no": "Abbrechen", - "error_username_short": "Benutzername muss mindestens 3 Zeichen haben", - "error_password_short": "Passwort muss mindestens 8 Zeichen haben", - "error_generic": "Fehlgeschlagen", - "error_network": "Netzwerkfehler: {{message}}", - "error_create_user": "Benutzer erstellen fehlgeschlagen", - "tab_storage": "Speicher", - "storage_title": "Speicherkonfiguration", - "storage_current_backend": "Aktuelles Backend", - "storage_total_blobs": "Gesamt-Blobs", - "storage_total_size": "Gesamtgröße", - "storage_dedup_ratio": "Deduplizierungsrate", - "storage_backend": "Backend", - "storage_local": "Lokal", - "storage_s3": "S3-kompatibel", - "storage_provider_preset": "Anbieter-Voreinstellung", - "storage_preset_custom": "Benutzerdefiniert", - "storage_endpoint_url": "Endpunkt-URL", - "storage_endpoint_hint": "Leer lassen für AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Region", - "storage_access_key": "Zugriffsschlüssel", - "storage_secret_key": "Geheimschlüssel", - "storage_secret_configured": "Schlüssel konfiguriert", - "storage_key_placeholder": "Neuen Schlüssel eingeben", - "storage_path_style": "Pfadstil erzwingen", - "storage_path_style_hint": "Erforderlich für MinIO und einige S3-kompatible Dienste", - "storage_test_connection": "Verbindung testen", - "storage_test_success": "Verbindung erfolgreich", - "storage_test_failure": "Verbindung fehlgeschlagen", - "storage_save": "Konfiguration speichern", - "storage_saved": "Konfiguration gespeichert", - "storage_migration": "Datenmigration", - "storage_migration_coming_soon": "Migrationstools demnächst verfügbar", - "migration_status_label": "Migrationsstatus", - "migration_start": "Migration starten", - "migration_pause": "Pausieren", - "migration_resume": "Fortsetzen", - "migration_verify": "Verifizieren", - "migration_complete": "Abschließen", - "migration_started": "Migration gestartet", - "migration_paused_msg": "Migration pausiert", - "migration_resumed_msg": "Migration fortgesetzt", - "migration_completed_msg": "Migration erfolgreich abgeschlossen", - "migration_verifying": "Wird verifiziert...", - "migration_verify_passed": "Verifizierung erfolgreich", - "migration_verify_failed": "Verifizierung fehlgeschlagen", - "migration_failed_blobs": "Fehlgeschlagene Blobs", - "testing": "Wird getestet...", - "smtp_disabled": "Deaktiviert (Host nicht gesetzt)", - "smtp_enabled": "Aktiviert", - "smtp_enabled_label": "Status", - "smtp_intro": "SMTP wird ausschließlich über Umgebungsvariablen (OXICLOUD_SMTP_*) konfiguriert. Die folgenden Werte werden aus dem laufenden Server gelesen — zum Ändern bearbeiten Sie die Umgebung und starten OxiCloud neu.", - "smtp_not_configured": "SMTP ist auf diesem Server nicht konfiguriert.", - "smtp_send_failed": "Senden fehlgeschlagen.", - "smtp_send_test": "Test-E-Mail senden", - "smtp_sending": "Senden …", - "smtp_sent": "Test-E-Mail gesendet.", - "smtp_server_code": "Server antwortete", - "smtp_test_intro": "Sendet eine fest einprogrammierte Diagnosenachricht an den unten angegebenen Empfänger und meldet die Antwort des SMTP-Servers, sodass Sie sie mit Ihren Relay-Protokollen abgleichen können.", - "smtp_test_missing_to": "Geben Sie eine Empfängeradresse ein.", - "smtp_test_title": "Test-E-Mail senden", - "smtp_test_to": "Empfängeradresse", - "smtp_title": "Ausgehende E-Mail (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profil", - "back_to_app": "Zurück zu OxiCloud", - "loading": "Laden…", - "not_authenticated": "Nicht authentifiziert", - "not_authenticated_desc": "Bitte melden Sie sich an, um Ihr Profil anzuzeigen.", - "sign_in": "Anmelden", - "role_admin": "Administrator", - "role_user": "Benutzer", - "account_details": "Kontodetails", - "username": "Benutzername", - "email": "E-Mail", - "role": "Rolle", - "last_login": "Letzter Login", - "storage": "Speicher", - "used": "Verwendet", - "quota": "Kontingent", - "usage": "Nutzung", - "unlimited": "Unbegrenzt", - "app_passwords": "App-Passwörter", - "app_pw_desc": "Passwörter für WebDAV-, CalDAV- und CardDAV-Clients generieren. Jedes Passwort wird nur einmal angezeigt.", - "app_pw_label_placeholder": "Bezeichnung (z.B. Thunderbird, macOS)", - "generate": "Generieren", - "generating": "Generieren…", - "new_password_for": "Neues Passwort für", - "copy_warning": "Kopieren Sie dieses Passwort jetzt. Sie können es nicht erneut anzeigen.", - "copy_to_clipboard": "In Zwischenablage kopieren", - "col_label": "Bezeichnung", - "col_created": "Erstellt", - "col_last_used": "Zuletzt verwendet", - "col_status": "Status", - "active": "Aktiv", - "revoked": "Widerrufen", - "revoke_title": "Widerrufen", - "no_app_passwords": "Noch keine App-Passwörter.", - "client_sessions": "Client-Sitzungen", - "client_sessions_desc": "Automatisch generiert beim Verbinden eines Nextcloud-kompatiblen Clients.", - "col_client": "Client", - "never": "Nie", - "just_now": "Gerade eben", - "minutes_ago": "vor {{n}} Min", - "hours_ago": "vor {{n}} Std", - "days_ago": "vor {{n}} Tagen", - "edit_profile": "Profil bearbeiten", - "edit_oidc_managed": "Um Ihre Informationen (Name, Vorname, Profilbild, …) zu ändern, aktualisieren Sie sie bitte bei Ihrem Identity-Provider. Ihre Änderungen erscheinen bei der nächsten Anmeldung.", - "username_claim_hint": "2–64 Zeichen, Buchstaben / Ziffern / Punkt / Bindestrich / Unterstrich. Nach der Wahl kann der Benutzername nicht mehr geändert werden (DAV/NextCloud-Clients hängen davon ab).", - "username_already_claimed": "Benutzername ist gesetzt und kann nicht geändert werden (DAV/NextCloud-Clients hängen davon ab).", - "given_name": "Vorname", - "family_name": "Nachname", - "notify_on_share": "Mich per E-Mail benachrichtigen, wenn jemand mit mir teilt", - "notify_on_share_hint": "Wenn deaktiviert, werden Freigaben weiterhin in deinem Konto angezeigt — du erhältst nur keine E-Mail dazu.", - "save_profile": "Änderungen speichern", - "profile_saved": "Profil aktualisiert", - "profile_no_changes": "Keine Änderungen zu speichern.", - "profile_save_failed": "Speichern fehlgeschlagen", - "username_taken_error": "Dieser Benutzername ist bereits vergeben.", - "username_immutable_error": "Ihr Benutzername ist bereits gesetzt und kann hier nicht geändert werden. Wenden Sie sich an einen Administrator, wenn Sie umbenennen möchten.", - "change_password": "Passwort ändern", - "current_password": "Aktuelles Passwort", - "new_password": "Neues Passwort", - "min_8_chars": "Mindestens 8 Zeichen", - "confirm_password": "Neues Passwort bestätigen", - "update_password": "Passwort aktualisieren", - "updating": "Aktualisierung…", - "password_updated": "Passwort erfolgreich aktualisiert", - "passwords_no_match": "Passwörter stimmen nicht überein", - "password_too_short": "Passwort muss mindestens 8 Zeichen haben", - "password_change_failed": "Passwort ändern fehlgeschlagen", - "error_network": "Netzwerkfehler: {{message}}", - "error_label_required": "Bitte Bezeichnung eingeben", - "error_create_pw": "App-Passwort erstellen fehlgeschlagen", - "confirm_revoke": "App-Passwort \"{{label}}\" widerrufen? Clients werden nicht mehr funktionieren.", - "error_revoke": "Widerrufen fehlgeschlagen", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Wird hochgeladen...", - "files": "Dateien", - "complete": "{{count}} / {{total}} hochgeladen" - }, - "storage_quota_exceeded": "Speicherplatz erschöpft", - "sharedwithme": { - "pageTitle": "Mit mir geteilt", - "pageDescription": "Dateien und Ordner, die andere Benutzer mit Ihnen geteilt haben", - "emptyStateTitle": "Noch nichts mit Ihnen geteilt", - "emptyStateDesc": "Elemente, die andere Benutzer mit Ihnen teilen, erscheinen hier", - "loadMore": "Mehr laden", - "sharedBy": "Geteilt von", - "colName": "Name", - "colType": "Typ", - "colSharedBy": "Geteilt von", - "colDate": "Datum der Freigabe", - "colPermissions": "Berechtigungen" - }, - "groupby": { - "none": "Keine", - "title": "Gruppieren nach", - "owner": "Eigentümer", - "shareDate": "Freigabedatum", - "type": "Typ", - "type.folders": "Ordner", - "accessedAt": "Zugriffsdatum", - "modifiedAt": "Änderungsdatum", - "createdAt": "Erstellungsdatum", - "size": "Größe", - "favoriteDate": "Datum der Markierung", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Neu" - }, - "dateBucket": { - "today": "Heute", - "last7days": "Letzte 7 Tage", - "last30days": "Letzte 30 Tage" - }, - "groups": { - "title": "Gruppen verwalten", - "create_button": "Gruppe erstellen", - "create_dialog_title": "Neue Gruppe", - "edit_dialog_title": "Gruppe umbenennen", - "name_label": "Name", - "name_placeholder": "engineering", - "description_label": "Beschreibung (optional)", - "members_section": "Mitglieder", - "add_member_placeholder": "Benutzer oder Gruppe hinzufügen…", - "no_members": "Noch keine Mitglieder.", - "remove_member": "Entfernen", - "delete_group": "Gruppe löschen", - "delete_confirm": "Die Gruppe „{name}\" löschen? Auf diese Gruppe verweisende Berechtigungen werden widerrufen.", - "empty_state": "Noch keine Gruppen.", - "load_more": "Mehr laden", - "back_to_list": "Zurück", - "loading": "Wird geladen…", - "virtual_badge": "System", - "member_count_zero": "Keine Mitglieder", - "member_count_one": "1 Mitglied", - "member_count_other": "{count} Mitglieder", - "delete_confirm_label": "Tippe den Gruppennamen zur Bestätigung ein:", - "delete_confirm_mismatch": "Tippe den Gruppennamen exakt zur Bestätigung ein.", - "virtual_internal_name": "Intern", - "members_loading": "Mitglieder werden geladen…", - "members_empty": "Keine Mitglieder", - "virtual_internal_explanation": "Jeder interne Benutzer auf diesem Server" - }, - "myshares": { - "copyLink": "Link kopieren", - "deleteLink": "Link löschen", - "notifyByEmail": "Per E-Mail benachrichtigen", - "notifyFailed": "Benachrichtigung konnte nicht gesendet werden.", - "notifyGroupMembers": "Gruppenmitglieder benachrichtigen", - "notifyRateLimited": "Zu viele Benachrichtigungen für diesen Empfänger — versuchen Sie es später erneut.", - "removeAccess": "Zugriff entfernen", - "resendInvitation": "Einladungs-E-Mail erneut senden" - }, - "sort": { - "asc": "aufsteigend", - "desc": "absteigend" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/en.json b/static/locales/en.json deleted file mode 100644 index c829bf57..00000000 --- a/static/locales/en.json +++ /dev/null @@ -1,1027 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "This sign-in link is no longer valid", - "expired_body": "The link may have expired or already been used. We can send you a fresh one — it'll arrive in your inbox in a few seconds.", - "resend_to": "Send a fresh link to {{email}}", - "generic_unavailable": "This sign-in link is no longer valid. It may have already been used or expired. Request a fresh link from the login page.", - "service_unavailable": "Magic-link sign-in is not enabled on this server.", - "internal_error": "Something went wrong while signing you in. Please try again.", - "resend_failure": "Something went wrong while sending the link. Please try again.", - "cross_browser_title": "Continue signing in on this device?", - "cross_browser_body": "You opened this sign-in link in a different browser or device than the one where you requested it.", - "cross_browser_warning": "If you requested this link, it's safe to continue. If you didn't, close this page — clicking Continue would sign someone else into your account.", - "cross_browser_continue": "Continue and sign in", - "resend_confirmation_title": "Check your inbox", - "resend_confirmation_body": "If the sign-in link belonged to an active account, a fresh link has just been sent. Please check your inbox.", - "return_link": "Return to OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", - "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen it by clicking the link below:\n{{link}}\n\nThe link works once and expires in {{ttl_hours}} hours.\nIf you didn't expect this invitation, you can safely ignore this message.\n\n— OxiCloud" - }, - "login": { - "subject": "Sign in to OxiCloud", - "body": "Hello,\n\nUse the link below to sign in to OxiCloud. The link works once and expires in {{ttl_minutes}} minutes. Open it on the same device where you requested it.\n\n{{link}}\n\nIf you didn't request this sign-in link, you can safely ignore this message — no further action is needed.\n\n— OxiCloud" - }, - "kind_file": "file", - "kind_folder": "folder", - "english_fallback_divider": "--- English version below ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} shared a {{kind}} with you on OxiCloud", - "body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen OxiCloud to see your new share:\n{{login_link}}\n\nYou may have additional new shares from {{inviter}} — sign in to see all your shared items.\n\n— OxiCloud\n\nYou're receiving this message because you have an OxiCloud account and your share-notification preference is on. You can turn it off in your profile (Email me when someone shares with me)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalist cloud storage system" - }, - "myshares": { - "resendInvitation": "Resend invitation email", - "notifyByEmail": "Notify by email", - "notifyGroupMembers": "Notify group members", - "notifyRateLimited": "Too many notifications for this recipient — try again later.", - "notifyFailed": "Could not send notification.", - "removeAccess": "Remove access", - "copyLink": "Copy link", - "deleteLink": "Delete link" - }, - "nav": { - "files": "Files", - "shared": "My shares", - "sharedwithme": "Shared with me", - "recent": "Recent", - "favorites": "Favorites", - "photos": "Photos", - "music": "Music", - "trash": "Trash" - }, - "photos": { - "empty_state": "No photos yet", - "empty_hint": "Upload images or videos to see them here", - "items_selected": "selected", - "view_daily": "Day", - "view_monthly": "Month", - "view_yearly": "Year" - }, - "music": { - "create_playlist": "Create Playlist", - "playlists": "Playlists", - "no_playlists": "No playlists yet", - "empty_hint": "Create your first playlist to start organizing your music", - "select_playlist": "Select a playlist", - "select_hint": "Choose a playlist from the sidebar or create a new one", - "add_tracks": "Add Tracks", - "add_to_playlist": "Add to Playlist", - "add": "Add", - "added": "Added!", - "added_to_playlist": "added to playlist", - "load_error": "Error loading playlists", - "add_error": "Could not add tracks to playlist", - "no_playlists_yet": "No playlists yet. Create one first!", - "selected_files": "Selected:", - "no_tracks": "No tracks in this playlist", - "unknown_artist": "Unknown Artist", - "unknown_title": "Unknown", - "confirm_delete": "Delete this playlist?", - "playlist_name": "Playlist name", - "create": "Create", - "delete": "Delete", - "share": "Share", - "edit": "Edit", - "play_all": "Play All", - "shuffle": "Shuffle", - "repeat": "Repeat", - "repeat_one": "Repeat One", - "queue": "Queue", - "queue_empty": "Queue is empty", - "not_playing": "Not playing", - "play": "Play", - "pause": "Pause", - "previous": "Previous", - "next": "Next", - "volume": "Volume", - "mute": "Mute", - "unmute": "Unmute", - "title": "Title", - "artist": "Artist", - "album": "Album", - "tracks": "tracks", - "share_with_user": "User ID or email", - "playback_error": "Playback failed", - "error": "Error", - "remove": "Remove", - "track_removed": "Track removed", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "remove_share": "Remove share", - "can_write": "Can edit", - "read_only": "Read only", - "public": "Public", - "private": "Private", - "toggle_public": "Visibility", - "make_public": "Make public", - "make_private": "Make private", - "set_cover": "Set cover", - "cover_updated": "Cover updated", - "search_audio": "Search audio files…", - "no_audio_files": "No audio files found", - "selected": "selected", - "loading": "Loading…", - "search_error": "Could not load audio files", - "adding": "Adding…" - }, - "actions": { - "search": "Search files...", - "new_folder": "New folder", - "upload": "Upload", - "upload_files": "Upload files", - "upload_folder": "Upload folder", - "upload.uploading": "Uploading...", - "upload.complete": "{count} / {total} uploaded", - "upload.files": "files", - "rename": "Rename", - "move": "Move to...", - "move_to": "Move to", - "delete": "Delete", - "download": "Download", - "view": "View", - "cancel": "Cancel", - "confirm": "Confirm", - "share": "Share", - "favorite": "Add to favorites", - "unfavorite": "Remove from favorites", - "copy": "Copy", - "notify": "Notify", - "send": "Send", - "clear_recent": "Clear recent", - "logout": "Log out", - "create": "Create", - "search_btn": "Search", - "close": "Close", - "delete_permanently": "Delete permanently", - "empty_trash": "Empty trash", - "open_parent_folder": "Go to parent folder", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Appearance", - "about": "About OxiCloud", - "about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private.", - "admin_panel": "Admin Panel", - "profile": "My Profile", - "role_user": "User", - "theme": { - "light": "Light", - "dark": "Dark", - "auto": "Like OS" - }, - "manage_groups": "Manage groups" - }, - "share": { - "dialogTitle": "Share Link", - "linkLabel": "Share Link:", - "copyLink": "Copy", - "permissions": "Permissions:", - "permissionRead": "Read", - "permissionWrite": "Write", - "permissionReshare": "Reshare", - "password": "Password Protection:", - "generatePassword": "Generate", - "expiration": "Expiration Date:", - "update": "Update Share", - "remove": "Remove Share", - "notifyTitle": "Send Notification", - "notifyEmailLabel": "Email Address:", - "notifyMessageLabel": "Message (optional):", - "notifySend": "Send Notification", - "shareWithOthers": "Share with others", - "sharePublicly": "Share publicly", - "shareSettings": "Sharing settings", - "shareCopied": "Link copied to clipboard", - "shareCreated": "Share link created successfully", - "shareUpdated": "Share settings updated successfully", - "shareRemoved": "Share removed successfully", - "inviteByEmail": "Invite by email — invitation will be sent", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Share Link", - "share_linkLabel": "Share Link:", - "share_copyLink": "Copy", - "share_permissions": "Permissions:", - "share_permissionRead": "Read", - "share_permissionWrite": "Write", - "share_permissionReshare": "Reshare", - "share_password": "Password Protection:", - "share_generatePassword": "Generate", - "share_expiration": "Expiration Date:", - "share_update": "Update Share", - "share_remove": "Remove Share", - "share_notifyTitle": "Send Notification", - "share_notifyEmailLabel": "Email Address:", - "share_notifyMessageLabel": "Message (optional):", - "share_notifySend": "Send Notification", - "shared": { - "backToFiles": "Back to Files", - "pageTitle": "Shared Resources", - "pageDescription": "Manage your shared files and folders", - "filterType": "Type:", - "filterAll": "All", - "filterFiles": "Files", - "filterFolders": "Folders", - "sortBy": "Sort by:", - "sortByName": "Name", - "sortByDate": "Date shared", - "sortByExpiration": "Expiration", - "search": "Search", - "colName": "Name", - "colType": "Type", - "colDateShared": "Date Shared", - "colExpiration": "Expiration", - "colPermissions": "Permissions", - "colPassword": "Password", - "colActions": "Actions", - "emptyStateTitle": "No shared resources yet", - "emptyStateDesc": "When you share files or folders, they will appear here", - "goToFiles": "Go to Files", - "typeFile": "File", - "typeFolder": "Folder", - "noExpiration": "No expiration", - "hasPassword": "Yes", - "noPassword": "No", - "editShare": "Edit Share", - "notifyShare": "Notify Someone", - "copyLink": "Copy Link", - "removeShare": "Remove Share", - "linkCopied": "Link copied to clipboard!", - "linkCopyFailed": "Failed to copy link", - "itemUpdated": "Share settings updated successfully", - "itemRemoved": "Share removed successfully", - "invalidEmail": "Please enter a valid email address", - "notificationSent": "Notification sent successfully", - "notificationFailed": "Failed to send notification", - "shared_backToFiles": "Back to Files", - "shared_pageTitle": "Shared Resources", - "shared_pageDescription": "Manage your shared files and folders", - "shared_filterType": "Type:", - "shared_filterAll": "All", - "shared_filterFiles": "Files", - "shared_filterFolders": "Folders", - "shared_sortBy": "Sort by:", - "shared_sortByName": "Name", - "shared_sortByDate": "Date shared", - "shared_sortByExpiration": "Expiration", - "shared_search": "Search", - "shared_colName": "Name", - "shared_colType": "Type", - "shared_colDateShared": "Date Shared", - "shared_colExpiration": "Expiration", - "shared_colPermissions": "Permissions", - "shared_colPassword": "Password", - "shared_colActions": "Actions", - "shared_emptyStateTitle": "No shared resources yet", - "shared_emptyStateDesc": "When you share files or folders, they will appear here", - "shared_goToFiles": "Go to Files", - "shared_typeFile": "File", - "shared_typeFolder": "Folder", - "shared_noExpiration": "No expiration", - "shared_hasPassword": "Yes", - "shared_noPassword": "No", - "shared_editShare": "Edit Share", - "shared_notifyShare": "Notify Someone", - "shared_copyLink": "Copy Link", - "shared_removeShare": "Remove Share", - "shared_linkCopied": "Link copied to clipboard!", - "shared_linkCopyFailed": "Failed to copy link", - "shared_itemUpdated": "Share settings updated successfully", - "shared_itemRemoved": "Share removed successfully", - "shared_invalidEmail": "Please enter a valid email address", - "shared_notificationSent": "Notification sent successfully", - "shared_notificationFailed": "Failed to send notification" - }, - "files": { - "name": "Name", - "type": "Type", - "size": "Size", - "modified": "Modified", - "no_files": "No files in this folder", - "empty_hint": "Upload files or create folders to get started", - "loading": "Loading files…", - "view_grid": "Grid view", - "view_list": "List view", - "file_types": { - "document": "Document", - "image": "Image", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Text", - "folder": "Folder", - "spreadsheet": "Spreadsheet", - "presentation": "Presentation", - "archive": "Archive", - "installer": "Installer", - "code": "Code" - }, - "owner": "Owner" - }, - "dialogs": { - "rename_folder": "Rename folder", - "rename_file": "Rename file", - "new_name": "New name", - "new_folder_title": "New folder", - "folder_name": "Folder name", - "folder_placeholder": "My folder", - "rename_title": "Rename", - "move_file": "Move file", - "move_folder": "Move folder", - "select_destination": "Select destination folder:", - "select_this_folder": "Select this folder", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "root": "Root", - "delete_confirmation": "Are you sure you want to delete", - "and_contents": "and all its contents", - "no_undo": "This action cannot be undone", - "confirm_title": "Confirm action", - "confirm_delete": "Move to trash", - "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", - "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", - "confirm_permanent_delete": "Delete permanently", - "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", - "confirm_empty_trash": "Empty trash", - "confirm_delete_share": "Delete share link", - "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", - "share_file": "Share File", - "share_folder": "Share Folder", - "existing_shares": "Existing Shares", - "share_options": "Share Options", - "password": "Password", - "expiration": "Expiration", - "permissions": "Permissions", - "generated_link": "Generated Link", - "notify": "Send Notification", - "recipient": "Recipient", - "message": "Message", - "move_to_home": "Move to Home folder" - }, - "dropzone": { - "drag_files": "Drag files here or click to select", - "drop_files": "Drop files to upload" - }, - "permissions": { - "read": "Read", - "write": "Write", - "reshare": "Reshare" - }, - "errors": { - "file_not_found": "File not found", - "folder_not_found": "Folder not found", - "delete_error": "Error deleting", - "upload_error": "Error uploading file", - "rename_error": "Error renaming", - "move_error": "Error moving", - "empty_name": "Name cannot be empty", - "name_exists": "A file or folder with that name already exists", - "generic_error": "An error has occurred", - "group_name_invalid": "Group name must match the email-prefix format (letters, digits, dot, dash, underscore; 1–64 chars).", - "group_cycle": "This member would create a circular group reference.", - "group_depth_exceeded": "This nesting depth exceeds the maximum allowed (8).", - "group_virtual_immutable": "The 'Internal' group is system-managed and cannot be modified.", - "group_not_found": "Group not found.", - "group_name_taken": "A group with this name already exists." - }, - "breadcrumb": { - "home": "Home" - }, - "trash": { - "empty_trash": "Empty Trash", - "empty_state": "Trash is empty", - "original_location": "Original location", - "deleted_date": "Deletion date", - "remaining": "Remaining", - "actions": "Actions", - "restore": "Restore", - "delete_permanently": "Delete permanently", - "empty_confirm": "Are you sure you want to empty the trash? This will permanently delete all items.", - "groupby": { - "remaining_days": "Remaining days", - "trashed_time": "Trashed time" - } - }, - "daysRemaining": { - "expired": "Expired", - "today": "Today", - "tomorrow": "Tomorrow", - "inDays": "{{count}} days" - }, - "expiryChip": { - "never": "Never expires", - "expired": "Expired", - "today": "Expires today", - "tomorrow": "Expires tomorrow", - "inDays": "Expires in {{count}} days", - "onDate": "Expires {{date}}" - }, - "auth": { - "login_title": "Sign in", - "username": "Username", - "username_placeholder": "Enter your username", - "login_identifier": "Username or email", - "login_identifier_placeholder": "Enter your username or email", - "password": "Password", - "password_placeholder": "Enter your password", - "login_button": "Sign in", - "no_account": "Don't have an account?", - "register": "Sign up", - "admin_setup": "First time?", - "setup": "Setup administrator", - "register_title": "Create account", - "email": "Email", - "email_placeholder": "Enter your email", - "confirm_password": "Confirm password", - "confirm_password_placeholder": "Confirm your password", - "register_button": "Create account", - "have_account": "Already have an account?", - "login": "Sign in", - "setup_title": "Initial setup", - "setup_step1": "Admin", - "setup_step2": "System", - "setup_step3": "Complete", - "admin_username": "Admin username", - "admin_email": "Admin email", - "admin_password": "Admin password", - "create_admin": "Create administrator", - "back_to_login": "Already set up?", - "admin_success": "Administrator account created successfully! You can now sign in.", - "account_success": "Account created successfully! You can now sign in.", - "passwords_mismatch": "Passwords do not match", - "admin_create_error": "Error creating administrator account", - "or": "or", - "sso_login": "Sign in with SSO", - "sso_login_provider": "Sign in with {{provider}}", - "magicLinkHint": "No password? Enter your email and we'll send you a one-time sign-in link.", - "magicLinkEmailLabel": "Email address", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "Send sign-in link", - "magicLinkSent": "If an account exists for that email, a sign-in link has been sent. Check your inbox.", - "magicLinkUnavailable": "Sign-in by email is not available on this server.", - "magicLinkNetworkError": "Could not reach the server: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Storage", - "calculating": "Calculating...", - "used": "{{percentage}}% used ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "This file type cannot be previewed.", - "download_file": "Download file", - "zoom_in": "Zoom in", - "zoom_out": "Zoom out", - "zoom_reset": "Reset zoom" - }, - "language_selector": { - "title": "Welcome!", - "subtitle": "Select your language to continue", - "continue": "Continue", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "No favorites yet", - "empty_hint": "Star files or folders to add them to your favorites", - "add": "Add to favorites", - "remove": "Remove from favorites", - "added_title": "Added to favorites", - "added_msg": "added to favorites", - "removed_title": "Removed from favorites", - "removed_msg": "removed from favorites" - }, - "recent": { - "title": "Recent", - "clear": "Clear recent", - "accessed": "Accessed", - "empty_state": "No recent files", - "empty_hint": "Files you open will appear here", - "loadMore": "Load more" - }, - "notifications": { - "file_renamed": "File renamed", - "file_renamed_to": "File renamed to \"{{name}}\"", - "folder_renamed": "Folder renamed", - "folder_renamed_to": "Folder renamed to \"{{name}}\"", - "file_uploaded": "File uploaded", - "file_deleted": "File moved to trash", - "folder_deleted": "Folder moved to trash", - "item_deleted_permanently": "Item permanently deleted", - "trash_emptied": "Trash emptied successfully", - "title": "Notifications", - "empty": "No notifications", - "link_created": "Link created", - "share_success": "Shared link created successfully", - "upload_files_section_title": "Upload not available here", - "upload_files_section_body": "Go to the Files section to upload files" - }, - "batch": { - "one_selected": "1 item selected", - "n_selected": "{{count}} items selected", - "confirm_delete": "Are you sure you want to move {{count}} items to trash?", - "move_title": "Move {{count}} item(s)", - "add_favorites": "Add to favorites", - "move_copy": "Move or copy" - }, - "admin": { - "page_title": "Admin Panel", - "back_to_app": "Back to OxiCloud", - "loading": "Loading…", - "access_denied": "Access Denied", - "access_denied_desc": "Administrator privileges required to access this panel.", - "sign_in": "Sign in", - "tab_dashboard": "Dashboard", - "tab_users": "Users", - "tab_oidc": "SSO / OIDC", - "total_users": "Total Users", - "active_users": "Active Users", - "admins": "Admins", - "version": "Version", - "storage_overview": "Storage Overview", - "used": "Used", - "total_quota": "Total Quota", - "usage_pct": "Usage %", - "users_over_80": "Users >80% quota", - "users_over_quota": "Users over quota", - "system": "System", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quotas", - "enabled": "Enabled", - "disabled": "Disabled", - "active": "Active", - "off": "Off", - "allow_registration": "Allow public self-registration", - "registration_warning": "Public registration is disabled. Only admins can create new users.", - "user_management": "User Management", - "create_user": "Create User", - "col_user": "User", - "col_role": "Role", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Storage", - "col_last_login": "Last Login", - "col_actions": "Actions", - "loading_users": "Loading users…", - "failed_load_users": "Failed to load users", - "no_users_found": "No users found", - "showing_users": "Showing {{from}}-{{to}} of {{total}}", - "prev": "Prev", - "next": "Next", - "inactive": "Inactive", - "you_badge": "(you)", - "local": "Local", - "never": "Never", - "just_now": "Just now", - "minutes_ago": "{{n}}m ago", - "hours_ago": "{{n}}h ago", - "days_ago": "{{n}}d ago", - "edit_quota_title": "Edit quota", - "reset_password_title": "Reset password", - "toggle_role_title": "Toggle role", - "deactivate_title": "Deactivate", - "activate_title": "Activate", - "delete_title": "Delete", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "Enable SSO Authentication", - "provider_name": "Provider Name", - "issuer_url": "Issuer URL", - "issuer_url_hint": "OpenID Connect issuer URL of your identity provider", - "auto_discover": "Auto-discover", - "discovering": "Discovering…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Leave empty to keep current value", - "secret_configured": "A client secret is already configured", - "callback_url": "Callback URL", - "callback_url_hint": "(register in your IdP)", - "advanced_settings": "Advanced Settings", - "scopes": "Scopes", - "auto_provision": "Auto-provision users on first login", - "admin_groups": "Admin Groups", - "admin_groups_hint": "Comma-separated OIDC group names that map to admin role", - "disable_password": "Disable password login (OIDC only)", - "password_warning": "This will prevent ALL password-based logins!", - "test_btn": "Test", - "save_btn": "Save", - "saving": "Saving…", - "settings_saved": "Settings saved — OIDC is now {{status}}", - "quota_modal_title": "Update Storage Quota", - "quota_user_label": "User:", - "new_quota": "New Quota", - "quota_unlimited_hint": "Set to 0 for unlimited", - "cancel": "Cancel", - "create_user_title": "Create New User", - "username_label": "Username", - "username_placeholder": "johndoe", - "username_hint": "3–32 characters", - "password_label": "Password", - "password_placeholder": "Min 8 characters", - "email_label": "Email", - "email_optional": "(optional)", - "email_placeholder": "user@example.com (auto-generated if empty)", - "role_label": "Role", - "role_user": "User", - "role_admin": "Admin", - "quota_label": "Quota", - "creating": "Creating…", - "reset_pw_title": "Reset Password", - "new_password_label": "New Password", - "resetting": "Resetting…", - "reset_btn": "Reset", - "confirm_role_change": "Change role to {{role}}?", - "confirm_deactivate": "Are you sure you want to deactivate this user?", - "confirm_activate": "Are you sure you want to activate this user?", - "confirm_delete_user": "DELETE user \"{{name}}\"? This cannot be undone!", - "confirm_action": "Confirm Action", - "confirm_yes": "Confirm", - "confirm_no": "Cancel", - "error_username_short": "Username must be at least 3 characters", - "error_password_short": "Password must be at least 8 characters", - "error_generic": "Failed", - "error_network": "Network error: {{message}}", - "error_create_user": "Failed to create user", - "tab_storage": "Storage", - "storage_title": "Storage Backend", - "storage_current_backend": "Active Backend", - "storage_total_blobs": "Total Blobs", - "storage_total_size": "Total Size", - "storage_dedup_ratio": "Dedup Ratio", - "storage_backend": "Backend Type", - "storage_local": "Local Filesystem", - "storage_s3": "S3-Compatible", - "storage_provider_preset": "Provider Preset", - "storage_preset_custom": "Custom", - "storage_endpoint_url": "Endpoint URL", - "storage_endpoint_hint": "Leave empty for Amazon S3 default", - "storage_bucket": "Bucket", - "storage_region": "Region", - "storage_access_key": "Access Key ID", - "storage_secret_key": "Secret Access Key", - "storage_secret_configured": "A secret key is already configured", - "storage_key_placeholder": "Leave empty to keep current value", - "storage_path_style": "Force Path Style", - "storage_path_style_hint": "Required for MinIO and some S3-compatible providers", - "storage_test_connection": "Test Connection", - "storage_test_success": "Connection successful", - "storage_test_failure": "Connection failed", - "storage_save": "Save", - "storage_saved": "Storage settings saved successfully", - "storage_migration": "Backend Migration", - "storage_migration_coming_soon": "Backend migration will be available in a future update.", - "migration_status_label": "Status:", - "migration_start": "Start Migration", - "migration_pause": "Pause", - "migration_resume": "Resume", - "migration_verify": "Verify Integrity", - "migration_complete": "Finalize", - "migration_started": "Migration started", - "migration_paused_msg": "Migration paused", - "migration_resumed_msg": "Migration resumed", - "migration_completed_msg": "Migration finalized. Restart the server to use the new backend.", - "migration_verifying": "Verifying…", - "migration_verify_passed": "Verification passed", - "migration_verify_failed": "Verification failed", - "migration_failed_blobs": "failed blobs", - "testing": "Testing…", - "tab_plugins": "Plugins", - "plugins_title": "Plugins", - "plugins_disabled": "Plugins are disabled on this server. Set OXICLOUD_ENABLE_PLUGINS=true (and build with the \"plugins\" feature) to manage WASM plugins here.", - "plugins_install_title": "Install a plugin", - "plugins_install_intro": "Upload a plugin bundle (.zip) containing plugin.toml and its compiled WebAssembly module (.wasm). The manifest is validated and the module is probed before installation.", - "plugins_bundle_label": "Plugin bundle (.zip)", - "plugins_install": "Install plugin", - "plugins_installed_title": "Installed plugins", - "plugins_col_name": "Name", - "plugins_col_id": "ID", - "plugins_col_version": "Version", - "plugins_col_events": "Events", - "plugins_col_status": "Status", - "plugins_col_actions": "Actions", - "plugins_loading": "Loading plugins…", - "plugins_none": "No plugins installed.", - "plugins_enabled": "Enabled", - "plugins_disabled_badge": "Disabled", - "plugins_enable": "Enable", - "plugins_disable": "Disable", - "plugins_delete": "Delete", - "plugins_confirm_delete": "Delete plugin \"{{name}}\"? Its files will be removed from the server.", - "plugins_installing": "Installing…", - "plugins_installed": "Installed {{name}}.", - "plugins_install_missing_bundle": "Select a plugin bundle (.zip).", - "plugins_details": "Logs & details", - "plugins_back": "Back to plugins", - "plugins_retention_title": "Log retention", - "plugins_retention_intro": "Rotated log segments older than the retention window, or beyond the size cap, are pruned on a schedule.", - "plugins_retention_days": "Retention (days)", - "plugins_retention_max_mb": "Max log size (MB)", - "plugins_retention_save": "Save retention", - "plugins_retention_saved": "Retention saved.", - "plugins_retention_invalid": "Enter non-negative numbers.", - "plugins_logs_title": "Logs", - "plugins_logs_level_all": "All levels", - "plugins_logs_search": "Search messages…", - "plugins_logs_live": "Live", - "plugins_logs_clear": "Clear", - "plugins_logs_confirm_clear": "Clear all logs for this plugin?", - "plugins_logs_none": "No log entries.", - "plugins_logs_col_time": "Time", - "plugins_logs_col_level": "Level", - "plugins_logs_col_kind": "Kind", - "plugins_logs_col_invocation": "Invocation", - "plugins_logs_col_message": "Message", - "plugins_logs_showing": "Showing {{from}}–{{to}} of {{total}}", - "tab_smtp": "SMTP", - "smtp_title": "Outbound Email (SMTP)", - "smtp_intro": "SMTP is configured exclusively via environment variables (OXICLOUD_SMTP_*). The values below are read from the running server — to change them, edit the environment and restart OxiCloud.", - "smtp_enabled_label": "Status", - "smtp_enabled": "Enabled", - "smtp_disabled": "Disabled (host unset)", - "smtp_test_title": "Send a test email", - "smtp_test_intro": "Sends a hardcoded diagnostic message to the recipient below and reports the SMTP server's response so you can correlate it with your relay logs.", - "smtp_test_to": "Recipient address", - "smtp_send_test": "Send test email", - "smtp_sending": "Sending…", - "smtp_sent": "Test email sent.", - "smtp_send_failed": "Send failed.", - "smtp_server_code": "Server replied", - "smtp_test_missing_to": "Enter a recipient address.", - "smtp_not_configured": "SMTP is not configured on this server." - }, - "profile": { - "page_title": "Profile", - "back_to_app": "Back to OxiCloud", - "loading": "Loading…", - "not_authenticated": "Not Authenticated", - "not_authenticated_desc": "Please sign in to view your profile.", - "sign_in": "Sign in", - "role_admin": "Administrator", - "role_user": "User", - "account_details": "Account Details", - "username": "Username", - "email": "Email", - "role": "Role", - "last_login": "Last Login", - "storage": "Storage", - "used": "Used", - "quota": "Quota", - "usage": "Usage", - "unlimited": "Unlimited", - "app_passwords": "App Passwords", - "app_pw_desc": "Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.", - "app_pw_label_placeholder": "Label (e.g. Thunderbird, macOS)", - "generate": "Generate", - "generating": "Generating…", - "new_password_for": "New password for", - "copy_warning": "Copy this password now. You won't be able to see it again.", - "copy_to_clipboard": "Copy to clipboard", - "col_label": "Label", - "col_created": "Created", - "col_last_used": "Last Used", - "col_status": "Status", - "active": "Active", - "revoked": "Revoked", - "revoke_title": "Revoke", - "no_app_passwords": "No app passwords yet.", - "client_sessions": "Client sessions", - "client_sessions_desc": "Auto-generated when you connect a Nextcloud-compatible client.", - "col_client": "Client", - "never": "Never", - "just_now": "Just now", - "minutes_ago": "{{n}} min ago", - "hours_ago": "{{n}}h ago", - "days_ago": "{{n}} days ago", - "edit_profile": "Edit Profile", - "edit_oidc_managed": "To change your information (name, first name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.", - "username_claim_hint": "2–64 characters, letters/digits/dot/dash/underscore. Once chosen, the username can't be changed (DAV/NextCloud clients depend on it).", - "username_already_claimed": "Username is set and can't be changed (DAV/NextCloud clients depend on it).", - "given_name": "First name", - "family_name": "Last name", - "notify_on_share": "Email me when someone shares with me", - "notify_on_share_hint": "When unchecked, shares still appear in your account — you just won't get an email about them.", - "save_profile": "Save changes", - "profile_saved": "Profile updated", - "profile_no_changes": "No changes to save.", - "profile_save_failed": "Save failed", - "username_taken_error": "That username is already taken.", - "username_immutable_error": "Your username is already set and can't be changed here. Contact an administrator if you need a rename.", - "change_password": "Change Password", - "current_password": "Current Password", - "new_password": "New Password", - "min_8_chars": "At least 8 characters", - "confirm_password": "Confirm New Password", - "update_password": "Update Password", - "updating": "Updating…", - "password_updated": "Password updated successfully", - "passwords_no_match": "Passwords do not match", - "password_too_short": "Password must be at least 8 characters", - "password_change_failed": "Failed to change password", - "error_network": "Network error: {{message}}", - "error_label_required": "Please enter a label", - "error_create_pw": "Failed to create app password", - "confirm_revoke": "Revoke app password \"{{label}}\"? Clients using this password will stop working.", - "error_revoke": "Failed to revoke app password", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Uploading...", - "files": "files", - "complete": "{{count}} / {{total}} uploaded" - }, - "storage_quota_exceeded": "Storage quota exceeded", - "sharedwithme": { - "pageTitle": "Shared with me", - "pageDescription": "Files and folders others have shared with you", - "emptyStateTitle": "Nothing shared with you yet", - "emptyStateDesc": "Items shared with you by other users will appear here", - "loadMore": "Load more", - "sharedBy": "Shared by", - "colName": "Name", - "colType": "Type", - "colSharedBy": "Shared by", - "colDate": "Date shared", - "colPermissions": "Permissions" - }, - "groupby": { - "none": "None", - "byFiles": "By files", - "sharedWith": "Shared with", - "title": "Group by", - "type": "Type", - "type.folders": "Folders", - "owner": "Owner", - "shareDate": "Share date", - "favoriteDate": "Favorite date", - "accessedAt": "Accessed date", - "modifiedAt": "Modified date", - "createdAt": "Created date", - "size": "Size", - "justAdded": "New" - }, - "dateBucket": { - "today": "Today", - "last7days": "Last 7 days", - "last30days": "Last 30 days" - }, - "groups": { - "title": "Manage groups", - "create_button": "Create group", - "create_dialog_title": "New group", - "edit_dialog_title": "Rename group", - "name_label": "Name", - "name_placeholder": "engineering", - "description_label": "Description (optional)", - "members_section": "Members", - "members_loading": "Loading members…", - "members_empty": "No members", - "add_member_placeholder": "Add a user or group…", - "no_members": "No members yet.", - "remove_member": "Remove", - "delete_group": "Delete group", - "delete_confirm": "Delete the group \"{name}\"? Grants referencing this group will be revoked.", - "empty_state": "No groups yet.", - "load_more": "Load more", - "back_to_list": "Back", - "loading": "Loading…", - "virtual_badge": "System", - "member_count_zero": "no members", - "member_count_one": "1 member", - "member_count_other": "{count} members", - "delete_confirm_label": "Type the group name to confirm:", - "delete_confirm_mismatch": "Type the group name exactly to confirm.", - "virtual_internal_name": "Internal", - "virtual_internal_explanation": "Every internal user on this server" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/es.json b/static/locales/es.json deleted file mode 100644 index 3a1027ea..00000000 --- a/static/locales/es.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Este enlace de inicio de sesión ya no es válido", - "expired_body": "Es posible que el enlace haya expirado o ya se haya utilizado. Podemos enviarte uno nuevo — llegará a tu bandeja de entrada en unos segundos.", - "resend_to": "Enviar un nuevo enlace a {{email}}", - "generic_unavailable": "Este enlace de inicio de sesión ya no es válido. Es posible que ya se haya usado o que haya expirado. Solicita uno nuevo desde la página de inicio de sesión.", - "service_unavailable": "El inicio de sesión por enlace mágico no está habilitado en este servidor.", - "internal_error": "Algo salió mal al iniciar sesión. Por favor, inténtalo de nuevo.", - "resend_failure": "Algo salió mal al enviar el enlace. Por favor, inténtalo de nuevo.", - "cross_browser_title": "¿Continuar el inicio de sesión en este dispositivo?", - "cross_browser_body": "Has abierto este enlace de inicio de sesión en un navegador o dispositivo diferente del que lo solicitó.", - "cross_browser_warning": "Si solicitaste este enlace, es seguro continuar. Si no, cierra esta página — hacer clic en Continuar iniciaría sesión a otra persona en tu cuenta.", - "cross_browser_continue": "Continuar e iniciar sesión", - "resend_confirmation_title": "Revisa tu bandeja de entrada", - "resend_confirmation_body": "Si el enlace de inicio de sesión pertenecía a una cuenta activa, se acaba de enviar uno nuevo. Por favor, revisa tu bandeja de entrada.", - "return_link": "Volver a OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", - "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nÁbrelo haciendo clic en el enlace de abajo:\n{{link}}\n\nEl enlace es de un solo uso y expira en {{ttl_hours}} horas.\nSi no esperabas esta invitación, puedes ignorar este mensaje.\n\n— OxiCloud" - }, - "login": { - "subject": "Inicia sesión en OxiCloud", - "body": "Hola,\n\nUsa el enlace de abajo para iniciar sesión en OxiCloud. El enlace es de un solo uso y expira en {{ttl_minutes}} minutos. Ábrelo en el mismo dispositivo donde lo solicitaste.\n\n{{link}}\n\nSi no solicitaste este enlace de inicio de sesión, puedes ignorar este mensaje — no se necesita ninguna acción adicional.\n\n— OxiCloud" - }, - "kind_file": "archivo", - "kind_folder": "carpeta", - "english_fallback_divider": "--- Versión en inglés a continuación ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} ha compartido un {{kind}} contigo en OxiCloud", - "body": "{{inviter_full}} ha compartido un {{kind}} contigo en OxiCloud.\n\nAbre OxiCloud para ver tu nuevo recurso compartido:\n{{login_link}}\n\nPuede que tengas más recursos compartidos nuevos de {{inviter}} — inicia sesión para ver todos tus elementos compartidos.\n\n— OxiCloud\n\nRecibes este mensaje porque tienes una cuenta de OxiCloud y la preferencia de notificación de recursos compartidos está activada. Puedes desactivarla en tu perfil (Enviarme un correo cuando alguien comparta conmigo)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Sistema de almacenamiento en la nube minimalista" - }, - "nav": { - "files": "Archivos", - "shared": "Compartidos", - "recent": "Recientes", - "favorites": "Favoritos", - "photos": "Fotos", - "music": "Música", - "trash": "Papelera", - "sharedwithme": "Compartidos conmigo" - }, - "photos": { - "empty_state": "Aún no hay fotos", - "empty_hint": "Sube imágenes o videos para verlos aquí", - "items_selected": "seleccionados", - "view_daily": "Día", - "view_monthly": "Mes", - "view_yearly": "Año" - }, - "music": { - "create_playlist": "Crear Lista", - "playlists": "Listas", - "no_playlists": "Sin listas aún", - "empty_hint": "Crea tu primera lista para empezar a organizar tu música", - "select_playlist": "Selecciona una lista", - "select_hint": "Elige una lista de la barra lateral o crea una nueva", - "add_tracks": "Añadir Pistas", - "no_tracks": "No hay pistas en esta lista", - "unknown_artist": "Artista Desconocido", - "unknown_title": "Desconocido", - "confirm_delete": "¿Eliminar esta lista?", - "playlist_name": "Nombre de la lista", - "create": "Crear", - "delete": "Eliminar", - "share": "Compartir", - "edit": "Editar", - "play_all": "Reproducir Todo", - "shuffle": "Aleatorio", - "repeat": "Repetir", - "repeat_one": "Repetir Una", - "queue": "Cola", - "queue_empty": "Cola vacía", - "not_playing": "No reproduciendo", - "play": "Reproducir", - "pause": "Pausar", - "previous": "Anterior", - "next": "Siguiente", - "volume": "Volumen", - "mute": "Silenciar", - "unmute": "Activar sonido", - "title": "Título", - "artist": "Artista", - "album": "Álbum", - "tracks": "pistas", - "add": "Añadir", - "added": "¡Añadido!", - "added_to_playlist": "añadido a la lista", - "add_to_playlist": "Añadir a playlist", - "load_error": "Error al cargar listas", - "add_error": "No se pudieron añadir las pistas", - "no_playlists_yet": "No hay listas aún. ¡Crea una primero!", - "selected_files": "Seleccionados:", - "share_with_user": "ID de usuario o email", - "playback_error": "Error de reproducción", - "error": "Error", - "remove": "Eliminar", - "track_removed": "Pista eliminada", - "manage_shares": "Gestionar compartidos", - "no_shares": "Sin compartidos aún", - "remove_share": "Eliminar compartido", - "can_write": "Puede editar", - "read_only": "Solo lectura", - "public": "Pública", - "private": "Privada", - "toggle_public": "Visibilidad", - "make_public": "Hacer pública", - "make_private": "Hacer privada", - "set_cover": "Establecer portada", - "cover_updated": "Portada actualizada", - "search_audio": "Buscar archivos de audio…", - "no_audio_files": "No se encontraron archivos de audio", - "selected": "seleccionados", - "loading": "Cargando…", - "search_error": "No se pudieron cargar los archivos de audio", - "adding": "Añadiendo…" - }, - "share": { - "dialogTitle": "Compartir Enlace", - "linkLabel": "Enlace compartido:", - "copyLink": "Copiar", - "permissions": "Permisos:", - "permissionRead": "Lectura", - "permissionWrite": "Escritura", - "permissionReshare": "Recompartir", - "password": "Protección con contraseña:", - "generatePassword": "Generar", - "expiration": "Fecha de caducidad:", - "update": "Actualizar compartido", - "remove": "Eliminar compartido", - "notifyTitle": "Enviar notificación", - "notifyEmailLabel": "Dirección de correo:", - "notifyMessageLabel": "Mensaje (opcional):", - "notifySend": "Enviar notificación", - "shareWithOthers": "Compartir con otros", - "sharePublicly": "Compartir públicamente", - "shareSettings": "Configuración de compartido", - "shareCopied": "Enlace copiado al portapapeles", - "shareCreated": "Enlace compartido creado correctamente", - "shareUpdated": "Configuración de compartido actualizada", - "shareRemoved": "Compartido eliminado correctamente", - "inviteByEmail": "Invitar por correo — se enviará una invitación", - "directoryUnavailable": "Directorio de usuarios no disponible", - "linkNamePlaceholder": "Nombre del enlace (opcional)", - "newLink": "Nuevo enlace", - "noExpiry": "Sin caducidad", - "pending": "Pendiente", - "people": "Personas", - "publicLinks": "Enlaces públicos", - "role": { - "canEdit": "Puede editar", - "canManage": "Puede gestionar", - "canView": "Puede ver" - }, - "searchPlaceholder": "Buscar personas…", - "shareOf": "Compartir:", - "sharedLink": "Enlace compartido" - }, - "share_dialogTitle": "Compartir Enlace", - "share_linkLabel": "Enlace compartido:", - "share_copyLink": "Copiar", - "share_permissions": "Permisos:", - "share_permissionRead": "Lectura", - "share_permissionWrite": "Escritura", - "share_permissionReshare": "Recompartir", - "share_password": "Protección con contraseña:", - "share_generatePassword": "Generar", - "share_expiration": "Fecha de caducidad:", - "share_update": "Actualizar compartido", - "share_remove": "Eliminar compartido", - "share_notifyTitle": "Enviar notificación", - "share_notifyEmailLabel": "Dirección de correo:", - "share_notifyMessageLabel": "Mensaje (opcional):", - "share_notifySend": "Enviar notificación", - "shared": { - "backToFiles": "Volver a Archivos", - "pageTitle": "Recursos Compartidos", - "pageDescription": "Administra tus archivos y carpetas compartidos", - "filterType": "Tipo:", - "filterAll": "Todos", - "filterFiles": "Archivos", - "filterFolders": "Carpetas", - "sortBy": "Ordenar por:", - "sortByName": "Nombre", - "sortByDate": "Fecha compartido", - "sortByExpiration": "Caducidad", - "search": "Buscar", - "colName": "Nombre", - "colType": "Tipo", - "colDateShared": "Fecha compartido", - "colExpiration": "Caducidad", - "colPermissions": "Permisos", - "colPassword": "Contraseña", - "colActions": "Acciones", - "emptyStateTitle": "Aún no hay recursos compartidos", - "emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", - "goToFiles": "Ir a Archivos", - "typeFile": "Archivo", - "typeFolder": "Carpeta", - "noExpiration": "Sin caducidad", - "hasPassword": "Sí", - "noPassword": "No", - "editShare": "Editar compartido", - "notifyShare": "Notificar a alguien", - "copyLink": "Copiar enlace", - "removeShare": "Eliminar compartido", - "linkCopied": "¡Enlace copiado al portapapeles!", - "linkCopyFailed": "Error al copiar el enlace", - "itemUpdated": "Configuración de compartido actualizada", - "itemRemoved": "Compartido eliminado correctamente", - "invalidEmail": "Por favor, introduce una dirección de correo válida", - "notificationSent": "Notificación enviada correctamente", - "notificationFailed": "Error al enviar la notificación", - "shared_backToFiles": "Volver a Archivos", - "shared_pageTitle": "Recursos Compartidos", - "shared_pageDescription": "Administra tus archivos y carpetas compartidos", - "shared_filterType": "Tipo:", - "shared_filterAll": "Todos", - "shared_filterFiles": "Archivos", - "shared_filterFolders": "Carpetas", - "shared_sortBy": "Ordenar por:", - "shared_sortByName": "Nombre", - "shared_sortByDate": "Fecha compartido", - "shared_sortByExpiration": "Caducidad", - "shared_search": "Buscar", - "shared_colName": "Nombre", - "shared_colType": "Tipo", - "shared_colDateShared": "Fecha compartido", - "shared_colExpiration": "Caducidad", - "shared_colPermissions": "Permisos", - "shared_colPassword": "Contraseña", - "shared_colActions": "Acciones", - "shared_emptyStateTitle": "Aún no hay recursos compartidos", - "shared_emptyStateDesc": "Cuando compartas archivos o carpetas, aparecerán aquí", - "shared_goToFiles": "Ir a Archivos", - "shared_typeFile": "Archivo", - "shared_typeFolder": "Carpeta", - "shared_noExpiration": "Sin caducidad", - "shared_hasPassword": "Sí", - "shared_noPassword": "No", - "shared_editShare": "Editar compartido", - "shared_notifyShare": "Notificar a alguien", - "shared_copyLink": "Copiar enlace", - "shared_removeShare": "Eliminar compartido", - "shared_linkCopied": "¡Enlace copiado al portapapeles!", - "shared_linkCopyFailed": "Error al copiar el enlace", - "shared_itemUpdated": "Configuración de compartido actualizada", - "shared_itemRemoved": "Compartido eliminado correctamente", - "shared_invalidEmail": "Por favor, introduce una dirección de correo válida", - "shared_notificationSent": "Notificación enviada correctamente", - "shared_notificationFailed": "Error al enviar la notificación" - }, - "actions": { - "search": "Buscar archivos...", - "new_folder": "Nueva carpeta", - "upload": "Subir", - "upload_files": "Subir archivos", - "upload_folder": "Subir carpeta", - "upload.uploading": "Subiendo...", - "upload.complete": "{count} / {total} subidos", - "upload.files": "archivos", - "rename": "Renombrar", - "move": "Mover a...", - "move_to": "Mover a", - "delete": "Eliminar", - "download": "Descargar", - "view": "Ver", - "cancel": "Cancelar", - "confirm": "Confirmar", - "share": "Compartir", - "favorite": "Añadir a favoritos", - "unfavorite": "Quitar de favoritos", - "copy": "Copiar", - "notify": "Notificar", - "send": "Enviar", - "clear_recent": "Limpiar recientes", - "logout": "Cerrar sesión", - "create": "Crear", - "search_btn": "Buscar", - "close": "Cerrar", - "delete_permanently": "Eliminar permanentemente", - "empty_trash": "Vaciar papelera", - "open_parent_folder": "Ir a la carpeta padre", - "add": "Añadir", - "apply": "Aplicar", - "clear": "Limpiar", - "remove": "Quitar" - }, - "user_menu": { - "appearance": "Apariencia", - "about": "Acerca de OxiCloud", - "about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada.", - "admin_panel": "Panel de administración", - "profile": "Mi perfil", - "role_user": "Usuario", - "theme": { - "light": "Claro", - "dark": "Oscuro", - "auto": "Como el sistema" - }, - "manage_groups": "Gestionar grupos" - }, - "files": { - "name": "Nombre", - "type": "Tipo", - "size": "Tamaño", - "modified": "Modificado", - "no_files": "No hay archivos en esta carpeta", - "empty_hint": "Sube archivos o crea carpetas para comenzar", - "loading": "Cargando archivos…", - "view_grid": "Vista de cuadrícula", - "view_list": "Vista de lista", - "file_types": { - "document": "Documento", - "image": "Imagen", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Texto", - "folder": "Carpeta", - "spreadsheet": "Hoja de cálculo", - "presentation": "Presentación", - "archive": "Archivo comprimido", - "installer": "Instalador", - "code": "Código" - }, - "owner": "Propietario" - }, - "dialogs": { - "rename_folder": "Renombrar carpeta", - "rename_file": "Renombrar archivo", - "new_name": "Nuevo nombre", - "new_folder_title": "Nueva carpeta", - "folder_name": "Nombre de la carpeta", - "folder_placeholder": "Mi carpeta", - "rename_title": "Renombrar", - "move_file": "Mover archivo", - "move_folder": "Mover carpeta", - "select_destination": "Selecciona la carpeta destino:", - "select_this_folder": "Seleccionar esta carpeta", - "go_to_parent": ".. (carpeta superior)", - "no_subfolders": "Sin subcarpetas", - "root": "Raíz", - "delete_confirmation": "¿Estás seguro de que quieres eliminar", - "and_contents": "y todo su contenido", - "no_undo": "Esta acción no se puede deshacer", - "confirm_title": "Confirmar acción", - "confirm_delete": "Mover a papelera", - "confirm_delete_file": "¿Estás seguro de que quieres mover a la papelera el archivo \"{{name}}\"?", - "confirm_delete_folder": "¿Estás seguro de que quieres mover a la papelera la carpeta \"{{name}}\" y todo su contenido?", - "confirm_permanent_delete": "Eliminar permanentemente", - "confirm_permanent_delete_msg": "¿Estás seguro de que quieres eliminar permanentemente este elemento? Esta acción no se puede deshacer.", - "confirm_empty_trash": "Vaciar papelera", - "confirm_delete_share": "Eliminar enlace compartido", - "confirm_delete_share_msg": "¿Estás seguro de que quieres eliminar este enlace compartido?", - "share_file": "Compartir Archivo", - "share_folder": "Compartir Carpeta", - "existing_shares": "Compartidos Existentes", - "share_options": "Opciones de Compartición", - "password": "Contraseña", - "expiration": "Caducidad", - "permissions": "Permisos", - "generated_link": "Enlace Generado", - "notify": "Enviar Notificación", - "recipient": "Destinatario", - "message": "Mensaje", - "move_to_home": "Mover a la carpeta de inicio" - }, - "dropzone": { - "drag_files": "Arrastra archivos aquí o haz clic para seleccionar", - "drop_files": "Suelta los archivos para subirlos" - }, - "permissions": { - "read": "Lectura", - "write": "Escritura", - "reshare": "Recompartir" - }, - "errors": { - "file_not_found": "Archivo no encontrado", - "folder_not_found": "Carpeta no encontrada", - "delete_error": "Error al eliminar", - "upload_error": "Error al subir el archivo", - "rename_error": "Error al renombrar", - "move_error": "Error al mover", - "empty_name": "El nombre no puede estar vacío", - "name_exists": "Ya existe un archivo o carpeta con ese nombre", - "generic_error": "Ha ocurrido un error", - "group_name_invalid": "El nombre del grupo debe seguir el formato de prefijo de correo (letras, dígitos, punto, guión, guion bajo; 1–64 caracteres).", - "group_cycle": "Este miembro creaería una referencia circular entre grupos.", - "group_depth_exceeded": "Esta profundidad de anidamiento excede el máximo permitido (8).", - "group_virtual_immutable": "El grupo «Internal» es gestionado por el sistema y no se puede modificar.", - "group_not_found": "Grupo no encontrado.", - "group_name_taken": "Ya existe un grupo con este nombre." - }, - "breadcrumb": { - "home": "Inicio" - }, - "trash": { - "empty_trash": "Vaciar papelera", - "empty_state": "La papelera está vacía", - "original_location": "Ubicación original", - "deleted_date": "Fecha de eliminación", - "remaining": "Restante", - "actions": "Acciones", - "restore": "Restaurar", - "delete_permanently": "Eliminar permanentemente", - "empty_confirm": "¿Estás seguro de que quieres vaciar la papelera? Esta acción eliminará permanentemente todos los elementos.", - "groupby": { - "remaining_days": "Días restantes", - "trashed_time": "Fecha de eliminación" - } - }, - "daysRemaining": { - "expired": "Caducado", - "today": "Hoy", - "tomorrow": "Mañana", - "inDays": "{{count}} días" - }, - "expiryChip": { - "never": "Nunca caduca", - "expired": "Caducado", - "today": "Caduca hoy", - "tomorrow": "Caduca mañana", - "inDays": "Caduca en {{count}} días", - "onDate": "Caduca el {{date}}" - }, - "auth": { - "login_title": "Iniciar sesión", - "username": "Usuario", - "username_placeholder": "Ingresa tu nombre de usuario", - "login_identifier": "Usuario o correo electrónico", - "login_identifier_placeholder": "Ingresa tu usuario o correo electrónico", - "password": "Contraseña", - "password_placeholder": "Ingresa tu contraseña", - "login_button": "Iniciar sesión", - "no_account": "¿No tienes cuenta?", - "register": "Regístrate", - "admin_setup": "¿Primera vez?", - "setup": "Configurar administrador", - "register_title": "Crear cuenta", - "email": "Email", - "email_placeholder": "Ingresa tu email", - "confirm_password": "Confirmar contraseña", - "confirm_password_placeholder": "Confirma tu contraseña", - "register_button": "Crear cuenta", - "have_account": "¿Ya tienes cuenta?", - "login": "Iniciar sesión", - "setup_title": "Configuración inicial", - "setup_step1": "Admin", - "setup_step2": "Sistema", - "setup_step3": "Completado", - "admin_username": "Usuario administrador", - "admin_email": "Email administrador", - "admin_password": "Contraseña administrador", - "create_admin": "Crear administrador", - "back_to_login": "¿Ya está configurado?", - "admin_success": "¡Cuenta de administrador creada con éxito! Ahora puedes iniciar sesión.", - "account_success": "¡Cuenta creada con éxito! Ahora puedes iniciar sesión.", - "passwords_mismatch": "Las contraseñas no coinciden", - "admin_create_error": "Error al crear cuenta de administrador", - "or": "o", - "sso_login": "Iniciar sesión con SSO", - "sso_login_provider": "Iniciar sesión con {{provider}}", - "magicLinkHint": "¿Sin contraseña? Introduce tu correo electrónico y te enviaremos un enlace de inicio de sesión único.", - "magicLinkEmailLabel": "Correo electrónico", - "magicLinkEmailPlaceholder": "tu@ejemplo.com", - "magicLinkSubmit": "Enviar enlace de inicio de sesión", - "magicLinkSent": "Si existe una cuenta para ese correo, se ha enviado un enlace de inicio de sesión. Revisa tu bandeja de entrada.", - "magicLinkUnavailable": "El inicio de sesión por correo electrónico no está disponible en este servidor.", - "magicLinkNetworkError": "No se pudo conectar con el servidor: {{message}}", - "magicLinkToggle": "¿Sin contraseña? Recíbelo por correo", - "passwordsMatch": "Las contraseñas coinciden", - "capsLock": "Bloq Mayús activado" - }, - "storage": { - "title": "Almacenamiento", - "calculating": "Calculando...", - "used": "{{percentage}}% usado ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Este tipo de archivo no se puede previsualizar.", - "download_file": "Descargar archivo", - "zoom_in": "Acercar", - "zoom_out": "Alejar", - "zoom_reset": "Restablecer zoom" - }, - "language_selector": { - "title": "¡Bienvenido!", - "subtitle": "Selecciona tu idioma para continuar", - "continue": "Continuar", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Aún no hay favoritos", - "empty_hint": "Marca archivos o carpetas con estrella para añadirlos a favoritos", - "add": "Añadir a favoritos", - "remove": "Quitar de favoritos", - "added_title": "Añadido a favoritos", - "added_msg": "añadido a favoritos", - "removed_title": "Quitado de favoritos", - "removed_msg": "quitado de favoritos" - }, - "recent": { - "title": "Recientes", - "clear": "Limpiar recientes", - "accessed": "Accedido", - "empty_state": "No hay archivos recientes", - "empty_hint": "Los archivos que abras aparecerán aquí", - "loadMore": "Cargar más" - }, - "notifications": { - "file_renamed": "Archivo renombrado", - "file_renamed_to": "Archivo renombrado a \"{{name}}\"", - "folder_renamed": "Carpeta renombrada", - "folder_renamed_to": "Carpeta renombrada a \"{{name}}\"", - "file_uploaded": "Archivo subido", - "file_deleted": "Archivo movido a papelera", - "folder_deleted": "Carpeta movida a papelera", - "item_deleted_permanently": "Elemento eliminado permanentemente", - "trash_emptied": "Papelera vaciada correctamente", - "title": "Notificaciones", - "empty": "Sin notificaciones", - "link_created": "Enlace creado", - "share_success": "Enlace compartido creado correctamente", - "upload_files_section_title": "Subida no disponible aquí", - "upload_files_section_body": "Ve a la sección Archivos para subir archivos" - }, - "batch": { - "one_selected": "1 elemento seleccionado", - "n_selected": "{{count}} elementos seleccionados", - "confirm_delete": "¿Estás seguro de que quieres mover {{count}} elementos a la papelera?", - "move_title": "Mover {{count}} elemento(s)", - "add_favorites": "Añadir a favoritos", - "move_copy": "Mover o copiar" - }, - "admin": { - "page_title": "Panel de Administración", - "back_to_app": "Volver a OxiCloud", - "loading": "Cargando…", - "access_denied": "Acceso Denegado", - "access_denied_desc": "Se requieren privilegios de administrador para acceder a este panel.", - "sign_in": "Iniciar sesión", - "tab_dashboard": "Panel", - "tab_users": "Usuarios", - "tab_oidc": "SSO / OIDC", - "total_users": "Usuarios Totales", - "active_users": "Usuarios Activos", - "admins": "Administradores", - "version": "Versión", - "storage_overview": "Resumen de Almacenamiento", - "used": "Usado", - "total_quota": "Cuota Total", - "usage_pct": "Uso %", - "users_over_80": "Usuarios >80% cuota", - "users_over_quota": "Usuarios sobre cuota", - "system": "Sistema", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Cuotas", - "enabled": "Habilitado", - "disabled": "Deshabilitado", - "active": "Activo", - "off": "Inactivo", - "allow_registration": "Permitir registro público", - "registration_warning": "El registro público está deshabilitado. Solo los administradores pueden crear nuevos usuarios.", - "user_management": "Gestión de Usuarios", - "create_user": "Crear Usuario", - "col_user": "Usuario", - "col_role": "Rol", - "col_auth": "Auth", - "col_status": "Estado", - "col_storage": "Almacenamiento", - "col_last_login": "Último Acceso", - "col_actions": "Acciones", - "loading_users": "Cargando usuarios…", - "failed_load_users": "Error al cargar usuarios", - "no_users_found": "No se encontraron usuarios", - "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", - "prev": "Anterior", - "next": "Siguiente", - "inactive": "Inactivo", - "you_badge": "(tú)", - "local": "Local", - "never": "Nunca", - "just_now": "Ahora mismo", - "minutes_ago": "hace {{n}}m", - "hours_ago": "hace {{n}}h", - "days_ago": "hace {{n}}d", - "edit_quota_title": "Editar cuota", - "reset_password_title": "Restablecer contraseña", - "toggle_role_title": "Cambiar rol", - "deactivate_title": "Desactivar", - "activate_title": "Activar", - "delete_title": "Eliminar", - "sso_title": "Inicio de Sesión Único (OIDC / SSO)", - "enable_sso": "Habilitar autenticación SSO", - "provider_name": "Nombre del Proveedor", - "issuer_url": "URL del Emisor", - "issuer_url_hint": "URL del emisor OpenID Connect de tu proveedor de identidad", - "auto_discover": "Auto-descubrir", - "discovering": "Descubriendo…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Dejar vacío para mantener el valor actual", - "secret_configured": "Ya hay un client secret configurado", - "callback_url": "URL de Callback", - "callback_url_hint": "(registrar en tu IdP)", - "advanced_settings": "Configuración Avanzada", - "scopes": "Scopes", - "auto_provision": "Auto-provisionar usuarios en el primer inicio de sesión", - "admin_groups": "Grupos de Admin", - "admin_groups_hint": "Nombres de grupos OIDC separados por comas que mapean al rol de admin", - "disable_password": "Desactivar inicio de sesión con contraseña (solo OIDC)", - "password_warning": "¡Esto impedirá TODOS los inicios de sesión con contraseña!", - "test_btn": "Probar", - "save_btn": "Guardar", - "saving": "Guardando…", - "settings_saved": "Configuración guardada — OIDC ahora está {{status}}", - "quota_modal_title": "Actualizar Cuota de Almacenamiento", - "quota_user_label": "Usuario:", - "new_quota": "Nueva Cuota", - "quota_unlimited_hint": "Establecer 0 para ilimitado", - "cancel": "Cancelar", - "create_user_title": "Crear Nuevo Usuario", - "username_label": "Nombre de usuario", - "username_placeholder": "juanperez", - "username_hint": "3–32 caracteres", - "password_label": "Contraseña", - "password_placeholder": "Mín 8 caracteres", - "email_label": "Correo", - "email_optional": "(opcional)", - "email_placeholder": "usuario@ejemplo.com (auto-generado si vacío)", - "role_label": "Rol", - "role_user": "Usuario", - "role_admin": "Admin", - "quota_label": "Cuota", - "creating": "Creando…", - "reset_pw_title": "Restablecer Contraseña", - "new_password_label": "Nueva Contraseña", - "resetting": "Restableciendo…", - "reset_btn": "Restablecer", - "confirm_role_change": "¿Cambiar rol a {{role}}?", - "confirm_deactivate": "¿Estás seguro de que quieres desactivar este usuario?", - "confirm_activate": "¿Estás seguro de que quieres activar este usuario?", - "confirm_delete_user": "¿ELIMINAR usuario \"{{name}}\"? ¡Esto no se puede deshacer!", - "confirm_action": "Confirmar Acción", - "confirm_yes": "Confirmar", - "confirm_no": "Cancelar", - "error_username_short": "El nombre de usuario debe tener al menos 3 caracteres", - "error_password_short": "La contraseña debe tener al menos 8 caracteres", - "error_generic": "Error", - "error_network": "Error de red: {{message}}", - "error_create_user": "Error al crear usuario", - "tab_storage": "Almacenamiento", - "storage_title": "Backend de Almacenamiento", - "storage_current_backend": "Backend Activo", - "storage_total_blobs": "Total de Blobs", - "storage_total_size": "Tamaño Total", - "storage_dedup_ratio": "Ratio de Dedup", - "storage_backend": "Tipo de Backend", - "storage_local": "Sistema de Archivos Local", - "storage_s3": "Compatible con S3", - "storage_provider_preset": "Proveedor Preconfigurado", - "storage_preset_custom": "Personalizado", - "storage_endpoint_url": "URL del Endpoint", - "storage_endpoint_hint": "Dejar vacío para usar Amazon S3 por defecto", - "storage_bucket": "Bucket", - "storage_region": "Región", - "storage_access_key": "Access Key ID", - "storage_secret_key": "Secret Access Key", - "storage_secret_configured": "Ya hay una clave secreta configurada", - "storage_key_placeholder": "Dejar vacío para mantener el valor actual", - "storage_path_style": "Forzar Path Style", - "storage_path_style_hint": "Requerido para MinIO y algunos proveedores compatibles con S3", - "storage_test_connection": "Probar Conexión", - "storage_test_success": "Conexión exitosa", - "storage_test_failure": "Conexión fallida", - "storage_save": "Guardar", - "storage_saved": "Configuración de almacenamiento guardada correctamente", - "storage_migration": "Migración de Backend", - "storage_migration_coming_soon": "La migración de backend estará disponible en una futura actualización.", - "migration_status_label": "Estado:", - "migration_start": "Iniciar Migración", - "migration_pause": "Pausar", - "migration_resume": "Reanudar", - "migration_verify": "Verificar Integridad", - "migration_complete": "Finalizar", - "migration_started": "Migración iniciada", - "migration_paused_msg": "Migración pausada", - "migration_resumed_msg": "Migración reanudada", - "migration_completed_msg": "Migración finalizada. Reinicia el servidor para usar el nuevo backend.", - "migration_verifying": "Verificando…", - "migration_verify_passed": "Verificación exitosa", - "migration_verify_failed": "Verificación fallida", - "migration_failed_blobs": "blobs fallidos", - "testing": "Probando…", - "smtp_disabled": "Desactivado (host no configurado)", - "smtp_enabled": "Activado", - "smtp_enabled_label": "Estado", - "smtp_intro": "SMTP se configura exclusivamente a través de variables de entorno (OXICLOUD_SMTP_*). Los valores siguientes se leen del servidor en ejecución — para modificarlos, edita el entorno y reinicia OxiCloud.", - "smtp_not_configured": "SMTP no está configurado en este servidor.", - "smtp_send_failed": "Fallo al enviar.", - "smtp_send_test": "Enviar correo de prueba", - "smtp_sending": "Enviando…", - "smtp_sent": "Correo de prueba enviado.", - "smtp_server_code": "Respuesta del servidor", - "smtp_test_intro": "Envía un mensaje de diagnóstico predefinido al destinatario indicado abajo e informa de la respuesta del servidor SMTP para que puedas cruzarla con los registros de tu relay.", - "smtp_test_missing_to": "Introduce una dirección de destinatario.", - "smtp_test_title": "Enviar correo de prueba", - "smtp_test_to": "Dirección del destinatario", - "smtp_title": "Correo saliente (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Perfil", - "back_to_app": "Volver a OxiCloud", - "loading": "Cargando…", - "not_authenticated": "No Autenticado", - "not_authenticated_desc": "Inicia sesión para ver tu perfil.", - "sign_in": "Iniciar sesión", - "role_admin": "Administrador", - "role_user": "Usuario", - "account_details": "Detalles de la Cuenta", - "username": "Nombre de usuario", - "email": "Correo electrónico", - "role": "Rol", - "last_login": "Último acceso", - "storage": "Almacenamiento", - "used": "Usado", - "quota": "Cuota", - "usage": "Uso", - "unlimited": "Ilimitado", - "app_passwords": "Contraseñas de Aplicación", - "app_pw_desc": "Genera contraseñas para clientes WebDAV, CalDAV y CardDAV. Cada contraseña se muestra solo una vez.", - "app_pw_label_placeholder": "Etiqueta (ej. Thunderbird, macOS)", - "generate": "Generar", - "generating": "Generando…", - "new_password_for": "Nueva contraseña para", - "copy_warning": "Copia esta contraseña ahora. No podrás verla de nuevo.", - "copy_to_clipboard": "Copiar al portapapeles", - "col_label": "Etiqueta", - "col_created": "Creado", - "col_last_used": "Último uso", - "col_status": "Estado", - "active": "Activa", - "revoked": "Revocada", - "revoke_title": "Revocar", - "no_app_passwords": "Aún no hay contraseñas de aplicación.", - "client_sessions": "Sesiones de cliente", - "client_sessions_desc": "Generadas automáticamente al conectar un cliente compatible con Nextcloud.", - "col_client": "Cliente", - "never": "Nunca", - "just_now": "Ahora mismo", - "minutes_ago": "hace {{n}} min", - "hours_ago": "hace {{n}}h", - "days_ago": "hace {{n}} días", - "edit_profile": "Editar perfil", - "edit_oidc_managed": "Para cambiar tu información (nombre, apellidos, foto de perfil, …), actualízala en tu proveedor de identidad. Los cambios se aplicarán en tu próximo inicio de sesión.", - "username_claim_hint": "Entre 2 y 64 caracteres, letras / dígitos / punto / guion / subrayado. Una vez elegido, el nombre de usuario no se puede cambiar (los clientes DAV/NextCloud dependen de él).", - "username_already_claimed": "Nombre de usuario fijado y no modificable (los clientes DAV/NextCloud dependen de él).", - "given_name": "Nombre", - "family_name": "Apellidos", - "notify_on_share": "Enviarme un correo cuando alguien comparta conmigo", - "notify_on_share_hint": "Cuando esté desmarcado, los recursos compartidos seguirán apareciendo en tu cuenta — simplemente no recibirás un correo sobre ellos.", - "save_profile": "Guardar cambios", - "profile_saved": "Perfil actualizado", - "profile_no_changes": "Sin cambios que guardar.", - "profile_save_failed": "Error al guardar", - "username_taken_error": "Ese nombre de usuario ya está en uso.", - "username_immutable_error": "Tu nombre de usuario ya está fijado y no se puede cambiar aquí. Contacta con un administrador si necesitas renombrarlo.", - "change_password": "Cambiar Contraseña", - "current_password": "Contraseña Actual", - "new_password": "Nueva Contraseña", - "min_8_chars": "Al menos 8 caracteres", - "confirm_password": "Confirmar Nueva Contraseña", - "update_password": "Actualizar Contraseña", - "updating": "Actualizando…", - "password_updated": "Contraseña actualizada correctamente", - "passwords_no_match": "Las contraseñas no coinciden", - "password_too_short": "La contraseña debe tener al menos 8 caracteres", - "password_change_failed": "Error al cambiar la contraseña", - "error_network": "Error de red: {{message}}", - "error_label_required": "Introduce una etiqueta", - "error_create_pw": "Error al crear contraseña de aplicación", - "confirm_revoke": "¿Revocar contraseña \"{{label}}\"? Los clientes que la usen dejarán de funcionar.", - "error_revoke": "Error al revocar contraseña", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Subiendo...", - "files": "archivos", - "complete": "{{count}} / {{total}} subidos" - }, - "storage_quota_exceeded": "Cuota de almacenamiento superada", - "sharedwithme": { - "pageTitle": "Compartido conmigo", - "pageDescription": "Archivos y carpetas que otros usuarios han compartido contigo", - "emptyStateTitle": "Aún no hay nada compartido contigo", - "emptyStateDesc": "Los elementos que otros usuarios compartan contigo aparecerán aquí", - "loadMore": "Cargar más", - "sharedBy": "Compartido por", - "colName": "Nombre", - "colType": "Tipo", - "colSharedBy": "Compartido por", - "colDate": "Fecha de compartición", - "colPermissions": "Permisos" - }, - "groupby": { - "none": "Ninguno", - "title": "Agrupar por", - "owner": "Propietario", - "shareDate": "Fecha de compartición", - "type": "Tipo", - "type.folders": "Carpetas", - "accessedAt": "Fecha de acceso", - "modifiedAt": "Fecha de modificación", - "createdAt": "Fecha de creación", - "size": "Tamaño", - "favoriteDate": "Fecha de favorito", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nuevo" - }, - "dateBucket": { - "today": "Hoy", - "last7days": "Últimos 7 días", - "last30days": "Últimos 30 días" - }, - "groups": { - "title": "Gestionar grupos", - "create_button": "Crear grupo", - "create_dialog_title": "Nuevo grupo", - "edit_dialog_title": "Renombrar grupo", - "name_label": "Nombre", - "name_placeholder": "ingenieria", - "description_label": "Descripción (opcional)", - "members_section": "Miembros", - "add_member_placeholder": "Añadir un usuario o grupo…", - "no_members": "Aún no hay miembros.", - "remove_member": "Eliminar", - "delete_group": "Eliminar grupo", - "delete_confirm": "¿Eliminar el grupo «{name}»? Se revocarán las concesiones que hagan referencia a este grupo.", - "empty_state": "Aún no hay grupos.", - "load_more": "Cargar más", - "back_to_list": "Volver", - "loading": "Cargando…", - "virtual_badge": "Sistema", - "member_count_zero": "Sin miembros", - "member_count_one": "1 miembro", - "member_count_other": "{count} miembros", - "delete_confirm_label": "Escribe el nombre del grupo para confirmar:", - "delete_confirm_mismatch": "Escribe el nombre del grupo exactamente para confirmar.", - "virtual_internal_name": "Interno", - "members_loading": "Cargando miembros…", - "members_empty": "Sin miembros", - "virtual_internal_explanation": "Todos los usuarios internos de este servidor" - }, - "myshares": { - "copyLink": "Copiar enlace", - "deleteLink": "Eliminar enlace", - "notifyByEmail": "Notificar por correo", - "notifyFailed": "No se pudo enviar la notificación.", - "notifyGroupMembers": "Notificar a los miembros del grupo", - "notifyRateLimited": "Demasiadas notificaciones para este destinatario — inténtalo más tarde.", - "removeAccess": "Quitar acceso", - "resendInvitation": "Reenviar correo de invitación" - }, - "sort": { - "asc": "ascendente", - "desc": "descendente" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error al realizar la búsqueda", - "cleanupCompleted": "Limpieza completada", - "cleanupCompletedBody": "Se ha borrado el historial de archivos recientes", - "batchCopy": "Copia en lote", - "batchCopyBody": "{{success}} copiados, {{errors}} fallidos", - "itemsCopied": "Elementos copiados", - "itemsCopiedBody": "{{count}} elementos copiados correctamente", - "batchMove": "Movimiento en lote", - "batchMoveBody": "{{success}} movidos, {{errors}} fallidos", - "itemsMoved": "Elementos movidos", - "itemsMovedBody": "{{count}} elementos movidos correctamente", - "batchDelete": "Borrado en lote", - "batchDeleteBody": "{{success}} movidos a la papelera, {{errors}} fallidos", - "movedToTrash": "Movido a la papelera", - "movedToTrashBody": "{{count}} elementos movidos a la papelera", - "trashItemsError": "No se pudieron mover los elementos a la papelera", - "preparingDownload": "Preparando descarga", - "preparingDownloadBody": "Preparando la descarga…", - "downloadItemsError": "No se pudieron descargar los elementos seleccionados", - "favoritesAddError": "No se pudieron añadir los elementos a favoritos", - "invalidEmail": "Introduce una dirección de correo válida", - "notificationSendError": "No se pudo enviar la notificación", - "folderCreated": "Carpeta creada", - "folderCreatedBody": "«{{name}}» creada correctamente", - "fileMoved": "Archivo movido", - "fileMovedBody": "Archivo movido correctamente", - "fileMoveError": "Error al mover el archivo: {{error}}", - "fileMoveErrorGeneric": "Error al mover el archivo", - "folderMoved": "Carpeta movida", - "folderMovedBody": "Carpeta movida correctamente", - "folderMoveError": "Error al mover la carpeta: {{error}}", - "folderMoveErrorGeneric": "Error al mover la carpeta", - "fileCopied": "Archivo copiado", - "fileCopiedBody": "Archivo copiado correctamente", - "fileCopyError": "Error al copiar el archivo: {{error}}", - "fileCopyErrorGeneric": "Error al copiar el archivo", - "folderRenamed": "Carpeta renombrada", - "folderRenamedBody": "Carpeta renombrada a «{{name}}»", - "fileTrashed": "Archivo movido a la papelera", - "fileTrashedBody": "«{{name}}» movido a la papelera", - "fileDeleted": "Archivo eliminado", - "fileDeletedBody": "«{{name}}» eliminado correctamente", - "fileDeleteError": "Error al eliminar el archivo", - "folderTrashed": "Carpeta movida a la papelera", - "folderTrashedBody": "«{{name}}» movida a la papelera", - "folderDeleted": "Carpeta eliminada", - "folderDeletedBody": "«{{name}}» eliminada correctamente", - "folderDeleteError": "Error al eliminar la carpeta", - "itemRestored": "Elemento restaurado", - "itemRestoredBody": "Elemento restaurado correctamente", - "itemRestoreError": "Error al restaurar el elemento", - "itemDeleted": "Elemento eliminado", - "itemDeletedBody": "Elemento eliminado permanentemente", - "itemDeleteError": "Error al eliminar el elemento", - "trashEmptied": "Papelera vaciada", - "trashEmptiedBody": "La papelera se ha vaciado correctamente", - "trashEmptyError": "Error al vaciar la papelera", - "cacheCleared": "Caché limpiada", - "cacheClearedBody": "Caché de búsqueda limpiada correctamente", - "cacheClearError": "Error al limpiar la caché de búsqueda", - "wopiOpenError": "No se pudo abrir el editor de documentos.", - "linkCopied": "Enlace copiado", - "linkCopiedBody": "Enlace copiado al portapapeles", - "linkCopyError": "No se pudo copiar el enlace", - "notificationSent": "Notificación enviada", - "notificationSentBody": "Notificación enviada a {{email}}" - } -} diff --git a/static/locales/fa.json b/static/locales/fa.json deleted file mode 100644 index b99bd940..00000000 --- a/static/locales/fa.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "این پیوند ورود دیگر معتبر نیست", - "expired_body": "ممکن است پیوند منقضی شده یا قبلاً استفاده شده باشد. می‌توانیم پیوند جدیدی برایتان ارسال کنیم — ظرف چند ثانیه به صندوق ورودی شما می‌رسد.", - "resend_to": "ارسال پیوند جدید به {{email}}", - "generic_unavailable": "این پیوند ورود دیگر معتبر نیست. ممکن است قبلاً استفاده شده باشد یا منقضی شده باشد. پیوند جدیدی را از صفحهٔ ورود درخواست کنید.", - "service_unavailable": "ورود از طریق پیوند جادویی روی این سرور فعال نیست.", - "internal_error": "هنگام ورود خطایی رخ داد. لطفاً دوباره تلاش کنید.", - "resend_failure": "هنگام ارسال پیوند خطایی رخ داد. لطفاً دوباره تلاش کنید.", - "cross_browser_title": "آیا می‌خواهید ورود در این دستگاه ادامه یابد؟", - "cross_browser_body": "این پیوند ورود را در مرورگر یا دستگاهی متفاوت از جایی که درخواست کرده‌اید باز کرده‌اید.", - "cross_browser_warning": "اگر این پیوند را خودتان درخواست کرده‌اید، ادامه دادن ایمن است. در غیر این صورت این صفحه را ببندید — کلیک روی ادامه باعث ورود شخص دیگری به حساب شما خواهد شد.", - "cross_browser_continue": "ادامه و ورود", - "resend_confirmation_title": "صندوق ورودی خود را بررسی کنید", - "resend_confirmation_body": "اگر پیوند ورود متعلق به یک حساب فعال بوده، پیوند جدیدی هم اکنون ارسال شد. لطفاً صندوق ورودی خود را بررسی کنید.", - "return_link": "بازگشت به OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", - "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبا کلیک روی پیوند زیر آن را باز کنید:\n{{link}}\n\nپیوند یک‌بار مصرف است و در {{ttl_hours}} ساعت منقضی می‌شود.\nاگر منتظر این دعوت نبودید، می‌توانید این پیام را نادیده بگیرید.\n\n— OxiCloud" - }, - "login": { - "subject": "ورود به OxiCloud", - "body": "سلام،\n\nبرای ورود به OxiCloud از پیوند زیر استفاده کنید. پیوند یک‌بار مصرف است و در {{ttl_minutes}} دقیقه منقضی می‌شود. آن را در همان دستگاهی که درخواست کرده‌اید باز کنید.\n\n{{link}}\n\nاگر این پیوند ورود را درخواست نکرده‌اید، می‌توانید این پیام را نادیده بگیرید — اقدام دیگری لازم نیست.\n\n— OxiCloud" - }, - "kind_file": "فایل", - "kind_folder": "پوشه", - "english_fallback_divider": "--- نسخهٔ انگلیسی در پایین ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت", - "body": "{{inviter_full}} یک {{kind}} را با شما در OxiCloud به اشتراک گذاشت.\n\nبرای دیدن اشتراک‌گذاری جدید خود، OxiCloud را باز کنید:\n{{login_link}}\n\nممکن است اشتراک‌گذاری‌های جدید دیگری از {{inviter}} داشته باشید — وارد شوید تا همه موارد به اشتراک گذاشته‌شده با خود را ببینید.\n\n— OxiCloud\n\nشما این پیام را دریافت می‌کنید زیرا حساب OxiCloud دارید و گزینه اعلان اشتراک‌گذاری شما روشن است. می‌توانید آن را در پروفایل خود خاموش کنید (وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "سیستم ذخیره‌سازی ابری ساده‌گرا" - }, - "nav": { - "files": "پرونده‌ها", - "shared": "هم‌رسانی‌های من", - "recent": "اخیر", - "favorites": "موردعلاقه‌ها", - "photos": "عکس‌ها", - "music": "موسیقی", - "trash": "سطل زباله", - "sharedwithme": "به اشتراک‌گذاشته شده با من" - }, - "photos": { - "empty_state": "هنوز عکسی نیست", - "empty_hint": "تصاویر یا ویدیوها را آپلود کنید تا اینجا نمایش داده شوند", - "items_selected": "انتخاب شده", - "view_daily": "روز", - "view_monthly": "ماه", - "view_yearly": "سال" - }, - "music": { - "create_playlist": "ایجاد فهرست پخش", - "playlists": "فهرست‌های پخش", - "no_playlists": "هنوز فهرست پخشی نیست", - "select_playlist": "یک فهرست پخش انتخاب کنید", - "select_hint": "از نوار کناری یک فهرست پخش انتخاب کنید یا یکی جدید بسازید", - "add_tracks": "افزودن آهنگ‌ها", - "no_tracks": "هیچ آهنگی در این فهرست پخش نیست", - "unknown_artist": "هنرمند ناشناس", - "unknown_title": "ناشناس", - "confirm_delete": "این فهرست پخش حذف شود؟", - "playlist_name": "نام فهرست پخش", - "create": "ایجاد", - "delete": "حذف", - "share": "هم‌رسانی", - "edit": "ویرایش", - "play_all": "پخش همه", - "shuffle": "تصادفی", - "repeat": "تکرار", - "repeat_one": "تکرار یک", - "queue": "صف", - "queue_empty": "صف خالی است", - "not_playing": "در حال پخش نیست", - "play": "پخش", - "pause": "توقف", - "previous": "قبلی", - "next": "بعدی", - "volume": "صدا", - "mute": "بی‌صدا", - "unmute": "صدا فعال", - "title": "عنوان", - "artist": "هنرمند", - "album": "آلبوم", - "tracks": "آهنگ", - "add": "افزودن", - "added": "افزوده شد!", - "added_to_playlist": "به فهرست پخش افزوده شد", - "add_to_playlist": "افزودن به فهرست پخش", - "load_error": "خطا در بارگیری فهرست پخش", - "add_error": "امکان افزودن آهنگ‌ها به فهرست پخش نیست", - "no_playlists_yet": "فهرست پخشی وجود ندارد. اول یکی بسازید!", - "selected_files": "انتخاب شده:", - "error": "خطا", - "search_audio": "جستجوی فایل‌های صوتی…", - "no_audio_files": "فایل صوتی یافت نشد", - "selected": "انتخاب شده", - "loading": "در حال بارگذاری…", - "search_error": "بارگذاری فایل‌های صوتی ممکن نشد", - "adding": "در حال افزودن…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "جست‌و‌جوی پرونده‌ها..", - "new_folder": "پوشهٔ جدید", - "upload": "بارگذاری", - "upload_files": "بارگذاری پرونده‌ها", - "upload_folder": "بارگذاری پوشه", - "upload.uploading": "...در حال بارگذاری", - "upload.complete": "{count} / {total} بارگذاری شد", - "upload.files": "فایل‌ها", - "rename": "تغییر نام", - "move": "انتقال به...", - "move_to": "انتقال به", - "delete": "حذف", - "download": "بارگیری", - "view": "مشاهده", - "cancel": "لغو", - "confirm": "تأیید", - "share": "هم‌رسانی", - "favorite": "افزودن به موردعلاقه‌ها", - "unfavorite": "حذف از موردعلاقه‌ها", - "copy": "رونوشت", - "notify": "آگاه‌سازی", - "send": "ارسال", - "clear_recent": "پاک‌کردن موارد اخیر", - "logout": "خروج", - "create": "ایجاد", - "search_btn": "جست‌و‌جو", - "close": "بستن", - "delete_permanently": "Delete permanently", - "empty_trash": "Empty trash", - "open_parent_folder": "رفتن به پوشه والد", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "ظاهر", - "about": "درباره OxiCloud", - "about_description": "پلتفرم ذخیره‌سازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی.", - "admin_panel": "پنل مدیریت", - "profile": "نمایه من", - "role_user": "کاربر", - "theme": { - "light": "روشن", - "dark": "تاریک", - "auto": "مانند سیستم" - }, - "manage_groups": "مدیریت گروه‌ها" - }, - "share": { - "dialogTitle": "پیوند هم‌رسانی", - "linkLabel": "پیوند هم‌سانی:", - "copyLink": "رونوشت", - "permissions": "دسترسی‌ها:", - "permissionRead": "خواندن", - "permissionWrite": "نوشتن", - "permissionReshare": "هم‌رسانی دوباره", - "password": "محافظت با گذرواژه:", - "generatePassword": "تولید", - "expiration": "تاریخ انقضا:", - "update": "به‌روزرسانی هم‌رسانی", - "remove": "پاک‌کردن هم‌رسانی", - "notifyTitle": "ارسال آگاه‌سازی", - "notifyEmailLabel": "نشانی رایانامه:", - "notifyMessageLabel": "پیام (اختیاری):", - "notifySend": "ارسال آگاه‌سازی", - "shareWithOthers": "هم‌رسانی با دیگران", - "sharePublicly": "هم‌رسانی عمومی", - "shareSettings": "تنظیمات هم‌رسانی", - "shareCopied": "پیوند به بُریده‌دان رونوشت شد", - "shareCreated": "پیوند هم‌رسانی با موفقیت ایجاد شد", - "shareUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", - "shareRemoved": "هم‌رسانی با موفقیت پاک شد", - "inviteByEmail": "دعوت از طریق ایمیل — دعوت ارسال خواهد شد", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "پیوند هم‌رسانی", - "share_linkLabel": "پیوند هم‌رسانی:", - "share_copyLink": "رونوشت", - "share_permissions": "دسترسی‌ها:", - "share_permissionRead": "خواندن", - "share_permissionWrite": "نوشتن", - "share_permissionReshare": "هم‌رسانی دوباره", - "share_password": "محافظت با گذرواژه:", - "share_generatePassword": "تولید", - "share_expiration": "تاریخ انقضا:", - "share_update": "به‌روزرسانی هم‌رسانی", - "share_remove": "پاک‌کردن هم‌رسانی", - "share_notifyTitle": "ارسال آگاه‌سازی", - "share_notifyEmailLabel": "نشانی رایانامه:", - "share_notifyMessageLabel": "پیام (اختیاری):", - "share_notifySend": "ارسال آگاه‌سازی", - "shared": { - "backToFiles": "بازگشت به پرونده‌ها", - "pageTitle": "منابع هم‌رسانی شده", - "pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", - "filterType": "نوع:", - "filterAll": "همه", - "filterFiles": "پرونده‌ها", - "filterFolders": "پوشه‌ها", - "sortBy": "مرتب‌سازی بر اساس:", - "sortByName": "نام", - "sortByDate": "تاریخ هم‌رسانی", - "sortByExpiration": "تاریخ انقضا", - "search": "جست‌و‌جو", - "colName": "نام", - "colType": "نوع", - "colDateShared": "تاریخ هم‌رسانی", - "colExpiration": "تاریخ انقضا", - "colPermissions": "دسترسی‌ها", - "colPassword": "گذرواژه", - "colActions": "عملیات", - "emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", - "emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", - "goToFiles": "رفتن به پرونده‌ها", - "typeFile": "پرونده", - "typeFolder": "پوشه", - "noExpiration": "بدون انقضا", - "hasPassword": "بله", - "noPassword": "خیر", - "editShare": "ویرایش هم‌رسانی", - "notifyShare": "آگاه‌سازی کسی", - "copyLink": "رونوشت پیوند", - "removeShare": "حذف هم‌رسانی", - "linkCopied": "پیوند به بُریده‌دان رونوشت شد", - "linkCopyFailed": "رونوشت پیوند ناموفق بود", - "itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", - "itemRemoved": "هم‌رسانی با موفقیت پاک شد", - "invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", - "notificationSent": "آگاه‌سازی با موفقیت ارسال شد", - "notificationFailed": "ارسال آگاه‌سازی ناموفق بود", - "shared_backToFiles": "بازگشت به پرونده‌ها", - "shared_pageTitle": "منابع هم‌رسانی شده", - "shared_pageDescription": "مدیریت پرونده‌ها و پوشه‌های هم‌رسانی شده شما", - "shared_filterType": "نوع:", - "shared_filterAll": "همه", - "shared_filterFiles": "پرونده‌ها", - "shared_filterFolders": "پوشه‌ها", - "shared_sortBy": "مرتب‌سازی بر اساس:", - "shared_sortByName": "نام", - "shared_sortByDate": "تاریخ هم‌رسانی", - "shared_sortByExpiration": "تاریخ انقضا", - "shared_search": "جست‌و‌جو", - "shared_colName": "نام", - "shared_colType": "نوع", - "shared_colDateShared": "تاریخ هم‌رسانی", - "shared_colExpiration": "تاریخ انقضا", - "shared_colPermissions": "دسترسی‌ها", - "shared_colPassword": "گذرواژه", - "shared_colActions": "عملیات", - "shared_emptyStateTitle": "هنوز هیچ منبعی هم‌رسانی نشده است", - "shared_emptyStateDesc": "وقتی پرونده‌ها یا پوشه‌ها را هم‌رسانی کنید، اینجا نمایش داده می‌شوند", - "shared_goToFiles": "رفتن به پرونده‌ها", - "shared_typeFile": "پرونده", - "shared_typeFolder": "پوشه", - "shared_noExpiration": "بدون انقضا", - "shared_hasPassword": "بله", - "shared_noPassword": "خیر", - "shared_editShare": "ویرایش هم‌رسانی", - "shared_notifyShare": "آگاه‌سازی کسی", - "shared_copyLink": "رونوشت پیوند", - "shared_removeShare": "حذف هم‌رسانی", - "shared_linkCopied": "پیوند به بُریده‌دان رونوشت شد", - "shared_linkCopyFailed": "رونوشت پیوند ناموفق بود", - "shared_itemUpdated": "تنظیمات هم‌رسانی با موفقیت به‌روزرسانی شد", - "shared_itemRemoved": "هم‌رسانی با موفقیت پاک شد", - "shared_invalidEmail": "لطفا یک نشانی رایانامه معتبر وارد کنید", - "shared_notificationSent": "آگاه‌سازی با موفقیت ارسال شد", - "shared_notificationFailed": "ارسال آگاه‌سازی ناموفق بود" - }, - "files": { - "name": "نام", - "type": "نوع", - "size": "اندازه", - "modified": "تاریخ تغییر", - "no_files": "هنوز هیچ پرونده‌ای در این پوشه وجود ندارد", - "empty_hint": "برای شروع، فایل‌ها را آپلود کنید یا پوشه بسازید", - "loading": "در حال بارگذاری فایل‌ها…", - "view_grid": "نمای شبکه‌ای", - "view_list": "نمای فهرستی", - "file_types": { - "document": "سند", - "image": "تصویر", - "video": "ویدیو", - "audio": "صوتی", - "pdf": "PDF", - "text": "متن", - "folder": "پوشه", - "spreadsheet": "صفحه گسترده", - "presentation": "ارائه", - "archive": "بایگانی", - "installer": "نصب‌کننده", - "code": "کد" - }, - "owner": "مالک" - }, - "dialogs": { - "rename_folder": "تغییر نام پوشه", - "new_name": "نام جدید", - "new_folder_title": "پوشه جدید", - "folder_name": "نام پوشه", - "folder_placeholder": "پوشه من", - "rename_title": "تغییر نام", - "move_file": "انتقال پرونده", - "select_destination": "انتخاب پوشهٔ مقصد", - "root": "ریشه", - "delete_confirmation": "آیا مطمئن هستید که می‌خواهید حذف کنید", - "and_contents": "و همهٔ محتویات آن", - "no_undo": "این عملیات قابل بازگردانی نیست", - "share_file": "هم‌رسانی پرونده", - "share_folder": "هم‌رسانی پوشه", - "existing_shares": "هم‌رسانی موجود", - "share_options": "گزینه‌های هم‌رسانی", - "password": "گذرواژه", - "expiration": "تاریخ انقضا", - "permissions": "دسترسی‌ها", - "generated_link": "پیوند تولید شده", - "notify": "ارسال آگاه‌سازی", - "recipient": "گیرنده", - "message": "پیام", - "confirm_delete": "Move to trash", - "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", - "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", - "confirm_delete_share": "Delete share link", - "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", - "confirm_empty_trash": "Empty trash", - "confirm_permanent_delete": "Delete permanently", - "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", - "confirm_title": "Confirm action", - "go_to_parent": ".. (parent folder)", - "move_folder": "Move folder", - "no_subfolders": "No subfolders", - "rename_file": "Rename file", - "select_this_folder": "Select this folder", - "move_to_home": "انتقال به پوشه خانگی" - }, - "dropzone": { - "drag_files": "پرونده‌ها را اینجا بکشید یا برای انتخاب کلیک کنید", - "drop_files": "پرونده‌ها را رها کنید تا بارگذاری شوند" - }, - "permissions": { - "read": "خواندن", - "write": "نوشتن", - "reshare": "هم‌رسانی دوباره" - }, - "errors": { - "file_not_found": "پرونده پیدا نشد", - "folder_not_found": "پوشه پیدا نشد", - "delete_error": "خطا در پاک کردن", - "upload_error": "خطا در بارگذاری پرونده", - "rename_error": "خطا در تغییر نام", - "move_error": "خطا در انتقال", - "empty_name": "نام نمی‌تواند خالی باشد", - "name_exists": "پرونده یا پوشه‌ای با این نام قبلا وجود دارد", - "generic_error": "خطایی رخ داده است", - "group_name_invalid": "نام گروه باید با قالب پیشوند ایمیل مطابقت داشته باشد (حروف، ارقام، نقطه، خط تیره، زیرخط؛ 1–64 نویسه).", - "group_cycle": "این عضو باعث ایجاد ارجاع چرخه‌ای بین گروه‌ها می‌شود.", - "group_depth_exceeded": "عمق تودرتو بیش از حداکثر مجاز (8) است.", - "group_virtual_immutable": "گروه «Internal» توسط سامانه مدیریت می‌شود و قابل تغییر نیست.", - "group_not_found": "گروه پیدا نشد.", - "group_name_taken": "گروهی با این نام پیش‌از این وجود دارد." - }, - "breadcrumb": { - "home": "صفحه اصلی" - }, - "trash": { - "empty_trash": "خالی کردن سطل زباله", - "empty_state": "سطل زباله خالی است", - "original_location": "محل اصلی", - "deleted_date": "تاریخ حذف", - "remaining": "باقی‌مانده", - "actions": "عملیات", - "restore": "بازیابی", - "delete_permanently": "حذف دائمی", - "empty_confirm": "آیا مطمئن هستید که می‌خواهید سطل زباله را خالی کنید؟ این کار همهٔ موارد را به‌طور دائمی حذف خواهد کرد.", - "groupby": { - "remaining_days": "روزهای باقی‌مانده", - "trashed_time": "زمان حذف" - } - }, - "daysRemaining": { - "expired": "منقضی شده", - "today": "امروز", - "tomorrow": "فردا", - "inDays": "{{count}} روز" - }, - "expiryChip": { - "never": "هرگز منقضی نمی‌شود", - "expired": "منقضی شده", - "today": "امروز منقضی می‌شود", - "tomorrow": "فردا منقضی می‌شود", - "inDays": "در {{count}} روز منقضی می‌شود", - "onDate": "در {{date}} منقضی می‌شود" - }, - "auth": { - "login_title": "ورود", - "username": "نام‌کاربری", - "username_placeholder": "نام‌کاربری خود را وارد کنید", - "login_identifier": "نام کاربری یا ایمیل", - "login_identifier_placeholder": "نام کاربری یا ایمیل خود را وارد کنید", - "password": "گذرواژه", - "password_placeholder": "گذرواژه خود را وارد کنید", - "login_button": "ورود", - "no_account": "حساب کاربری ندارید؟", - "register": "نام‌نویسی", - "admin_setup": "اولین بار است؟", - "setup": "راه‌اندازی اولیه مدیریت", - "register_title": "ایجاد حساب کاربری", - "email": "رایانامه", - "email_placeholder": "رایانامه خود را وارد کنید", - "confirm_password": "تأیید گذرواژه", - "confirm_password_placeholder": "گذرواژه خود را تأیید کنید", - "register_button": "ایجاد حساب کاربری", - "have_account": "حساب کاربری دارید؟", - "login": "ورود", - "setup_title": "راه‌اندازی اولیه", - "setup_step1": "مدیر", - "setup_step2": "سیستم", - "setup_step3": "تکمیل", - "admin_username": "نام‌کاربری مدیر", - "admin_email": "رایانامه مدیر", - "admin_password": "گذرواژه مدیر", - "create_admin": "ایجاد مدیر", - "back_to_login": "قبلا راه‌اندازی شده است؟", - "admin_success": "حساب کاربری مدیر با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", - "account_success": "حساب کاربری با موفقیت ایجاد شد! اکنون می‌توانید وارد شوید.", - "passwords_mismatch": "گذرواژه‌ها مطابقت ندارند", - "admin_create_error": "خطا در ایجاد حساب کاربری مدیر", - "or": "یا", - "sso_login": "ورود با SSO", - "sso_login_provider": "ورود با {{provider}}", - "magicLinkHint": "رمز عبور ندارید؟ ایمیل خود را وارد کنید تا یک پیوند ورود یک‌بار‌مصرف برایتان ارسال شود.", - "magicLinkEmailLabel": "آدرس ایمیل", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "ارسال پیوند ورود", - "magicLinkSent": "اگر برای این ایمیل حسابی وجود داشته باشد، پیوند ورود ارسال شده است. صندوق ورودی خود را بررسی کنید.", - "magicLinkUnavailable": "ورود با ایمیل در این سرور در دسترس نیست.", - "magicLinkNetworkError": "ارتباط با سرور برقرار نشد: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "فضای ذخیره‌سازی", - "calculating": "در حال محاسبه...", - "used": "{{percentage}}% استفاده شده ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "این نوع پرونده قابل پیش‌نمایش نیست.", - "download_file": "بارگیری پرونده", - "zoom_in": "بزرگ‌نمایی", - "zoom_out": "کوچک‌نمایی", - "zoom_reset": "بازنشانی بزرگ‌نمایی" - }, - "language_selector": { - "title": "!خوش آمدید", - "subtitle": "زبان خود را برای ادامه انتخاب کنید", - "continue": "ادامه", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "هنوز هیچ مورد علاقه‌ای وجود ندارد", - "empty_hint": "برای افزودن به موارد علاقه‌مند، پرونده‌ها یا پوشه‌ها را ستاره‌دار کنید", - "add": "افزودن به موارد علاقه‌مند", - "remove": "حذف از موارد علاقه‌مند", - "added_title": "به موارد علاقه‌مند افزوده شد", - "added_msg": "به موارد علاقه‌مند افزوده شد", - "removed_title": "از موارد علاقه‌مند حذف شد", - "removed_msg": "از موارد علاقه‌مند حذف شد" - }, - "recent": { - "title": "اخیر", - "clear": "پاک کردن اخیر", - "accessed": "دسترسی یافته", - "empty_state": "هنوز هیچ پروندهٔ اخیر وجود ندارد", - "empty_hint": "پرونده‌هایی که باز می‌کنید اینجا ظاهر می‌شوند", - "loadMore": "بارگذاری بیشتر" - }, - "batch": { - "one_selected": "۱ مورد انتخاب شده", - "n_selected": "{{count}} مورد انتخاب شده", - "confirm_delete": "آیا مطمئنید که می‌خواهید {{count}} مورد را به سطل زباله منتقل کنید؟", - "move_title": "انتقال {{count}} مورد", - "add_favorites": "افزودن به موارد علاقه‌مند", - "move_copy": "انتقال یا کپی" - }, - "admin": { - "page_title": "پنل مدیریت", - "back_to_app": "بازگشت به OxiCloud", - "loading": "در حال بارگذاری…", - "access_denied": "دسترسی ممنوع", - "access_denied_desc": "امتیازات مدیر لازم است.", - "sign_in": "ورود", - "tab_dashboard": "داشبورد", - "tab_users": "کاربران", - "tab_oidc": "SSO / OIDC", - "total_users": "کل کاربران", - "active_users": "کاربران فعال", - "admins": "مدیران", - "version": "نسخه", - "storage_overview": "نمای کلی فضا", - "used": "استفاده شده", - "total_quota": "سهمیه کل", - "usage_pct": "درصد استفاده", - "users_over_80": "کاربران بالای ۸۰٪", - "users_over_quota": "کاربران بالای سهمیه", - "system": "سیستم", - "auth_label": "احراز هویت", - "oidc_label": "OIDC", - "quotas_label": "سهمیه‌ها", - "enabled": "فعال", - "disabled": "غیرفعال", - "active": "فعال", - "off": "خاموش", - "allow_registration": "اجازه ثبت‌نام عمومی", - "registration_warning": "ثبت‌نام عمومی غیرفعال است. فقط مدیران می‌توانند کاربر جدید بسازند.", - "user_management": "مدیریت کاربران", - "create_user": "ایجاد کاربر", - "col_user": "کاربر", - "col_role": "نقش", - "col_auth": "احراز هویت", - "col_status": "وضعیت", - "col_storage": "فضا", - "col_last_login": "آخرین ورود", - "col_actions": "عملیات", - "loading_users": "در حال بارگذاری…", - "failed_load_users": "خطا در بارگذاری", - "no_users_found": "کاربری یافت نشد", - "showing_users": "نمایش {{from}}-{{to}} از {{total}}", - "prev": "قبلی", - "next": "بعدی", - "inactive": "غیرفعال", - "you_badge": "(شما)", - "local": "محلی", - "never": "هرگز", - "just_now": "همین الان", - "minutes_ago": "{{n}} دقیقه پیش", - "hours_ago": "{{n}} ساعت پیش", - "days_ago": "{{n}} روز پیش", - "edit_quota_title": "ویرایش سهمیه", - "reset_password_title": "بازنشانی رمز", - "toggle_role_title": "تغییر نقش", - "deactivate_title": "غیرفعال کردن", - "activate_title": "فعال کردن", - "delete_title": "حذف", - "sso_title": "ورود یکپارچه (OIDC / SSO)", - "enable_sso": "فعال‌سازی SSO", - "provider_name": "نام ارائه‌دهنده", - "issuer_url": "آدرس صادرکننده", - "issuer_url_hint": "آدرس صادرکننده OpenID Connect", - "auto_discover": "کشف خودکار", - "discovering": "در حال کشف…", - "client_id": "شناسه مشتری", - "client_secret": "رمز مشتری", - "client_secret_placeholder": "خالی بگذارید تا مقدار فعلی حفظ شود", - "secret_configured": "رمز مشتری قبلاً پیکربندی شده", - "callback_url": "آدرس بازگشت", - "callback_url_hint": "(در IdP ثبت کنید)", - "advanced_settings": "تنظیمات پیشرفته", - "scopes": "محدوده‌ها", - "auto_provision": "تامین خودکار کاربران", - "admin_groups": "گروه‌های مدیر", - "admin_groups_hint": "نام گروه‌های OIDC جدا شده با کاما", - "disable_password": "غیرفعال‌سازی ورود با رمز (فقط OIDC)", - "password_warning": "تمام ورودهای رمزی متوقف می‌شود!", - "test_btn": "آزمایش", - "save_btn": "ذخیره", - "saving": "در حال ذخیره…", - "settings_saved": "تنظیمات ذخیره شد — OIDC اکنون {{status}}", - "quota_modal_title": "به‌روزرسانی سهمیه", - "quota_user_label": "کاربر:", - "new_quota": "سهمیه جدید", - "quota_unlimited_hint": "۰ برای نامحدود", - "cancel": "انصراف", - "create_user_title": "ایجاد کاربر جدید", - "username_label": "نام کاربری", - "username_placeholder": "نام‌کاربری", - "username_hint": "۳ تا ۳۲ کاراکتر", - "password_label": "رمز عبور", - "password_placeholder": "حداقل ۸ کاراکتر", - "email_label": "ایمیل", - "email_optional": "(اختیاری)", - "email_placeholder": "user@example.com (خودکار اگر خالی)", - "role_label": "نقش", - "role_user": "کاربر", - "role_admin": "مدیر", - "quota_label": "سهمیه", - "creating": "در حال ایجاد…", - "reset_pw_title": "بازنشانی رمز عبور", - "new_password_label": "رمز عبور جدید", - "resetting": "در حال بازنشانی…", - "reset_btn": "بازنشانی", - "confirm_role_change": "نقش به {{role}} تغییر یابد؟", - "confirm_deactivate": "آیا از غیرفعال‌سازی این کاربر مطمئنید؟", - "confirm_activate": "آیا از فعال‌سازی این کاربر مطمئنید؟", - "confirm_delete_user": "کاربر «{{name}}» حذف شود؟ قابل بازگشت نیست!", - "confirm_action": "تأیید عملیات", - "confirm_yes": "تأیید", - "confirm_no": "انصراف", - "error_username_short": "نام کاربری حداقل ۳ کاراکتر", - "error_password_short": "رمز عبور حداقل ۸ کاراکتر", - "error_generic": "خطا", - "error_network": "خطای شبکه: {{message}}", - "error_create_user": "خطا در ایجاد کاربر", - "tab_storage": "فضای ذخیره‌سازی", - "storage_title": "تنظیمات فضای ذخیره‌سازی", - "storage_current_backend": "بک‌اند فعلی", - "storage_total_blobs": "مجموع بلوب‌ها", - "storage_total_size": "حجم کل", - "storage_dedup_ratio": "نسبت حذف تکراری", - "storage_backend": "بک‌اند", - "storage_local": "محلی", - "storage_s3": "سازگار با S3", - "storage_provider_preset": "پیش‌تنظیم ارائه‌دهنده", - "storage_preset_custom": "سفارشی", - "storage_endpoint_url": "آدرس نقطه پایانی", - "storage_endpoint_hint": "برای AWS S3 خالی بگذارید", - "storage_bucket": "باکت", - "storage_region": "منطقه", - "storage_access_key": "کلید دسترسی", - "storage_secret_key": "کلید مخفی", - "storage_secret_configured": "کلید تنظیم شد", - "storage_key_placeholder": "کلید جدید وارد کنید", - "storage_path_style": "اجبار سبک مسیر", - "storage_path_style_hint": "برای MinIO و برخی سرویس‌های سازگار با S3 لازم است", - "storage_test_connection": "آزمایش اتصال", - "storage_test_success": "اتصال موفق", - "storage_test_failure": "اتصال ناموفق", - "storage_save": "ذخیره تنظیمات", - "storage_saved": "تنظیمات ذخیره شد", - "storage_migration": "انتقال داده", - "storage_migration_coming_soon": "ابزارهای انتقال به زودی", - "migration_status_label": "وضعیت انتقال", - "migration_start": "شروع انتقال", - "migration_pause": "توقف", - "migration_resume": "ادامه", - "migration_verify": "تأیید", - "migration_complete": "تکمیل", - "migration_started": "انتقال شروع شد", - "migration_paused_msg": "انتقال متوقف شد", - "migration_resumed_msg": "انتقال ادامه یافت", - "migration_completed_msg": "انتقال با موفقیت تکمیل شد", - "migration_verifying": "در حال تأیید...", - "migration_verify_passed": "تأیید موفق", - "migration_verify_failed": "تأیید ناموفق", - "migration_failed_blobs": "بلوب‌های ناموفق", - "testing": "در حال آزمایش...", - "smtp_disabled": "غیرفعال (میزبان تنظیم نشده)", - "smtp_enabled": "فعال", - "smtp_enabled_label": "وضعیت", - "smtp_intro": "SMTP فقط از طریق متغیرهای محیطی (OXICLOUD_SMTP_*) پیکربندی می‌شود. مقادیر زیر از سرور در حال اجرا خوانده می‌شوند — برای تغییر آن‌ها، محیط را ویرایش کرده و OxiCloud را راه‌اندازی مجدد کنید.", - "smtp_not_configured": "SMTP روی این سرور پیکربندی نشده است.", - "smtp_send_failed": "ارسال ناموفق.", - "smtp_send_test": "ارسال ایمیل آزمایشی", - "smtp_sending": "در حال ارسال…", - "smtp_sent": "ایمیل آزمایشی ارسال شد.", - "smtp_server_code": "پاسخ سرور", - "smtp_test_intro": "یک پیام تشخیصی از پیش تعریف‌شده را به گیرنده زیر ارسال می‌کند و پاسخ سرور SMTP را گزارش می‌دهد تا بتوانید آن را با گزارش‌های ریلی خود مطابقت دهید.", - "smtp_test_missing_to": "آدرس گیرنده را وارد کنید.", - "smtp_test_title": "ارسال ایمیل آزمایشی", - "smtp_test_to": "آدرس گیرنده", - "smtp_title": "ایمیل خروجی (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "پروفایل", - "back_to_app": "بازگشت به OxiCloud", - "loading": "در حال بارگذاری…", - "not_authenticated": "احراز هویت نشده", - "not_authenticated_desc": "برای مشاهده پروفایل وارد شوید.", - "sign_in": "ورود", - "role_admin": "مدیر", - "role_user": "کاربر", - "account_details": "جزئیات حساب", - "username": "نام کاربری", - "email": "ایمیل", - "role": "نقش", - "last_login": "آخرین ورود", - "storage": "فضای ذخیره‌سازی", - "used": "استفاده شده", - "quota": "سهمیه", - "usage": "مصرف", - "unlimited": "نامحدود", - "app_passwords": "رمزهای برنامه", - "app_pw_desc": "رمزهایی برای کلاینت‌های WebDAV، CalDAV و CardDAV ایجاد کنید. هر رمز فقط یک بار نمایش داده می‌شود.", - "app_pw_label_placeholder": "برچسب (مثلاً Thunderbird، macOS)", - "generate": "ایجاد", - "generating": "در حال ایجاد…", - "new_password_for": "رمز جدید برای", - "copy_warning": "این رمز را اکنون کپی کنید. دوباره قابل مشاهده نیست.", - "copy_to_clipboard": "کپی به کلیپ‌بورد", - "col_label": "برچسب", - "col_created": "ایجاد شده", - "col_last_used": "آخرین استفاده", - "col_status": "وضعیت", - "active": "فعال", - "revoked": "ابطال شده", - "revoke_title": "ابطال", - "no_app_passwords": "هنوز رمز برنامه‌ای وجود ندارد.", - "client_sessions": "نشست‌های کلاینت", - "client_sessions_desc": "هنگام اتصال کلاینت سازگار با Nextcloud به صورت خودکار ایجاد می‌شود.", - "col_client": "کلاینت", - "never": "هرگز", - "just_now": "همین الان", - "minutes_ago": "{{n}} دقیقه پیش", - "hours_ago": "{{n}} ساعت پیش", - "days_ago": "{{n}} روز پیش", - "edit_profile": "ویرایش نمایه", - "edit_oidc_managed": "برای تغییر اطلاعات خود (نام، نام خانوادگی، عکس نمایه، …)، لطفاً آن‌ها را در ارائه‌دهنده هویت خود به‌روز کنید. تغییرات شما در ورود بعدی ظاهر خواهد شد.", - "username_claim_hint": "۲ تا ۶۴ کاراکتر، حروف / ارقام / نقطه / خط تیره / زیرخط. پس از انتخاب، نام کاربری قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", - "username_already_claimed": "نام کاربری تنظیم شده و قابل تغییر نیست (کلاینت‌های DAV/NextCloud به آن وابسته‌اند).", - "given_name": "نام", - "family_name": "نام خانوادگی", - "notify_on_share": "وقتی کسی با من چیزی به اشتراک می‌گذارد، به من ایمیل بزن", - "notify_on_share_hint": "وقتی تیک‌خورده نباشد، اشتراک‌گذاری‌ها همچنان در حساب شما نمایش داده می‌شوند — فقط ایمیلی درباره آنها دریافت نخواهید کرد.", - "save_profile": "ذخیره تغییرات", - "profile_saved": "نمایه به‌روز شد", - "profile_no_changes": "تغییری برای ذخیره وجود ندارد.", - "profile_save_failed": "ذخیره ناموفق بود", - "username_taken_error": "این نام کاربری قبلاً گرفته شده است.", - "username_immutable_error": "نام کاربری شما قبلاً تنظیم شده و در اینجا قابل تغییر نیست. در صورت نیاز به تغییر نام، با مدیر تماس بگیرید.", - "change_password": "تغییر رمز عبور", - "current_password": "رمز فعلی", - "new_password": "رمز جدید", - "min_8_chars": "حداقل ۸ کاراکتر", - "confirm_password": "تأیید رمز جدید", - "update_password": "به‌روزرسانی رمز", - "updating": "در حال به‌روزرسانی…", - "password_updated": "رمز عبور با موفقیت به‌روز شد", - "passwords_no_match": "رمزها مطابقت ندارند", - "password_too_short": "رمز باید حداقل ۸ کاراکتر باشد", - "password_change_failed": "تغییر رمز ناموفق بود", - "error_network": "خطای شبکه: {{message}}", - "error_label_required": "لطفاً برچسب وارد کنید", - "error_create_pw": "ایجاد رمز ناموفق بود", - "confirm_revoke": "رمز «{{label}}» ابطال شود؟ کلاینت‌ها از کار می‌افتند.", - "error_revoke": "ابطال ناموفق بود", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "notifications": { - "file_renamed": "فایل تغییر نام داد", - "file_renamed_to": "فایل به \"{{name}}\" تغییر نام داد", - "folder_renamed": "پوشه تغییر نام داد", - "folder_renamed_to": "پوشه به \"{{name}}\" تغییر نام داد", - "file_uploaded": "فایل آپلود شد", - "file_deleted": "فایل به زباله‌دان منتقل شد", - "folder_deleted": "پوشه به زباله‌دان منتقل شد", - "item_deleted_permanently": "آیتم برای همیشه حذف شد", - "trash_emptied": "زباله‌دان خالی شد", - "title": "اعلان‌ها", - "empty": "بدون اعلان", - "link_created": "پیوند ایجاد شد", - "share_success": "پیوند اشتراک‌گذاری با موفقیت ایجاد شد", - "upload_files_section_title": "بارگذاری اینجا در دسترس نیست", - "upload_files_section_body": "برای بارگذاری فایل‌ها به بخش فایل‌ها بروید" - }, - "upload": { - "uploading": "در حال آپلود...", - "files": "فایل‌ها", - "complete": "{{count}} / {{total}} آپلود شد" - }, - "storage_quota_exceeded": "سهمیه فضای ذخیره‌سازی تجاوز کرده است", - "sharedwithme": { - "pageTitle": "به اشتراک‌گذاشته شده با من", - "pageDescription": "فایل‌ها و پوشه‌هایی که کاربران دیگر با شما به اشتراک گذاشته‌اند", - "emptyStateTitle": "هنوز چیزی با شما به اشتراک گذاشته نشده", - "emptyStateDesc": "مواردی که کاربران دیگر با شما به اشتراک می‌گذارند اینجا نمایش داده می‌شوند", - "loadMore": "بارگذاری بیشتر", - "sharedBy": "به اشتراک‌گذاشته توسط", - "colName": "نام", - "colType": "نوع", - "colSharedBy": "به اشتراک‌گذاشته توسط", - "colDate": "تاریخ اشتراک‌گذاری", - "colPermissions": "مجوزها" - }, - "groupby": { - "none": "هیچ", - "title": "گروه‌بندی بر اساس", - "owner": "مالک", - "shareDate": "تاریخ اشتراک", - "type": "نوع", - "type.folders": "پوشه‌ها", - "accessedAt": "تاریخ دسترسی", - "modifiedAt": "تاریخ تغییر", - "createdAt": "تاریخ ایجاد", - "size": "اندازه", - "favoriteDate": "تاریخ مورد علاقه", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "جدید" - }, - "dateBucket": { - "today": "امروز", - "last7days": "۷ روز گذشته", - "last30days": "۳۰ روز گذشته" - }, - "groups": { - "title": "مدیریت گروه‌ها", - "create_button": "ایجاد گروه", - "create_dialog_title": "گروه جدید", - "edit_dialog_title": "تغییر نام گروه", - "name_label": "نام", - "name_placeholder": "engineering", - "description_label": "توضیحات (اختیاری)", - "members_section": "اعضا", - "add_member_placeholder": "افزودن کاربر یا گروه…", - "no_members": "هنوز عضوی وجود ندارد.", - "remove_member": "حذف", - "delete_group": "حذف گروه", - "delete_confirm": "گروه «{name}» حذف شود؟ مجوزهای مرتبط با این گروه باطل خواهند شد.", - "empty_state": "هنوز گروهی وجود ندارد.", - "load_more": "بارگیری بیشتر", - "back_to_list": "بازگشت", - "loading": "در حال بارگذاری…", - "virtual_badge": "سامانه", - "member_count_zero": "بدون عضو", - "member_count_one": "۱ عضو", - "member_count_other": "{count} عضو", - "delete_confirm_label": "نام گروه را برای تأیید وارد کنید:", - "delete_confirm_mismatch": "نام گروه را دقیقاً برای تأیید وارد کنید.", - "virtual_internal_name": "داخلی", - "members_loading": "در حال بارگیری اعضا…", - "members_empty": "بدون عضو", - "virtual_internal_explanation": "هر کاربر داخلی روی این سرور" - }, - "myshares": { - "copyLink": "کپی پیوند", - "deleteLink": "حذف پیوند", - "notifyByEmail": "اطلاع‌رسانی از طریق ایمیل", - "notifyFailed": "ارسال اعلان ممکن نشد.", - "notifyGroupMembers": "اطلاع‌رسانی به اعضای گروه", - "notifyRateLimited": "اعلان‌های زیادی برای این گیرنده — بعداً دوباره تلاش کنید.", - "removeAccess": "حذف دسترسی", - "resendInvitation": "ارسال مجدد ایمیل دعوت" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/fr.json b/static/locales/fr.json deleted file mode 100644 index 10e04013..00000000 --- a/static/locales/fr.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Ce lien de connexion n'est plus valide", - "expired_body": "Le lien a peut-être expiré ou a déjà été utilisé. Nous pouvons vous en envoyer un nouveau — il arrivera dans votre boîte de réception dans quelques secondes.", - "resend_to": "Envoyer un nouveau lien à {{email}}", - "generic_unavailable": "Ce lien de connexion n'est plus valide. Il a peut-être déjà été utilisé ou a expiré. Demandez un nouveau lien depuis la page de connexion.", - "service_unavailable": "La connexion par lien magique n'est pas activée sur ce serveur.", - "internal_error": "Une erreur s'est produite lors de votre connexion. Veuillez réessayer.", - "resend_failure": "Une erreur s'est produite lors de l'envoi du lien. Veuillez réessayer.", - "cross_browser_title": "Continuer la connexion sur cet appareil ?", - "cross_browser_body": "Vous avez ouvert ce lien de connexion dans un navigateur ou un appareil différent de celui où vous l'avez demandé.", - "cross_browser_warning": "Si vous avez demandé ce lien, vous pouvez continuer en toute sécurité. Sinon, fermez cette page — cliquer sur Continuer connecterait quelqu'un d'autre à votre compte.", - "cross_browser_continue": "Continuer et se connecter", - "resend_confirmation_title": "Vérifiez votre boîte de réception", - "resend_confirmation_body": "Si le lien de connexion correspondait à un compte actif, un nouveau lien vient d'être envoyé. Veuillez vérifier votre boîte de réception.", - "return_link": "Retour à OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", - "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez-le en cliquant sur le lien ci-dessous :\n{{link}}\n\nLe lien est à usage unique et expire dans {{ttl_hours}} heures.\nSi vous n'attendiez pas cette invitation, vous pouvez ignorer ce message.\n\n— OxiCloud" - }, - "login": { - "subject": "Connexion à OxiCloud", - "body": "Bonjour,\n\nUtilisez le lien ci-dessous pour vous connecter à OxiCloud. Le lien est à usage unique et expire dans {{ttl_minutes}} minutes. Ouvrez-le sur le même appareil que celui où vous l'avez demandé.\n\n{{link}}\n\nSi vous n'avez pas demandé ce lien de connexion, vous pouvez ignorer ce message — aucune action supplémentaire n'est nécessaire.\n\n— OxiCloud" - }, - "kind_file": "fichier", - "kind_folder": "dossier", - "english_fallback_divider": "--- Version anglaise ci-dessous ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} a partagé un {{kind}} avec vous sur OxiCloud", - "body": "{{inviter_full}} a partagé un {{kind}} avec vous sur OxiCloud.\n\nOuvrez OxiCloud pour voir votre nouveau partage :\n{{login_link}}\n\nVous avez peut-être d'autres nouveaux partages de {{inviter}} — connectez-vous pour voir tous vos éléments partagés.\n\n— OxiCloud\n\nVous recevez ce message parce que vous avez un compte OxiCloud et que la préférence de notification de partage est activée. Vous pouvez la désactiver dans votre profil (M'avertir par e-mail quand quelqu'un partage avec moi)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Système de stockage cloud minimaliste" - }, - "nav": { - "files": "Fichiers", - "shared": "Partages", - "recent": "Récents", - "favorites": "Favoris", - "photos": "Photos", - "music": "Musique", - "trash": "Corbeille", - "sharedwithme": "Partages avec moi" - }, - "photos": { - "empty_state": "Pas encore de photos", - "empty_hint": "Téléchargez des images ou des vidéos pour les voir ici", - "items_selected": "sélectionnés", - "view_daily": "Jour", - "view_monthly": "Mois", - "view_yearly": "Année" - }, - "music": { - "create_playlist": "Créer une Playlist", - "playlists": "Playlists", - "no_playlists": "Aucune playlist", - "select_playlist": "Sélectionnez une playlist", - "select_hint": "Choisissez une playlist dans la barre latérale ou créez-en une nouvelle", - "add_tracks": "Ajouter des Pistes", - "no_tracks": "Aucune piste dans cette playlist", - "unknown_artist": "Artiste Inconnu", - "unknown_title": "Inconnu", - "confirm_delete": "Supprimer cette playlist ?", - "playlist_name": "Nom de la playlist", - "create": "Créer", - "delete": "Supprimer", - "share": "Partager", - "edit": "Modifier", - "play_all": "Tout Lire", - "shuffle": "Aléatoire", - "repeat": "Répéter", - "repeat_one": "Répéter Une", - "queue": "File d'attente", - "queue_empty": "File d'attente vide", - "not_playing": "Pas en lecture", - "play": "Lecture", - "pause": "Pause", - "previous": "Précédent", - "next": "Suivant", - "volume": "Volume", - "mute": "Muet", - "unmute": "Activer le son", - "title": "Titre", - "artist": "Artiste", - "album": "Album", - "tracks": "pistes", - "add": "Ajouter", - "added": "Ajouté !", - "added_to_playlist": "ajouté à la playlist", - "add_to_playlist": "Ajouter à la playlist", - "load_error": "Erreur de chargement des playlists", - "add_error": "Impossible d'ajouter les pistes", - "no_playlists_yet": "Pas encore de playlists. Créez-en une d'abord !", - "selected_files": "Sélectionnés :", - "error": "Erreur", - "search_audio": "Rechercher des fichiers audio…", - "no_audio_files": "Aucun fichier audio trouvé", - "selected": "sélectionnés", - "loading": "Chargement…", - "search_error": "Impossible de charger les fichiers audio", - "adding": "Ajout en cours…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Rechercher des fichiers...", - "new_folder": "Nouveau dossier", - "upload": "Téléverser", - "upload_files": "Téléverser des fichiers", - "upload_folder": "Téléverser un dossier", - "upload.uploading": "Envoi en cours...", - "upload.complete": "{count} / {total} envoyés", - "upload.files": "fichiers", - "rename": "Renommer", - "move": "Déplacer vers...", - "move_to": "Déplacer vers", - "delete": "Supprimer", - "download": "Télécharger", - "view": "Afficher", - "cancel": "Annuler", - "confirm": "Confirmer", - "share": "Partager", - "favorite": "Ajouter aux favoris", - "unfavorite": "Retirer des favoris", - "copy": "Copier", - "notify": "Notifier", - "send": "Envoyer", - "clear_recent": "Effacer les récents", - "logout": "Se déconnecter", - "create": "Créer", - "search_btn": "Rechercher", - "close": "Fermer", - "delete_permanently": "Supprimer définitivement", - "empty_trash": "Vider la corbeille", - "open_parent_folder": "Aller au dossier parent", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Apparence", - "about": "À propos d'OxiCloud", - "about_description": "Plateforme de stockage cloud construite avec Rust et Architecture Propre. Rapide, sécurisée et privée.", - "admin_panel": "Panneau d'administration", - "profile": "Mon profil", - "role_user": "Utilisateur", - "theme": { - "light": "Clair", - "dark": "Sombre", - "auto": "Comme le système" - }, - "manage_groups": "Gérer les groupes" - }, - "share": { - "dialogTitle": "Lien de partage", - "linkLabel": "Lien partagé :", - "copyLink": "Copier", - "permissions": "Permissions :", - "permissionRead": "Lecture", - "permissionWrite": "Écriture", - "permissionReshare": "Repartager", - "password": "Protection par mot de passe :", - "generatePassword": "Générer", - "expiration": "Date d'expiration :", - "update": "Mettre à jour le partage", - "remove": "Supprimer le partage", - "notifyTitle": "Envoyer une notification", - "notifyEmailLabel": "Adresse e-mail :", - "notifyMessageLabel": "Message (facultatif) :", - "notifySend": "Envoyer la notification", - "shareWithOthers": "Partager avec d'autres", - "sharePublicly": "Partager publiquement", - "shareSettings": "Paramètres de partage", - "shareCopied": "Lien copié dans le presse-papiers", - "shareCreated": "Lien de partage créé avec succès", - "shareUpdated": "Paramètres de partage mis à jour", - "shareRemoved": "Partage supprimé avec succès", - "inviteByEmail": "Inviter par e-mail — une invitation sera envoyée", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Lien de partage", - "share_linkLabel": "Lien partagé :", - "share_copyLink": "Copier", - "share_permissions": "Permissions :", - "share_permissionRead": "Lecture", - "share_permissionWrite": "Écriture", - "share_permissionReshare": "Repartager", - "share_password": "Protection par mot de passe :", - "share_generatePassword": "Générer", - "share_expiration": "Date d'expiration :", - "share_update": "Mettre à jour le partage", - "share_remove": "Supprimer le partage", - "share_notifyTitle": "Envoyer une notification", - "share_notifyEmailLabel": "Adresse e-mail :", - "share_notifyMessageLabel": "Message (facultatif) :", - "share_notifySend": "Envoyer la notification", - "shared": { - "backToFiles": "Retour aux fichiers", - "pageTitle": "Ressources partagées", - "pageDescription": "Gérez vos fichiers et dossiers partagés", - "filterType": "Type :", - "filterAll": "Tous", - "filterFiles": "Fichiers", - "filterFolders": "Dossiers", - "sortBy": "Trier par :", - "sortByName": "Nom", - "sortByDate": "Date de partage", - "sortByExpiration": "Expiration", - "search": "Rechercher", - "colName": "Nom", - "colType": "Type", - "colDateShared": "Date de partage", - "colExpiration": "Expiration", - "colPermissions": "Permissions", - "colPassword": "Mot de passe", - "colActions": "Actions", - "emptyStateTitle": "Aucune ressource partagée", - "emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", - "goToFiles": "Aller aux fichiers", - "typeFile": "Fichier", - "typeFolder": "Dossier", - "noExpiration": "Sans expiration", - "hasPassword": "Oui", - "noPassword": "Non", - "editShare": "Modifier le partage", - "notifyShare": "Notifier quelqu'un", - "copyLink": "Copier le lien", - "removeShare": "Supprimer le partage", - "linkCopied": "Lien copié dans le presse-papiers !", - "linkCopyFailed": "Erreur lors de la copie du lien", - "itemUpdated": "Paramètres de partage mis à jour", - "itemRemoved": "Partage supprimé avec succès", - "invalidEmail": "Veuillez entrer une adresse e-mail valide", - "notificationSent": "Notification envoyée avec succès", - "notificationFailed": "Erreur lors de l'envoi de la notification", - "shared_backToFiles": "Retour aux fichiers", - "shared_pageTitle": "Ressources partagées", - "shared_pageDescription": "Gérez vos fichiers et dossiers partagés", - "shared_filterType": "Type :", - "shared_filterAll": "Tous", - "shared_filterFiles": "Fichiers", - "shared_filterFolders": "Dossiers", - "shared_sortBy": "Trier par :", - "shared_sortByName": "Nom", - "shared_sortByDate": "Date de partage", - "shared_sortByExpiration": "Expiration", - "shared_search": "Rechercher", - "shared_colName": "Nom", - "shared_colType": "Type", - "shared_colDateShared": "Date de partage", - "shared_colExpiration": "Expiration", - "shared_colPermissions": "Permissions", - "shared_colPassword": "Mot de passe", - "shared_colActions": "Actions", - "shared_emptyStateTitle": "Aucune ressource partagée", - "shared_emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici", - "shared_goToFiles": "Aller aux fichiers", - "shared_typeFile": "Fichier", - "shared_typeFolder": "Dossier", - "shared_noExpiration": "Sans expiration", - "shared_hasPassword": "Oui", - "shared_noPassword": "Non", - "shared_editShare": "Modifier le partage", - "shared_notifyShare": "Notifier quelqu'un", - "shared_copyLink": "Copier le lien", - "shared_removeShare": "Supprimer le partage", - "shared_linkCopied": "Lien copié dans le presse-papiers !", - "shared_linkCopyFailed": "Erreur lors de la copie du lien", - "shared_itemUpdated": "Paramètres de partage mis à jour", - "shared_itemRemoved": "Partage supprimé avec succès", - "shared_invalidEmail": "Veuillez entrer une adresse e-mail valide", - "shared_notificationSent": "Notification envoyée avec succès", - "shared_notificationFailed": "Erreur lors de l'envoi de la notification" - }, - "files": { - "name": "Nom", - "type": "Type", - "size": "Taille", - "modified": "Modifié", - "no_files": "Aucun fichier dans ce dossier", - "empty_hint": "Téléversez des fichiers ou créez des dossiers pour commencer", - "loading": "Chargement des fichiers…", - "view_grid": "Vue en grille", - "view_list": "Vue en liste", - "file_types": { - "document": "Document", - "image": "Image", - "video": "Vidéo", - "audio": "Audio", - "pdf": "PDF", - "text": "Texte", - "folder": "Dossier", - "spreadsheet": "Tableur", - "presentation": "Présentation", - "archive": "Archive", - "installer": "Installateur", - "code": "Code" - }, - "owner": "Propriétaire" - }, - "dialogs": { - "rename_folder": "Renommer le dossier", - "rename_file": "Renommer le fichier", - "new_name": "Nouveau nom", - "new_folder_title": "Nouveau dossier", - "folder_name": "Nom du dossier", - "folder_placeholder": "Mon dossier", - "rename_title": "Renommer", - "move_file": "Déplacer le fichier", - "move_folder": "Déplacer le dossier", - "select_destination": "Sélectionnez le dossier de destination :", - "root": "Racine", - "delete_confirmation": "Êtes-vous sûr de vouloir supprimer", - "and_contents": "et tout son contenu", - "no_undo": "Cette action est irréversible", - "confirm_title": "Confirmer l'action", - "confirm_delete": "Déplacer vers la corbeille", - "confirm_delete_file": "Êtes-vous sûr de vouloir déplacer le fichier « {{name}} » vers la corbeille ?", - "confirm_delete_folder": "Êtes-vous sûr de vouloir déplacer le dossier « {{name}} » et tout son contenu vers la corbeille ?", - "confirm_permanent_delete": "Supprimer définitivement", - "confirm_permanent_delete_msg": "Êtes-vous sûr de vouloir supprimer définitivement cet élément ? Cette action est irréversible.", - "confirm_empty_trash": "Vider la corbeille", - "confirm_delete_share": "Supprimer le lien de partage", - "confirm_delete_share_msg": "Êtes-vous sûr de vouloir supprimer ce lien de partage ?", - "share_file": "Partager le fichier", - "share_folder": "Partager le dossier", - "existing_shares": "Partages existants", - "share_options": "Options de partage", - "password": "Mot de passe", - "expiration": "Expiration", - "permissions": "Permissions", - "generated_link": "Lien généré", - "notify": "Envoyer une notification", - "recipient": "Destinataire", - "message": "Message", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Déplacer vers le dossier personnel" - }, - "dropzone": { - "drag_files": "Glissez des fichiers ici ou cliquez pour sélectionner", - "drop_files": "Déposez les fichiers pour téléverser" - }, - "permissions": { - "read": "Lecture", - "write": "Écriture", - "reshare": "Repartager" - }, - "errors": { - "file_not_found": "Fichier introuvable", - "folder_not_found": "Dossier introuvable", - "delete_error": "Erreur lors de la suppression", - "upload_error": "Erreur lors du téléversement", - "rename_error": "Erreur lors du renommage", - "move_error": "Erreur lors du déplacement", - "empty_name": "Le nom ne peut pas être vide", - "name_exists": "Un fichier ou dossier portant ce nom existe déjà", - "generic_error": "Une erreur est survenue", - "group_name_invalid": "Le nom du groupe doit respecter le format préfixe d'email (lettres, chiffres, point, tiret, souligné ; 1–64 caractères).", - "group_cycle": "Ce membre créerait une référence circulaire entre groupes.", - "group_depth_exceeded": "Cette profondeur d'imbrication dépasse le maximum autorisé (8).", - "group_virtual_immutable": "Le groupe « Internal » est géré par le système et ne peut pas être modifié.", - "group_not_found": "Groupe introuvable.", - "group_name_taken": "Un groupe portant ce nom existe déjà." - }, - "breadcrumb": { - "home": "Accueil" - }, - "trash": { - "empty_trash": "Vider la corbeille", - "empty_state": "La corbeille est vide", - "original_location": "Emplacement d'origine", - "deleted_date": "Date de suppression", - "remaining": "Restant", - "actions": "Actions", - "restore": "Restaurer", - "delete_permanently": "Supprimer définitivement", - "empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés.", - "groupby": { - "remaining_days": "Jours restants", - "trashed_time": "Date de suppression" - } - }, - "daysRemaining": { - "expired": "Expiré", - "today": "Aujourd'hui", - "tomorrow": "Demain", - "inDays": "{{count}} jours" - }, - "expiryChip": { - "never": "N'expire jamais", - "expired": "Expiré", - "today": "Expire aujourd'hui", - "tomorrow": "Expire demain", - "inDays": "Expire dans {{count}} jours", - "onDate": "Expire le {{date}}" - }, - "auth": { - "login_title": "Se connecter", - "username": "Nom d'utilisateur", - "username_placeholder": "Entrez votre nom d'utilisateur", - "login_identifier": "Nom d'utilisateur ou e-mail", - "login_identifier_placeholder": "Saisissez votre nom d'utilisateur ou e-mail", - "password": "Mot de passe", - "password_placeholder": "Entrez votre mot de passe", - "login_button": "Se connecter", - "no_account": "Vous n'avez pas de compte ?", - "register": "S'inscrire", - "admin_setup": "Première fois ?", - "setup": "Configurer l'administrateur", - "register_title": "Créer un compte", - "email": "E-mail", - "email_placeholder": "Entrez votre e-mail", - "confirm_password": "Confirmer le mot de passe", - "confirm_password_placeholder": "Confirmez votre mot de passe", - "register_button": "Créer un compte", - "have_account": "Vous avez déjà un compte ?", - "login": "Se connecter", - "setup_title": "Configuration initiale", - "setup_step1": "Admin", - "setup_step2": "Système", - "setup_step3": "Terminé", - "admin_username": "Nom d'utilisateur administrateur", - "admin_email": "E-mail administrateur", - "admin_password": "Mot de passe administrateur", - "create_admin": "Créer l'administrateur", - "back_to_login": "Déjà configuré ?", - "admin_success": "Compte administrateur créé avec succès ! Vous pouvez maintenant vous connecter.", - "account_success": "Compte créé avec succès ! Vous pouvez maintenant vous connecter.", - "passwords_mismatch": "Les mots de passe ne correspondent pas", - "admin_create_error": "Erreur lors de la création du compte administrateur", - "or": "ou", - "sso_login": "Se connecter avec SSO", - "sso_login_provider": "Se connecter avec {{provider}}", - "magicLinkHint": "Pas de mot de passe ? Saisissez votre adresse e-mail et nous vous enverrons un lien de connexion à usage unique.", - "magicLinkEmailLabel": "Adresse e-mail", - "magicLinkEmailPlaceholder": "vous@exemple.com", - "magicLinkSubmit": "Envoyer le lien de connexion", - "magicLinkSent": "Si un compte existe pour cette adresse, un lien de connexion vient d'être envoyé. Consultez votre boîte de réception.", - "magicLinkUnavailable": "La connexion par e-mail n'est pas disponible sur ce serveur.", - "magicLinkNetworkError": "Impossible de joindre le serveur : {{message}}", - "magicLinkToggle": "Pas de mot de passe ? Recevez un lien par e-mail", - "passwordsMatch": "Les mots de passe correspondent", - "capsLock": "Verr. Maj activé" - }, - "storage": { - "title": "Stockage", - "calculating": "Calcul en cours...", - "used": "{{percentage}}% utilisé ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Ce type de fichier ne peut pas être prévisualisé.", - "download_file": "Télécharger le fichier", - "zoom_in": "Zoom avant", - "zoom_out": "Zoom arrière", - "zoom_reset": "Réinitialiser le zoom" - }, - "language_selector": { - "title": "Bienvenue !", - "subtitle": "Sélectionnez votre langue pour continuer", - "continue": "Continuer", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Aucun favori pour le moment", - "empty_hint": "Marquez des fichiers ou dossiers avec une étoile pour les ajouter à vos favoris", - "add": "Ajouter aux favoris", - "remove": "Retirer des favoris", - "added_title": "Ajouté aux favoris", - "added_msg": "ajouté aux favoris", - "removed_title": "Retiré des favoris", - "removed_msg": "retiré des favoris" - }, - "recent": { - "title": "Récents", - "clear": "Effacer les récents", - "accessed": "Consulté", - "empty_state": "Aucun fichier récent", - "empty_hint": "Les fichiers que vous ouvrez apparaîtront ici", - "loadMore": "Charger plus" - }, - "notifications": { - "file_renamed": "Fichier renommé", - "file_renamed_to": "Fichier renommé en « {{name}} »", - "folder_renamed": "Dossier renommé", - "folder_renamed_to": "Dossier renommé en « {{name}} »", - "file_uploaded": "Fichier téléversé", - "file_deleted": "Fichier déplacé vers la corbeille", - "folder_deleted": "Dossier déplacé vers la corbeille", - "item_deleted_permanently": "Élément supprimé définitivement", - "trash_emptied": "Corbeille vidée avec succès", - "empty": "No notifications", - "title": "Notifications", - "link_created": "Lien créé", - "share_success": "Lien de partage créé avec succès", - "upload_files_section_title": "Dépôt non disponible ici", - "upload_files_section_body": "Accédez à la section Fichiers pour déposer des fichiers" - }, - "batch": { - "one_selected": "1 élément sélectionné", - "n_selected": "{{count}} éléments sélectionnés", - "confirm_delete": "Voulez-vous vraiment déplacer {{count}} éléments vers la corbeille ?", - "move_title": "Déplacer {{count}} élément(s)", - "add_favorites": "Ajouter aux favoris", - "move_copy": "Déplacer ou copier" - }, - "admin": { - "page_title": "Panneau d'administration", - "back_to_app": "Retour à OxiCloud", - "loading": "Chargement…", - "access_denied": "Accès refusé", - "access_denied_desc": "Privilèges d'administrateur requis.", - "sign_in": "Se connecter", - "tab_dashboard": "Tableau de bord", - "tab_users": "Utilisateurs", - "tab_oidc": "SSO / OIDC", - "total_users": "Utilisateurs totaux", - "active_users": "Utilisateurs actifs", - "admins": "Admins", - "version": "Version", - "storage_overview": "Aperçu du stockage", - "used": "Utilisé", - "total_quota": "Quota total", - "usage_pct": "Utilisation %", - "users_over_80": "Utilisateurs >80% quota", - "users_over_quota": "Utilisateurs dépassant le quota", - "system": "Système", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quotas", - "enabled": "Activé", - "disabled": "Désactivé", - "active": "Actif", - "off": "Inactif", - "allow_registration": "Autoriser l'inscription publique", - "registration_warning": "L'inscription publique est désactivée. Seuls les admins peuvent créer des utilisateurs.", - "user_management": "Gestion des utilisateurs", - "create_user": "Créer un utilisateur", - "col_user": "Utilisateur", - "col_role": "Rôle", - "col_auth": "Auth", - "col_status": "Statut", - "col_storage": "Stockage", - "col_last_login": "Dernière connexion", - "col_actions": "Actions", - "loading_users": "Chargement des utilisateurs…", - "failed_load_users": "Échec du chargement", - "no_users_found": "Aucun utilisateur trouvé", - "showing_users": "Affichage {{from}}-{{to}} sur {{total}}", - "prev": "Précédent", - "next": "Suivant", - "inactive": "Inactif", - "you_badge": "(vous)", - "local": "Local", - "never": "Jamais", - "just_now": "À l'instant", - "minutes_ago": "il y a {{n}}min", - "hours_ago": "il y a {{n}}h", - "days_ago": "il y a {{n}}j", - "edit_quota_title": "Modifier le quota", - "reset_password_title": "Réinitialiser le mot de passe", - "toggle_role_title": "Changer de rôle", - "deactivate_title": "Désactiver", - "activate_title": "Activer", - "delete_title": "Supprimer", - "sso_title": "Authentification unique (OIDC / SSO)", - "enable_sso": "Activer l'authentification SSO", - "provider_name": "Nom du fournisseur", - "issuer_url": "URL de l'émetteur", - "issuer_url_hint": "URL de l'émetteur OpenID Connect", - "auto_discover": "Auto-découverte", - "discovering": "Découverte…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Laisser vide pour conserver la valeur", - "secret_configured": "Un client secret est déjà configuré", - "callback_url": "URL de rappel", - "callback_url_hint": "(enregistrer dans votre IdP)", - "advanced_settings": "Paramètres avancés", - "scopes": "Scopes", - "auto_provision": "Provisionner automatiquement les utilisateurs", - "admin_groups": "Groupes admin", - "admin_groups_hint": "Noms de groupes OIDC séparés par des virgules", - "disable_password": "Désactiver la connexion par mot de passe (OIDC uniquement)", - "password_warning": "Cela empêchera TOUTES les connexions par mot de passe !", - "test_btn": "Tester", - "save_btn": "Enregistrer", - "saving": "Enregistrement…", - "settings_saved": "Paramètres enregistrés — OIDC est maintenant {{status}}", - "quota_modal_title": "Mettre à jour le quota", - "quota_user_label": "Utilisateur :", - "new_quota": "Nouveau quota", - "quota_unlimited_hint": "0 pour illimité", - "cancel": "Annuler", - "create_user_title": "Créer un nouvel utilisateur", - "username_label": "Nom d'utilisateur", - "username_placeholder": "jeandupont", - "username_hint": "3–32 caractères", - "password_label": "Mot de passe", - "password_placeholder": "Min 8 caractères", - "email_label": "E-mail", - "email_optional": "(facultatif)", - "email_placeholder": "utilisateur@exemple.com (auto-généré si vide)", - "role_label": "Rôle", - "role_user": "Utilisateur", - "role_admin": "Admin", - "quota_label": "Quota", - "creating": "Création…", - "reset_pw_title": "Réinitialiser le mot de passe", - "new_password_label": "Nouveau mot de passe", - "resetting": "Réinitialisation…", - "reset_btn": "Réinitialiser", - "confirm_role_change": "Changer le rôle en {{role}} ?", - "confirm_deactivate": "Voulez-vous vraiment désactiver cet utilisateur ?", - "confirm_activate": "Voulez-vous vraiment activer cet utilisateur ?", - "confirm_delete_user": "SUPPRIMER l'utilisateur \"{{name}}\" ? Irréversible !", - "confirm_action": "Confirmer l'action", - "confirm_yes": "Confirmer", - "confirm_no": "Annuler", - "error_username_short": "Le nom d'utilisateur doit contenir au moins 3 caractères", - "error_password_short": "Le mot de passe doit contenir au moins 8 caractères", - "error_generic": "Échec", - "error_network": "Erreur réseau : {{message}}", - "error_create_user": "Impossible de créer l'utilisateur", - "tab_storage": "Stockage", - "storage_title": "Configuration du stockage", - "storage_current_backend": "Backend actuel", - "storage_total_blobs": "Total des blobs", - "storage_total_size": "Taille totale", - "storage_dedup_ratio": "Taux de déduplication", - "storage_backend": "Backend", - "storage_local": "Local", - "storage_s3": "Compatible S3", - "storage_provider_preset": "Préréglage du fournisseur", - "storage_preset_custom": "Personnalisé", - "storage_endpoint_url": "URL du point de terminaison", - "storage_endpoint_hint": "Laisser vide pour AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Région", - "storage_access_key": "Clé d'accès", - "storage_secret_key": "Clé secrète", - "storage_secret_configured": "Clé configurée", - "storage_key_placeholder": "Saisir une nouvelle clé", - "storage_path_style": "Forcer le style de chemin", - "storage_path_style_hint": "Requis pour MinIO et certains services compatibles S3", - "storage_test_connection": "Tester la connexion", - "storage_test_success": "Connexion réussie", - "storage_test_failure": "Échec de la connexion", - "storage_save": "Enregistrer la configuration", - "storage_saved": "Configuration enregistrée", - "storage_migration": "Migration des données", - "storage_migration_coming_soon": "Outils de migration bientôt disponibles", - "migration_status_label": "État de la migration", - "migration_start": "Démarrer la migration", - "migration_pause": "Pause", - "migration_resume": "Reprendre", - "migration_verify": "Vérifier", - "migration_complete": "Terminer", - "migration_started": "Migration démarrée", - "migration_paused_msg": "Migration en pause", - "migration_resumed_msg": "Migration reprise", - "migration_completed_msg": "Migration terminée avec succès", - "migration_verifying": "Vérification en cours...", - "migration_verify_passed": "Vérification réussie", - "migration_verify_failed": "Échec de la vérification", - "migration_failed_blobs": "Blobs échoués", - "testing": "Test en cours...", - "tab_smtp": "SMTP", - "smtp_title": "E-mail sortant (SMTP)", - "smtp_intro": "Le SMTP est configuré exclusivement via les variables d'environnement (OXICLOUD_SMTP_*). Les valeurs ci-dessous proviennent du serveur en cours d'exécution — pour les modifier, éditez l'environnement et redémarrez OxiCloud.", - "smtp_enabled_label": "État", - "smtp_enabled": "Activé", - "smtp_disabled": "Désactivé (hôte non défini)", - "smtp_test_title": "Envoyer un e-mail de test", - "smtp_test_intro": "Envoie un message de diagnostic au destinataire ci-dessous et affiche la réponse du serveur SMTP afin que vous puissiez la corréler avec les journaux de votre relais.", - "smtp_test_to": "Adresse du destinataire", - "smtp_send_test": "Envoyer l'e-mail de test", - "smtp_sending": "Envoi…", - "smtp_sent": "E-mail de test envoyé.", - "smtp_send_failed": "Échec de l'envoi.", - "smtp_server_code": "Le serveur a répondu", - "smtp_test_missing_to": "Veuillez saisir une adresse de destinataire.", - "smtp_not_configured": "Le SMTP n'est pas configuré sur ce serveur." - }, - "profile": { - "page_title": "Profil", - "back_to_app": "Retour à OxiCloud", - "loading": "Chargement…", - "not_authenticated": "Non authentifié", - "not_authenticated_desc": "Connectez-vous pour voir votre profil.", - "sign_in": "Se connecter", - "role_admin": "Administrateur", - "role_user": "Utilisateur", - "account_details": "Détails du compte", - "username": "Nom d'utilisateur", - "email": "E-mail", - "role": "Rôle", - "last_login": "Dernière connexion", - "storage": "Stockage", - "used": "Utilisé", - "quota": "Quota", - "usage": "Utilisation", - "unlimited": "Illimité", - "app_passwords": "Mots de passe d'application", - "app_pw_desc": "Générez des mots de passe pour les clients WebDAV, CalDAV et CardDAV. Chaque mot de passe n'est affiché qu'une seule fois.", - "app_pw_label_placeholder": "Libellé (ex. Thunderbird, macOS)", - "generate": "Générer", - "generating": "Génération…", - "new_password_for": "Nouveau mot de passe pour", - "copy_warning": "Copiez ce mot de passe maintenant. Vous ne pourrez plus le revoir.", - "copy_to_clipboard": "Copier dans le presse-papiers", - "col_label": "Libellé", - "col_created": "Créé", - "col_last_used": "Dernière utilisation", - "col_status": "Statut", - "active": "Actif", - "revoked": "Révoqué", - "revoke_title": "Révoquer", - "no_app_passwords": "Aucun mot de passe d'application.", - "client_sessions": "Sessions client", - "client_sessions_desc": "Générées automatiquement lors de la connexion d'un client compatible Nextcloud.", - "col_client": "Client", - "never": "Jamais", - "just_now": "À l'instant", - "minutes_ago": "il y a {{n}} min", - "hours_ago": "il y a {{n}}h", - "days_ago": "il y a {{n}} jours", - "edit_profile": "Modifier le profil", - "edit_oidc_managed": "Pour modifier vos informations (nom, prénom, photo de profil, …), veuillez les mettre à jour chez votre fournisseur d'identité. Vos changements apparaîtront à votre prochaine connexion.", - "username_claim_hint": "2 à 64 caractères, lettres / chiffres / point / tiret / souligné. Une fois choisi, le nom d'utilisateur ne peut plus être modifié (les clients DAV/NextCloud en dépendent).", - "username_already_claimed": "Nom d'utilisateur fixé et non modifiable (les clients DAV/NextCloud en dépendent).", - "given_name": "Prénom", - "family_name": "Nom", - "notify_on_share": "M'avertir par e-mail quand quelqu'un partage avec moi", - "notify_on_share_hint": "Lorsque décoché, les partages apparaissent toujours dans votre compte — vous ne recevrez simplement pas d'e-mail à leur sujet.", - "save_profile": "Enregistrer", - "profile_saved": "Profil mis à jour", - "profile_no_changes": "Aucun changement à enregistrer.", - "profile_save_failed": "Échec de l'enregistrement", - "username_taken_error": "Ce nom d'utilisateur est déjà pris.", - "username_immutable_error": "Votre nom d'utilisateur est déjà défini et ne peut plus être modifié ici. Contactez un administrateur si vous souhaitez le renommer.", - "change_password": "Changer le mot de passe", - "current_password": "Mot de passe actuel", - "new_password": "Nouveau mot de passe", - "min_8_chars": "Au moins 8 caractères", - "confirm_password": "Confirmer le nouveau mot de passe", - "update_password": "Mettre à jour le mot de passe", - "updating": "Mise à jour…", - "password_updated": "Mot de passe mis à jour avec succès", - "passwords_no_match": "Les mots de passe ne correspondent pas", - "password_too_short": "Le mot de passe doit contenir au moins 8 caractères", - "password_change_failed": "Échec du changement de mot de passe", - "error_network": "Erreur réseau : {{message}}", - "error_label_required": "Veuillez entrer un libellé", - "error_create_pw": "Impossible de créer le mot de passe", - "confirm_revoke": "Révoquer le mot de passe \"{{label}}\" ? Les clients l'utilisant ne fonctionneront plus.", - "error_revoke": "Échec de la révocation", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Téléchargement en cours...", - "files": "fichiers", - "complete": "{{count}} / {{total}} téléchargés" - }, - "storage_quota_exceeded": "Quota de stockage dépassé", - "sharedwithme": { - "pageTitle": "Partagé avec moi", - "pageDescription": "Fichiers et dossiers que d'autres utilisateurs ont partagés avec vous", - "emptyStateTitle": "Rien n'a encore été partagé avec vous", - "emptyStateDesc": "Les éléments partagés avec vous par d'autres utilisateurs apparaîtront ici", - "loadMore": "Charger plus", - "sharedBy": "Partagé par", - "colName": "Nom", - "colType": "Type", - "colSharedBy": "Partagé par", - "colDate": "Date de partage", - "colPermissions": "Permissions" - }, - "groupby": { - "none": "Aucun", - "title": "Grouper par", - "type": "Type", - "type.folders": "Dossiers", - "owner": "Propriétaire", - "shareDate": "Date de partage", - "favoriteDate": "Date d'ajout aux favoris", - "accessedAt": "Date d'accès", - "modifiedAt": "Date de modification", - "createdAt": "Date de création", - "size": "Taille", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nouveau" - }, - "dateBucket": { - "today": "Aujourd'hui", - "last7days": "7 derniers jours", - "last30days": "30 derniers jours" - }, - "groups": { - "title": "Gérer les groupes", - "create_button": "Créer un groupe", - "create_dialog_title": "Nouveau groupe", - "edit_dialog_title": "Renommer le groupe", - "name_label": "Nom", - "name_placeholder": "ingenierie", - "description_label": "Description (facultatif)", - "members_section": "Membres", - "add_member_placeholder": "Ajouter un utilisateur ou un groupe…", - "no_members": "Aucun membre pour le moment.", - "remove_member": "Retirer", - "delete_group": "Supprimer le groupe", - "delete_confirm": "Supprimer le groupe « {name} » ? Les autorisations associées à ce groupe seront révoquées.", - "empty_state": "Aucun groupe pour le moment.", - "load_more": "Charger plus", - "back_to_list": "Retour", - "loading": "Chargement…", - "virtual_badge": "Système", - "member_count_zero": "Aucun membre", - "member_count_one": "1 membre", - "member_count_other": "{count} membres", - "delete_confirm_label": "Tapez le nom du groupe pour confirmer :", - "delete_confirm_mismatch": "Tapez le nom du groupe exactement pour confirmer.", - "virtual_internal_name": "Interne", - "members_loading": "Chargement des membres…", - "members_empty": "Aucun membre", - "virtual_internal_explanation": "Tous les utilisateurs internes de ce serveur" - }, - "myshares": { - "copyLink": "Copier le lien", - "deleteLink": "Supprimer le lien", - "notifyByEmail": "Notifier par e-mail", - "notifyFailed": "Impossible d'envoyer la notification.", - "notifyGroupMembers": "Notifier les membres du groupe", - "notifyRateLimited": "Trop de notifications pour ce destinataire — réessayez plus tard.", - "removeAccess": "Retirer l'accès", - "resendInvitation": "Renvoyer l'e-mail d'invitation" - }, - "sort": { - "asc": "croissant", - "desc": "décroissant" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/hi.json b/static/locales/hi.json deleted file mode 100644 index 87725c07..00000000 --- a/static/locales/hi.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "यह साइन-इन लिंक अब वैध नहीं है", - "expired_body": "लिंक समाप्त हो गया हो सकता है या पहले से उपयोग किया जा चुका हो सकता है। हम आपको एक नया भेज सकते हैं — यह कुछ ही सेकंड में आपके इनबॉक्स में पहुँच जाएगा।", - "resend_to": "{{email}} को नया लिंक भेजें", - "generic_unavailable": "यह साइन-इन लिंक अब वैध नहीं है। यह पहले से उपयोग किया जा चुका हो सकता है या समाप्त हो गया हो सकता है। लॉगिन पृष्ठ से नया लिंक माँगें।", - "service_unavailable": "इस सर्वर पर मैजिक-लिंक साइन-इन सक्षम नहीं है।", - "internal_error": "साइन इन करते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", - "resend_failure": "लिंक भेजते समय कुछ गलत हो गया। कृपया फिर से प्रयास करें।", - "cross_browser_title": "इस डिवाइस पर साइन-इन जारी रखें?", - "cross_browser_body": "आपने यह साइन-इन लिंक उससे भिन्न ब्राउज़र या डिवाइस में खोला है जहाँ से आपने इसका अनुरोध किया था।", - "cross_browser_warning": "यदि आपने यह लिंक माँगा है, तो आगे बढ़ना सुरक्षित है। यदि नहीं, तो इस पृष्ठ को बंद कर दें — जारी रखें पर क्लिक करने से कोई और आपके खाते में साइन-इन हो जाएगा।", - "cross_browser_continue": "जारी रखें और साइन इन करें", - "resend_confirmation_title": "अपना इनबॉक्स देखें", - "resend_confirmation_body": "यदि साइन-इन लिंक किसी सक्रिय खाते का था, तो अभी एक नया लिंक भेजा गया है। कृपया अपना इनबॉक्स देखें।", - "return_link": "OxiCloud पर वापस जाएँ" - }, - "email": { - "invitation": { - "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", - "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nइसे नीचे दिए गए लिंक पर क्लिक करके खोलें:\n{{link}}\n\nलिंक केवल एक बार काम करता है और {{ttl_hours}} घंटों में समाप्त हो जाता है।\nयदि आप इस आमंत्रण की अपेक्षा नहीं कर रहे थे, तो आप इस संदेश को अनदेखा कर सकते हैं।\n\n— OxiCloud" - }, - "login": { - "subject": "OxiCloud में साइन इन करें", - "body": "नमस्ते,\n\nOxiCloud में साइन इन करने के लिए नीचे दिए गए लिंक का उपयोग करें। लिंक केवल एक बार काम करता है और {{ttl_minutes}} मिनट में समाप्त हो जाता है। इसे उसी डिवाइस पर खोलें जहाँ से आपने अनुरोध किया था।\n\n{{link}}\n\nयदि आपने यह साइन-इन लिंक नहीं माँगा था, तो आप इस संदेश को अनदेखा कर सकते हैं — किसी और कार्रवाई की आवश्यकता नहीं है।\n\n— OxiCloud" - }, - "kind_file": "फ़ाइल", - "kind_folder": "फ़ोल्डर", - "english_fallback_divider": "--- अंग्रेज़ी संस्करण नीचे ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया", - "body": "{{inviter_full}} ने OxiCloud पर आपके साथ एक {{kind}} साझा किया है।\n\nअपना नया साझाकरण देखने के लिए OxiCloud खोलें:\n{{login_link}}\n\nहो सकता है आपके पास {{inviter}} से और भी नए साझाकरण हों — साइन इन करें और अपने सभी साझा किए गए आइटम देखें।\n\n— OxiCloud\n\nआपको यह संदेश इसलिए मिल रहा है क्योंकि आपका OxiCloud खाता है और साझाकरण-सूचना प्राथमिकता चालू है। आप इसे अपनी प्रोफ़ाइल में बंद कर सकते हैं (जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें)।" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "न्यूनतम क्लाउड स्टोरेज सिस्टम" - }, - "nav": { - "files": "फ़ाइलें", - "shared": "साझा", - "recent": "हाल ही में", - "favorites": "पसंदीदा", - "photos": "फ़ोटो", - "music": "संगीत", - "trash": "रद्दी", - "sharedwithme": "मेरे साथ साझा किए गए" - }, - "photos": { - "empty_state": "अभी कोई फ़ोटो नहीं", - "empty_hint": "यहाँ देखने के लिए चित्र या वीडियो अपलोड करें", - "items_selected": "चयनित", - "view_daily": "दिन", - "view_monthly": "महीना", - "view_yearly": "वर्ष" - }, - "music": { - "create_playlist": "प्लेलिस्ट बनाएँ", - "playlists": "प्लेलिस्ट", - "no_playlists": "अभी कोई प्लेलिस्ट नहीं", - "select_playlist": "प्लेलिस्ट चुनें", - "select_hint": "साइडबार से प्लेलिस्ट चुनें या नई बनाएँ", - "add_tracks": "ट्रैक जोड़ें", - "no_tracks": "इस प्लेलिस्ट में कोई ट्रैक नहीं", - "unknown_artist": "अज्ञात कलाकार", - "unknown_title": "अज्ञात", - "confirm_delete": "इस प्लेलिस्ट को हटाएँ?", - "playlist_name": "प्लेलिस्ट का नाम", - "create": "बनाएँ", - "delete": "हटाएँ", - "share": "साझा करें", - "edit": "संपादित करें", - "play_all": "सभी चलाएँ", - "shuffle": "शफल", - "repeat": "दोहराएँ", - "repeat_one": "एक दोहराएँ", - "queue": "कतार", - "queue_empty": "कतार खाली है", - "not_playing": "नहीं चल रहा", - "play": "चलाएँ", - "pause": "रोकें", - "previous": "पिछला", - "next": "अगला", - "volume": "आवाज़", - "mute": "म्यूट", - "unmute": "अनम्यूट", - "title": "शीर्षक", - "artist": "कलाकार", - "album": "एल्बम", - "tracks": "ट्रैक", - "add": "जोड़ें", - "added": "जोड़ा गया!", - "added_to_playlist": "प्लेलिस्ट में जोड़ा गया", - "add_to_playlist": "प्लेलिस्ट में जोड़ें", - "load_error": "प्लेलिस्ट लोड करने में त्रुटि", - "add_error": "प्लेलिस्ट में ट्रैक नहीं जोड़े जा सके", - "no_playlists_yet": "अभी तक कोई प्लेलिस्ट नहीं। पहले एक बनाएं!", - "selected_files": "चयनित:", - "error": "त्रुटि", - "search_audio": "ऑडियो फ़ाइलें खोजें…", - "no_audio_files": "कोई ऑडियो फ़ाइल नहीं मिली", - "selected": "चयनित", - "loading": "लोड हो रहा है…", - "search_error": "ऑडियो फ़ाइलें लोड नहीं हो सकीं", - "adding": "जोड़ा जा रहा है…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "फ़ाइलें खोजें...", - "new_folder": "नया फ़ोल्डर", - "upload": "अपलोड", - "upload_files": "फ़ाइलें अपलोड करें", - "upload_folder": "फ़ोल्डर अपलोड करें", - "upload.uploading": "अपलोड हो रहा है...", - "upload.complete": "{count} / {total} अपलोड हुईं", - "upload.files": "फ़ाइलें", - "rename": "नाम बदलें", - "move": "यहाँ ले जाएँ...", - "move_to": "यहाँ ले जाएँ", - "delete": "हटाएँ", - "download": "डाउनलोड", - "view": "देखें", - "cancel": "रद्द करें", - "confirm": "पुष्टि करें", - "share": "साझा करें", - "favorite": "पसंदीदा में जोड़ें", - "unfavorite": "पसंदीदा से हटाएँ", - "copy": "कॉपी करें", - "notify": "सूचित करें", - "send": "भेजें", - "clear_recent": "हाल ही का साफ़ करें", - "logout": "लॉग आउट", - "create": "बनाएँ", - "search_btn": "खोजें", - "close": "बंद करें", - "delete_permanently": "स्थायी रूप से हटाएँ", - "empty_trash": "रद्दी खाली करें", - "open_parent_folder": "मूल फ़ोल्डर पर जाएं", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "दिखावट", - "about": "OxiCloud के बारे में", - "about_description": "Rust और Clean Architecture से बना क्लाउड स्टोरेज प्लेटफ़ॉर्म। तेज़, सुरक्षित और निजी।", - "admin_panel": "एडमिन पैनल", - "profile": "मेरी प्रोफ़ाइल", - "role_user": "उपयोगकर्ता", - "theme": { - "light": "हल्का", - "dark": "गहरा", - "auto": "सिस्टम जैसा" - }, - "manage_groups": "समूह प्रबंधित करें" - }, - "share": { - "dialogTitle": "शेयर लिंक", - "linkLabel": "शेयर लिंक:", - "copyLink": "कॉपी", - "permissions": "अनुमतियाँ:", - "permissionRead": "पढ़ें", - "permissionWrite": "लिखें", - "permissionReshare": "पुनः साझा करें", - "password": "पासवर्ड सुरक्षा:", - "generatePassword": "जनरेट करें", - "expiration": "समाप्ति तिथि:", - "update": "शेयर अपडेट करें", - "remove": "शेयर हटाएँ", - "notifyTitle": "सूचना भेजें", - "notifyEmailLabel": "ईमेल पता:", - "notifyMessageLabel": "संदेश (वैकल्पिक):", - "notifySend": "सूचना भेजें", - "shareWithOthers": "दूसरों के साथ साझा करें", - "sharePublicly": "सार्वजनिक रूप से साझा करें", - "shareSettings": "साझा सेटिंग्स", - "shareCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ", - "shareCreated": "शेयर लिंक सफलतापूर्वक बनाया गया", - "shareUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", - "shareRemoved": "शेयर सफलतापूर्वक हटाया गया", - "inviteByEmail": "ईमेल द्वारा आमंत्रित करें — आमंत्रण भेजा जाएगा", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "शेयर लिंक", - "share_linkLabel": "शेयर लिंक:", - "share_copyLink": "कॉपी", - "share_permissions": "अनुमतियाँ:", - "share_permissionRead": "पढ़ें", - "share_permissionWrite": "लिखें", - "share_permissionReshare": "पुनः साझा करें", - "share_password": "पासवर्ड सुरक्षा:", - "share_generatePassword": "जनरेट करें", - "share_expiration": "समाप्ति तिथि:", - "share_update": "शेयर अपडेट करें", - "share_remove": "शेयर हटाएँ", - "share_notifyTitle": "सूचना भेजें", - "share_notifyEmailLabel": "ईमेल पता:", - "share_notifyMessageLabel": "संदेश (वैकल्पिक):", - "share_notifySend": "सूचना भेजें", - "shared": { - "backToFiles": "फ़ाइलों पर वापस", - "pageTitle": "साझा संसाधन", - "pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", - "filterType": "प्रकार:", - "filterAll": "सभी", - "filterFiles": "फ़ाइलें", - "filterFolders": "फ़ोल्डर", - "sortBy": "क्रमबद्ध:", - "sortByName": "नाम", - "sortByDate": "साझा तिथि", - "sortByExpiration": "समाप्ति", - "search": "खोजें", - "colName": "नाम", - "colType": "प्रकार", - "colDateShared": "साझा तिथि", - "colExpiration": "समाप्ति", - "colPermissions": "अनुमतियाँ", - "colPassword": "पासवर्ड", - "colActions": "कार्य", - "emptyStateTitle": "अभी कोई साझा संसाधन नहीं", - "emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", - "goToFiles": "फ़ाइलों पर जाएँ", - "typeFile": "फ़ाइल", - "typeFolder": "फ़ोल्डर", - "noExpiration": "कोई समाप्ति नहीं", - "hasPassword": "हाँ", - "noPassword": "नहीं", - "editShare": "शेयर संपादित करें", - "notifyShare": "किसी को सूचित करें", - "copyLink": "लिंक कॉपी करें", - "removeShare": "शेयर हटाएँ", - "linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", - "linkCopyFailed": "लिंक कॉपी करने में विफल", - "itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", - "itemRemoved": "शेयर सफलतापूर्वक हटाया गया", - "invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", - "notificationSent": "सूचना सफलतापूर्वक भेजी गई", - "notificationFailed": "सूचना भेजने में विफल", - "shared_backToFiles": "फ़ाइलों पर वापस", - "shared_pageTitle": "साझा संसाधन", - "shared_pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें", - "shared_filterType": "प्रकार:", - "shared_filterAll": "सभी", - "shared_filterFiles": "फ़ाइलें", - "shared_filterFolders": "फ़ोल्डर", - "shared_sortBy": "क्रमबद्ध:", - "shared_sortByName": "नाम", - "shared_sortByDate": "साझा तिथि", - "shared_sortByExpiration": "समाप्ति", - "shared_search": "खोजें", - "shared_colName": "नाम", - "shared_colType": "प्रकार", - "shared_colDateShared": "साझा तिथि", - "shared_colExpiration": "समाप्ति", - "shared_colPermissions": "अनुमतियाँ", - "shared_colPassword": "पासवर्ड", - "shared_colActions": "कार्य", - "shared_emptyStateTitle": "अभी कोई साझा संसाधन नहीं", - "shared_emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे", - "shared_goToFiles": "फ़ाइलों पर जाएँ", - "shared_typeFile": "फ़ाइल", - "shared_typeFolder": "फ़ोल्डर", - "shared_noExpiration": "कोई समाप्ति नहीं", - "shared_hasPassword": "हाँ", - "shared_noPassword": "नहीं", - "shared_editShare": "शेयर संपादित करें", - "shared_notifyShare": "किसी को सूचित करें", - "shared_copyLink": "लिंक कॉपी करें", - "shared_removeShare": "शेयर हटाएँ", - "shared_linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!", - "shared_linkCopyFailed": "लिंक कॉपी करने में विफल", - "shared_itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं", - "shared_itemRemoved": "शेयर सफलतापूर्वक हटाया गया", - "shared_invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें", - "shared_notificationSent": "सूचना सफलतापूर्वक भेजी गई", - "shared_notificationFailed": "सूचना भेजने में विफल" - }, - "files": { - "name": "नाम", - "type": "प्रकार", - "size": "आकार", - "modified": "संशोधित", - "no_files": "इस फ़ोल्डर में कोई फ़ाइल नहीं", - "empty_hint": "शुरू करने के लिए ह़ैलें अपलोड करें या होल्डर बनाएँ", - "loading": "फ़ाइलें लोड हो रही हैं…", - "view_grid": "ग्रिड दृश्य", - "view_list": "सूची दृश्य", - "file_types": { - "document": "दस्तावेज़", - "image": "चित्र", - "video": "वीडियो", - "audio": "ऑडियो", - "pdf": "PDF", - "text": "टेक्स्ट", - "folder": "फ़ोल्डर", - "spreadsheet": "स्प्रेडशीट", - "presentation": "प्रेज़ेंटेशन", - "archive": "संग्रह", - "installer": "इंस्टॉलर", - "code": "कोड" - }, - "owner": "स्वामी" - }, - "dialogs": { - "rename_folder": "फ़ोल्डर का नाम बदलें", - "rename_file": "फ़ाइल का नाम बदलें", - "new_name": "नया नाम", - "new_folder_title": "नया फ़ोल्डर", - "folder_name": "फ़ोल्डर का नाम", - "folder_placeholder": "मेरा फ़ोल्डर", - "rename_title": "नाम बदलें", - "move_file": "फ़ाइल ले जाएँ", - "move_folder": "फ़ोल्डर ले जाएँ", - "select_destination": "गंतव्य फ़ोल्डर चुनें:", - "select_this_folder": "यह फ़ोल्डर चुनें", - "go_to_parent": ".. (पैरेंट फ़ोल्डर)", - "no_subfolders": "कोई सब-फ़ोल्डर नहीं", - "root": "रूट", - "delete_confirmation": "क्या आप वाकई हटाना चाहते हैं", - "and_contents": "और इसकी सभी सामग्री", - "no_undo": "यह कार्य पूर्ववत नहीं किया जा सकता", - "confirm_title": "कार्य की पुष्टि करें", - "confirm_delete": "रद्दी में भेजें", - "confirm_delete_file": "क्या आप वाकई फ़ाइल \"{{name}}\" को रद्दी में भेजना चाहते हैं?", - "confirm_delete_folder": "क्या आप वाकई फ़ोल्डर \"{{name}}\" और उसकी सभी सामग्री को रद्दी में भेजना चाहते हैं?", - "confirm_permanent_delete": "स्थायी रूप से हटाएँ", - "confirm_permanent_delete_msg": "क्या आप वाकई इस आइटम को स्थायी रूप से हटाना चाहते हैं? यह कार्य पूर्ववत नहीं किया जा सकता।", - "confirm_empty_trash": "रद्दी खाली करें", - "confirm_delete_share": "शेयर लिंक हटाएँ", - "confirm_delete_share_msg": "क्या आप वाकई इस शेयर लिंक को हटाना चाहते हैं?", - "share_file": "फ़ाइल साझा करें", - "share_folder": "फ़ोल्डर साझा करें", - "existing_shares": "मौजूदा शेयर", - "share_options": "शेयर विकल्प", - "password": "पासवर्ड", - "expiration": "समाप्ति", - "permissions": "अनुमतियाँ", - "generated_link": "जनरेट किया गया लिंक", - "notify": "सूचना भेजें", - "recipient": "प्राप्तकर्ता", - "message": "संदेश", - "move_to_home": "होम फ़ोल्डर में ले जाएं" - }, - "dropzone": { - "drag_files": "फ़ाइलें यहाँ खींचें या चुनने के लिए क्लिक करें", - "drop_files": "अपलोड करने के लिए फ़ाइलें छोड़ें" - }, - "permissions": { - "read": "पढ़ें", - "write": "लिखें", - "reshare": "पुनः साझा करें" - }, - "errors": { - "file_not_found": "फ़ाइल नहीं मिली", - "folder_not_found": "फ़ोल्डर नहीं मिला", - "delete_error": "हटाने में त्रुटि", - "upload_error": "फ़ाइल अपलोड करने में त्रुटि", - "rename_error": "नाम बदलने में त्रुटि", - "move_error": "ले जाने में त्रुटि", - "empty_name": "नाम खाली नहीं हो सकता", - "name_exists": "इस नाम की फ़ाइल या फ़ोल्डर पहले से मौजूद है", - "generic_error": "एक त्रुटि हुई है", - "group_name_invalid": "समूह का नाम ईमेल उपसर्ग प्रारूप के अनुरूप होना चाहिए (अक्षर, अंक, बिंदु, डैश, अंडरस्कोर; 1–64 वर्ण).", - "group_cycle": "यह सदस्य समूहों के बीच चक्रीय संदर्भ बनाएगा।", - "group_depth_exceeded": "यह नेस्टिंग गहराई अनुमत अधिकतम (8) से अधिक है।", - "group_virtual_immutable": "«Internal» समूह सिस्टम द्वारा प्रबंधित है और इसे संशोधित नहीं किया जा सकता।", - "group_not_found": "समूह नहीं मिला।", - "group_name_taken": "इस नाम का एक समूह पहले से मौजूद है।" - }, - "breadcrumb": { - "home": "होम" - }, - "trash": { - "empty_trash": "रद्दी खाली करें", - "empty_state": "रद्दी खाली है", - "original_location": "मूल स्थान", - "deleted_date": "हटाने की तिथि", - "remaining": "शेष", - "actions": "कार्य", - "restore": "पुनर्स्थापित करें", - "delete_permanently": "स्थायी रूप से हटाएँ", - "empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।", - "groupby": { - "remaining_days": "शेष दिन", - "trashed_time": "हटाने का समय" - } - }, - "daysRemaining": { - "expired": "समाप्त", - "today": "आज", - "tomorrow": "कल", - "inDays": "{{count}} दिन" - }, - "expiryChip": { - "never": "कभी समाप्त नहीं होता", - "expired": "समाप्त", - "today": "आज समाप्त होता है", - "tomorrow": "कल समाप्त होता है", - "inDays": "{{count}} दिनों में समाप्त होता है", - "onDate": "{{date}} को समाप्त होता है" - }, - "auth": { - "login_title": "साइन इन", - "username": "उपयोगकर्ता नाम", - "username_placeholder": "अपना उपयोगकर्ता नाम दर्ज करें", - "login_identifier": "उपयोगकर्ता नाम या ईमेल", - "login_identifier_placeholder": "अपना उपयोगकर्ता नाम या ईमेल दर्ज करें", - "password": "पासवर्ड", - "password_placeholder": "अपना पासवर्ड दर्ज करें", - "login_button": "साइन इन", - "no_account": "खाता नहीं है?", - "register": "साइन अप करें", - "admin_setup": "पहली बार?", - "setup": "एडमिन सेटअप करें", - "register_title": "खाता बनाएँ", - "email": "ईमेल", - "email_placeholder": "अपना ईमेल दर्ज करें", - "confirm_password": "पासवर्ड की पुष्टि करें", - "confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें", - "register_button": "खाता बनाएँ", - "have_account": "पहले से खाता है?", - "login": "साइन इन", - "setup_title": "प्रारंभिक सेटअप", - "setup_step1": "एडमिन", - "setup_step2": "सिस्टम", - "setup_step3": "पूर्ण", - "admin_username": "एडमिन उपयोगकर्ता नाम", - "admin_email": "एडमिन ईमेल", - "admin_password": "एडमिन पासवर्ड", - "create_admin": "एडमिन बनाएँ", - "back_to_login": "पहले से सेटअप है?", - "admin_success": "एडमिन खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", - "account_success": "खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।", - "passwords_mismatch": "पासवर्ड मेल नहीं खाते", - "admin_create_error": "एडमिन खाता बनाने में त्रुटि", - "or": "या", - "sso_login": "SSO से साइन इन करें", - "sso_login_provider": "{{provider}} से साइन इन करें", - "magicLinkHint": "पासवर्ड नहीं है? अपना ईमेल दर्ज करें और हम आपको एक बार उपयोग होने वाला साइन-इन लिंक भेज देंगे।", - "magicLinkEmailLabel": "ईमेल पता", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "साइन-इन लिंक भेजें", - "magicLinkSent": "यदि उस ईमेल के लिए कोई खाता मौजूद है, तो एक साइन-इन लिंक भेज दिया गया है। अपना इनबॉक्स देखें।", - "magicLinkUnavailable": "इस सर्वर पर ईमेल द्वारा साइन-इन उपलब्ध नहीं है।", - "magicLinkNetworkError": "सर्वर से कनेक्ट नहीं हो सका: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "स्टोरेज", - "calculating": "गणना हो रही है...", - "used": "{{percentage}}% उपयोग ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "इस फ़ाइल प्रकार का पूर्वावलोकन नहीं किया जा सकता।", - "download_file": "फ़ाइल डाउनलोड करें", - "zoom_in": "ज़ूम इन", - "zoom_out": "ज़ूम आउट", - "zoom_reset": "ज़ूम रीसेट" - }, - "language_selector": { - "title": "स्वागत है!", - "subtitle": "जारी रखने के लिए अपनी भाषा चुनें", - "continue": "आगे बढ़ें", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "hi": "हिन्दी", - "ar": "العربية", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "अभी कोई पसंदीदा नहीं", - "empty_hint": "पसंदीदा में जोड़ने के लिए फ़ाइलों या फ़ोल्डर को स्टार करें", - "add": "पसंदीदा में जोड़ें", - "remove": "पसंदीदा से हटाएँ", - "added_title": "पसंदीदा में जोड़ा गया", - "added_msg": "पसंदीदा में जोड़ा गया", - "removed_title": "पसंदीदा से हटाया गया", - "removed_msg": "पसंदीदा से हटाया गया" - }, - "recent": { - "title": "हाल ही में", - "clear": "हाल ही का साफ़ करें", - "accessed": "एक्सेस किया", - "empty_state": "कोई हाल की फ़ाइलें नहीं", - "empty_hint": "जो फ़ाइलें आप खोलेंगे वे यहाँ दिखेंगी", - "loadMore": "और लोड करें" - }, - "notifications": { - "file_renamed": "फ़ाइल का नाम बदला गया", - "file_renamed_to": "फ़ाइल का नाम \"{{name}}\" रखा गया", - "folder_renamed": "फ़ोल्डर का नाम बदला गया", - "folder_renamed_to": "फ़ोल्डर का नाम \"{{name}}\" रखा गया", - "file_uploaded": "फ़ाइल अपलोड हुई", - "file_deleted": "फ़ाइल रद्दी में भेजी गई", - "folder_deleted": "फ़ोल्डर रद्दी में भेजा गया", - "item_deleted_permanently": "आइटम स्थायी रूप से हटाया गया", - "trash_emptied": "रद्दी सफलतापूर्वक खाली की गई", - "title": "सूचनाएँ", - "empty": "कोई सूचना नहीं", - "link_created": "लिंक बनाया गया", - "share_success": "शेयर लिंक सफलतापूर्वक बनाया गया", - "upload_files_section_title": "यहाँ अपलोड उपलब्ध नहीं है", - "upload_files_section_body": "फ़ाइलें अपलोड करने के लिए फ़ाइलें अनुभाग पर जाएँ" - }, - "batch": { - "one_selected": "1 आइटम चयनित", - "n_selected": "{{count}} आइटम चयनित", - "confirm_delete": "क्या आप वाकई {{count}} आइटम रद्दी में भेजना चाहते हैं?", - "move_title": "{{count}} आइटम ले जाएँ", - "add_favorites": "पसंदीदा में जोड़ें", - "move_copy": "ले जाएँ या कॉपी करें" - }, - "admin": { - "page_title": "एडमिन पैनल", - "back_to_app": "OxiCloud पर वापस", - "loading": "लोड हो रहा है…", - "access_denied": "पहुंच अस्वीकृत", - "access_denied_desc": "व्यवस्थापक विशेषाधिकार आवश्यक।", - "sign_in": "साइन इन", - "tab_dashboard": "डैशबोर्ड", - "tab_users": "उपयोगकर्ता", - "tab_oidc": "SSO / OIDC", - "total_users": "कुल उपयोगकर्ता", - "active_users": "सक्रिय उपयोगकर्ता", - "admins": "व्यवस्थापक", - "version": "संस्करण", - "storage_overview": "स्टोरेज अवलोकन", - "used": "उपयोग किया", - "total_quota": "कुल कोटा", - "usage_pct": "उपयोग %", - "users_over_80": ">80% कोटा वाले", - "users_over_quota": "कोटा से अधिक", - "system": "सिस्टम", - "auth_label": "प्रमाणीकरण", - "oidc_label": "OIDC", - "quotas_label": "कोटा", - "enabled": "सक्षम", - "disabled": "अक्षम", - "active": "सक्रिय", - "off": "बंद", - "allow_registration": "सार्वजनिक पंजीकरण की अनुमति", - "registration_warning": "सार्वजनिक पंजीकरण अक्षम है। केवल व्यवस्थापक उपयोगकर्ता बना सकते हैं।", - "user_management": "उपयोगकर्ता प्रबंधन", - "create_user": "उपयोगकर्ता बनाएं", - "col_user": "उपयोगकर्ता", - "col_role": "भूमिका", - "col_auth": "प्रमाणीकरण", - "col_status": "स्थिति", - "col_storage": "स्टोरेज", - "col_last_login": "अंतिम लॉगिन", - "col_actions": "कार्रवाई", - "loading_users": "उपयोगकर्ता लोड हो रहे हैं…", - "failed_load_users": "लोड करने में विफल", - "no_users_found": "कोई उपयोगकर्ता नहीं मिला", - "showing_users": "{{from}}-{{to}} / {{total}} दिखा रहे हैं", - "prev": "पिछला", - "next": "अगला", - "inactive": "निष्क्रिय", - "you_badge": "(आप)", - "local": "स्थानीय", - "never": "कभी नहीं", - "just_now": "अभी", - "minutes_ago": "{{n}} मिनट पहले", - "hours_ago": "{{n}} घंटे पहले", - "days_ago": "{{n}} दिन पहले", - "edit_quota_title": "कोटा संपादित करें", - "reset_password_title": "पासवर्ड रीसेट", - "toggle_role_title": "भूमिका बदलें", - "deactivate_title": "निष्क्रिय करें", - "activate_title": "सक्रिय करें", - "delete_title": "हटाएं", - "sso_title": "सिंगल साइन-ऑन (OIDC / SSO)", - "enable_sso": "SSO सक्षम करें", - "provider_name": "प्रदाता का नाम", - "issuer_url": "जारीकर्ता URL", - "issuer_url_hint": "OpenID Connect जारीकर्ता URL", - "auto_discover": "स्वतः खोज", - "discovering": "खोज रहे हैं…", - "client_id": "क्लाइंट ID", - "client_secret": "क्लाइंट सीक्रेट", - "client_secret_placeholder": "वर्तमान मान बनाए रखने के लिए खाली छोड़ें", - "secret_configured": "क्लाइंट सीक्रेट पहले से कॉन्फ़िगर है", - "callback_url": "कॉलबैक URL", - "callback_url_hint": "(अपने IdP में पंजीकृत करें)", - "advanced_settings": "उन्नत सेटिंग्स", - "scopes": "स्कोप", - "auto_provision": "पहले लॉगिन पर स्वतः प्रावधान", - "admin_groups": "व्यवस्थापक समूह", - "admin_groups_hint": "अल्पविराम-पृथक OIDC समूह नाम", - "disable_password": "पासवर्ड लॉगिन अक्षम (केवल OIDC)", - "password_warning": "सभी पासवर्ड लॉगिन रुक जाएंगे!", - "test_btn": "परीक्षण", - "save_btn": "सहेजें", - "saving": "सहेज रहे हैं…", - "settings_saved": "सेटिंग्स सहेजी गईं — OIDC अब {{status}}", - "quota_modal_title": "स्टोरेज कोटा अपडेट", - "quota_user_label": "उपयोगकर्ता:", - "new_quota": "नया कोटा", - "quota_unlimited_hint": "असीमित के लिए 0", - "cancel": "रद्द करें", - "create_user_title": "नया उपयोगकर्ता बनाएं", - "username_label": "उपयोगकर्ता नाम", - "username_placeholder": "username", - "username_hint": "3–32 अक्षर", - "password_label": "पासवर्ड", - "password_placeholder": "न्यूनतम 8 अक्षर", - "email_label": "ईमेल", - "email_optional": "(वैकल्पिक)", - "email_placeholder": "user@example.com (खाली होने पर स्वतः)", - "role_label": "भूमिका", - "role_user": "उपयोगकर्ता", - "role_admin": "व्यवस्थापक", - "quota_label": "कोटा", - "creating": "बना रहे हैं…", - "reset_pw_title": "पासवर्ड रीसेट", - "new_password_label": "नया पासवर्ड", - "resetting": "रीसेट हो रहा है…", - "reset_btn": "रीसेट", - "confirm_role_change": "भूमिका {{role}} में बदलें?", - "confirm_deactivate": "इस उपयोगकर्ता को निष्क्रिय करें?", - "confirm_activate": "इस उपयोगकर्ता को सक्रिय करें?", - "confirm_delete_user": "उपयोगकर्ता \"{{name}}\" हटाएं? पूर्ववत नहीं होगा!", - "confirm_action": "कार्रवाई की पुष्टि", - "confirm_yes": "पुष्टि", - "confirm_no": "रद्द", - "error_username_short": "नाम कम से कम 3 अक्षर", - "error_password_short": "पासवर्ड कम से कम 8 अक्षर", - "error_generic": "विफल", - "error_network": "नेटवर्क त्रुटि: {{message}}", - "error_create_user": "उपयोगकर्ता बनाने में विफल", - "tab_storage": "स्टोरेज", - "storage_title": "स्टोरेज कॉन्फ़िगरेशन", - "storage_current_backend": "वर्तमान बैकएंड", - "storage_total_blobs": "कुल ब्लॉब्स", - "storage_total_size": "कुल आकार", - "storage_dedup_ratio": "डीडुप्लिकेशन अनुपात", - "storage_backend": "बैकएंड", - "storage_local": "स्थानीय", - "storage_s3": "S3 संगत", - "storage_provider_preset": "प्रदाता प्रीसेट", - "storage_preset_custom": "कस्टम", - "storage_endpoint_url": "एंडपॉइंट URL", - "storage_endpoint_hint": "AWS S3 के लिए खाली छोड़ें", - "storage_bucket": "बकेट", - "storage_region": "क्षेत्र", - "storage_access_key": "एक्सेस की", - "storage_secret_key": "सीक्रेट की", - "storage_secret_configured": "की कॉन्फ़िगर की गई", - "storage_key_placeholder": "नई की दर्ज करें", - "storage_path_style": "पाथ स्टाइल फ़ोर्स करें", - "storage_path_style_hint": "MinIO और कुछ S3-संगत सेवाओं के लिए आवश्यक", - "storage_test_connection": "कनेक्शन परीक्षण", - "storage_test_success": "कनेक्शन सफल", - "storage_test_failure": "कनेक्शन विफल", - "storage_save": "कॉन्फ़िगरेशन सहेजें", - "storage_saved": "कॉन्फ़िगरेशन सहेजी गई", - "storage_migration": "डेटा माइग्रेशन", - "storage_migration_coming_soon": "माइग्रेशन टूल्स जल्द आ रहे हैं", - "migration_status_label": "माइग्रेशन स्थिति", - "migration_start": "माइग्रेशन शुरू करें", - "migration_pause": "रोकें", - "migration_resume": "फिर से शुरू करें", - "migration_verify": "सत्यापित करें", - "migration_complete": "पूर्ण करें", - "migration_started": "माइग्रेशन शुरू हुआ", - "migration_paused_msg": "माइग्रेशन रोका गया", - "migration_resumed_msg": "माइग्रेशन फिर से शुरू हुआ", - "migration_completed_msg": "माइग्रेशन सफलतापूर्वक पूर्ण हुआ", - "migration_verifying": "सत्यापन हो रहा है...", - "migration_verify_passed": "सत्यापन पास", - "migration_verify_failed": "सत्यापन विफल", - "migration_failed_blobs": "विफल ब्लॉब्स", - "testing": "परीक्षण हो रहा है...", - "smtp_disabled": "अक्षम (होस्ट सेट नहीं)", - "smtp_enabled": "सक्षम", - "smtp_enabled_label": "स्थिति", - "smtp_intro": "SMTP केवल पर्यावरण चर (OXICLOUD_SMTP_*) के माध्यम से कॉन्फ़िगर किया जाता है। नीचे दिए गए मान चल रहे सर्वर से पढ़े जाते हैं — उन्हें बदलने के लिए, पर्यावरण संपादित करें और OxiCloud को पुनः आरंभ करें।", - "smtp_not_configured": "इस सर्वर पर SMTP कॉन्फ़िगर नहीं है।", - "smtp_send_failed": "भेजना विफल।", - "smtp_send_test": "परीक्षण ईमेल भेजें", - "smtp_sending": "भेजा जा रहा है…", - "smtp_sent": "परीक्षण ईमेल भेजा गया।", - "smtp_server_code": "सर्वर का उत्तर", - "smtp_test_intro": "नीचे दिए गए प्राप्तकर्ता को एक पूर्व-निर्धारित निदान संदेश भेजता है और SMTP सर्वर का उत्तर रिपोर्ट करता है ताकि आप इसे अपने रिले लॉग्स से मिला सकें।", - "smtp_test_missing_to": "प्राप्तकर्ता पता दर्ज करें।", - "smtp_test_title": "परीक्षण ईमेल भेजें", - "smtp_test_to": "प्राप्तकर्ता का पता", - "smtp_title": "जावक ईमेल (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "प्रोफ़ाइल", - "back_to_app": "OxiCloud पर वापस", - "loading": "लोड हो रहा है…", - "not_authenticated": "प्रमाणित नहीं", - "not_authenticated_desc": "अपना प्रोफ़ाइल देखने के लिए साइन इन करें।", - "sign_in": "साइन इन", - "role_admin": "व्यवस्थापक", - "role_user": "उपयोगकर्ता", - "account_details": "खाता विवरण", - "username": "उपयोगकर्ता नाम", - "email": "ईमेल", - "role": "भूमिका", - "last_login": "अंतिम लॉगिन", - "storage": "स्टोरेज", - "used": "उपयोग किया", - "quota": "कोटा", - "usage": "उपयोग", - "unlimited": "असीमित", - "app_passwords": "ऐप पासवर्ड", - "app_pw_desc": "WebDAV, CalDAV और CardDAV क्लाइंट के लिए पासवर्ड जनरेट करें। प्रत्येक पासवर्ड केवल एक बार दिखाया जाता है।", - "app_pw_label_placeholder": "लेबल (जैसे Thunderbird, macOS)", - "generate": "जनरेट करें", - "generating": "जनरेट हो रहा है…", - "new_password_for": "नया पासवर्ड", - "copy_warning": "इस पासवर्ड को अभी कॉपी करें। आप इसे दोबारा नहीं देख पाएंगे।", - "copy_to_clipboard": "क्लिपबोर्ड पर कॉपी करें", - "col_label": "लेबल", - "col_created": "बनाया गया", - "col_last_used": "अंतिम उपयोग", - "col_status": "स्थिति", - "active": "सक्रिय", - "revoked": "रद्द", - "revoke_title": "रद्द करें", - "no_app_passwords": "अभी तक कोई ऐप पासवर्ड नहीं।", - "client_sessions": "क्लाइंट सत्र", - "client_sessions_desc": "Nextcloud-संगत क्लाइंट कनेक्ट करने पर स्वतः जनरेट।", - "col_client": "क्लाइंट", - "never": "कभी नहीं", - "just_now": "अभी", - "minutes_ago": "{{n}} मिनट पहले", - "hours_ago": "{{n}} घंटे पहले", - "days_ago": "{{n}} दिन पहले", - "edit_profile": "प्रोफ़ाइल संपादित करें", - "edit_oidc_managed": "अपनी जानकारी (नाम, प्रथम नाम, प्रोफ़ाइल चित्र, …) बदलने के लिए, कृपया अपने पहचान प्रदाता पर इसे अद्यतन करें। आपके परिवर्तन अगले साइन-इन पर दिखाई देंगे।", - "username_claim_hint": "2–64 अक्षर, अक्षर / अंक / डॉट / डैश / अंडरस्कोर। एक बार चुनने के बाद, उपयोगकर्ता नाम नहीं बदला जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", - "username_already_claimed": "उपयोगकर्ता नाम सेट है और बदला नहीं जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।", - "given_name": "प्रथम नाम", - "family_name": "अंतिम नाम", - "notify_on_share": "जब कोई मेरे साथ साझा करे तो मुझे ईमेल भेजें", - "notify_on_share_hint": "जब अनचेक किया जाए, तो साझाकरण आपके खाते में दिखाई देते रहेंगे — आपको बस उनके बारे में ईमेल नहीं मिलेगा।", - "save_profile": "परिवर्तन सहेजें", - "profile_saved": "प्रोफ़ाइल अद्यतन की गई", - "profile_no_changes": "सहेजने के लिए कोई परिवर्तन नहीं।", - "profile_save_failed": "सहेजना विफल", - "username_taken_error": "यह उपयोगकर्ता नाम पहले से उपयोग में है।", - "username_immutable_error": "आपका उपयोगकर्ता नाम पहले से सेट है और यहाँ नहीं बदला जा सकता। यदि आपको नाम बदलने की आवश्यकता है तो किसी व्यवस्थापक से संपर्क करें।", - "change_password": "पासवर्ड बदलें", - "current_password": "वर्तमान पासवर्ड", - "new_password": "नया पासवर्ड", - "min_8_chars": "कम से कम 8 अक्षर", - "confirm_password": "नया पासवर्ड पुष्टि करें", - "update_password": "पासवर्ड अपडेट करें", - "updating": "अपडेट हो रहा है…", - "password_updated": "पासवर्ड सफलतापूर्वक अपडेट हुआ", - "passwords_no_match": "पासवर्ड मेल नहीं खाते", - "password_too_short": "पासवर्ड कम से कम 8 अक्षर का होना चाहिए", - "password_change_failed": "पासवर्ड बदलने में विफल", - "error_network": "नेटवर्क त्रुटि: {{message}}", - "error_label_required": "कृपया एक लेबल दर्ज करें", - "error_create_pw": "ऐप पासवर्ड बनाने में विफल", - "confirm_revoke": "ऐप पासवर्ड \"{{label}}\" रद्द करें? इसका उपयोग करने वाले क्लाइंट काम करना बंद कर देंगे।", - "error_revoke": "रद्द करने में विफल", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "अपलोड हो रहा है...", - "files": "फ़ाइलें", - "complete": "{{count}} / {{total}} अपलोड हुए" - }, - "storage_quota_exceeded": "स्टोरेज कोटा पार हो गया", - "sharedwithme": { - "pageTitle": "मेरे साथ साझा किया", - "pageDescription": "फ़ाइलें और फ़ोल्डर जो अन्य उपयोगकर्ताओं ने आपके साथ साझा किए हैं", - "emptyStateTitle": "अभी तक आपके साथ कुछ भी साझा नहीं किया गया", - "emptyStateDesc": "अन्य उपयोगकर्ताओं द्वारा आपके साथ साझा किए गए आइटम यहाँ दिखाई देंगे", - "loadMore": "और लोड करें", - "sharedBy": "द्वारा साझा किया", - "colName": "नाम", - "colType": "प्रकार", - "colSharedBy": "द्वारा साझा किया", - "colDate": "साझाकरण तिथि", - "colPermissions": "अनुमतियाँ" - }, - "groupby": { - "none": "कोई नहीं", - "title": "इसके अनुसार समूहीकृत करें", - "owner": "स्वामी", - "shareDate": "साझा तिथि", - "type": "प्रकार", - "type.folders": "फ़ोल्डर", - "accessedAt": "पहुँच की तारीख", - "modifiedAt": "संशोधन की तारीख", - "createdAt": "बनाने की तारीख", - "size": "आकार", - "favoriteDate": "पसंदीदा की तारीख", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "नया" - }, - "dateBucket": { - "today": "आज", - "last7days": "पिछले 7 दिन", - "last30days": "पिछले 30 दिन" - }, - "groups": { - "title": "समूह प्रबंधित करें", - "create_button": "समूह बनाएँ", - "create_dialog_title": "नया समूह", - "edit_dialog_title": "समूह का नाम बदलें", - "name_label": "नाम", - "name_placeholder": "engineering", - "description_label": "विवरण (वैकल्पिक)", - "members_section": "सदस्य", - "add_member_placeholder": "उपयोगकर्ता या समूह जोड़ें…", - "no_members": "अभी तक कोई सदस्य नहीं।", - "remove_member": "हटाएँ", - "delete_group": "समूह हटाएँ", - "delete_confirm": "समूह \"{name}\" को हटाएँ? इस समूह से जुड़ी अनुमतियाँ रद्द कर दी जाएँगी।", - "empty_state": "अभी तक कोई समूह नहीं।", - "load_more": "और लोड करें", - "back_to_list": "वापस", - "loading": "लोड हो रहा है…", - "virtual_badge": "सिस्टम", - "member_count_zero": "कोई सदस्य नहीं", - "member_count_one": "1 सदस्य", - "member_count_other": "{count} सदस्य", - "delete_confirm_label": "पुष्टि के लिए समूह का नाम लिखें:", - "delete_confirm_mismatch": "पुष्टि के लिए समूह का नाम बिल्कुल वैसा ही लिखें।", - "virtual_internal_name": "आंतरिक", - "members_loading": "सदस्य लोड हो रहे हैं…", - "members_empty": "कोई सदस्य नहीं", - "virtual_internal_explanation": "इस सर्वर पर हर आंतरिक उपयोगकर्ता" - }, - "myshares": { - "copyLink": "लिंक कॉपी करें", - "deleteLink": "लिंक हटाएँ", - "notifyByEmail": "ईमेल से सूचित करें", - "notifyFailed": "सूचना नहीं भेजी जा सकी।", - "notifyGroupMembers": "समूह के सदस्यों को सूचित करें", - "notifyRateLimited": "इस प्राप्तकर्ता के लिए बहुत अधिक सूचनाएँ — बाद में पुनः प्रयास करें।", - "removeAccess": "पहुँच हटाएँ", - "resendInvitation": "आमंत्रण ईमेल पुनः भेजें" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/it.json b/static/locales/it.json deleted file mode 100644 index 31bb6052..00000000 --- a/static/locales/it.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Questo link di accesso non è più valido", - "expired_body": "Il link potrebbe essere scaduto o già stato utilizzato. Possiamo inviartene uno nuovo — arriverà nella tua casella di posta in pochi secondi.", - "resend_to": "Invia un nuovo link a {{email}}", - "generic_unavailable": "Questo link di accesso non è più valido. Potrebbe essere già stato utilizzato o essere scaduto. Richiedi un nuovo link dalla pagina di accesso.", - "service_unavailable": "L'accesso tramite magic link non è abilitato su questo server.", - "internal_error": "Si è verificato un errore durante l'accesso. Riprova.", - "resend_failure": "Si è verificato un errore durante l'invio del link. Riprova.", - "cross_browser_title": "Continuare l'accesso su questo dispositivo?", - "cross_browser_body": "Hai aperto questo link di accesso in un browser o dispositivo diverso da quello in cui l'hai richiesto.", - "cross_browser_warning": "Se hai richiesto questo link, puoi continuare in sicurezza. In caso contrario, chiudi questa pagina — cliccare su Continua effettuerebbe l'accesso di qualcun altro al tuo account.", - "cross_browser_continue": "Continua e accedi", - "resend_confirmation_title": "Controlla la tua casella di posta", - "resend_confirmation_body": "Se il link di accesso apparteneva a un account attivo, è appena stato inviato un nuovo link. Controlla la tua casella di posta.", - "return_link": "Torna a OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", - "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nAprilo facendo clic sul link sottostante:\n{{link}}\n\nIl link è monouso e scade tra {{ttl_hours}} ore.\nSe non ti aspettavi questo invito, puoi ignorare questo messaggio.\n\n— OxiCloud" - }, - "login": { - "subject": "Accedi a OxiCloud", - "body": "Ciao,\n\nUsa il link sottostante per accedere a OxiCloud. Il link è monouso e scade tra {{ttl_minutes}} minuti. Aprilo sullo stesso dispositivo da cui l'hai richiesto.\n\n{{link}}\n\nSe non hai richiesto questo link di accesso, puoi ignorare questo messaggio — non è necessaria alcuna ulteriore azione.\n\n— OxiCloud" - }, - "kind_file": "file", - "kind_folder": "cartella", - "english_fallback_divider": "--- Versione inglese qui sotto ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} ha condiviso un {{kind}} con te su OxiCloud", - "body": "{{inviter_full}} ha condiviso un {{kind}} con te su OxiCloud.\n\nApri OxiCloud per vedere la tua nuova condivisione:\n{{login_link}}\n\nPotresti avere altre nuove condivisioni da {{inviter}} — accedi per vedere tutti gli elementi condivisi con te.\n\n— OxiCloud\n\nRicevi questo messaggio perché hai un account OxiCloud e la preferenza di notifica delle condivisioni è attiva. Puoi disattivarla dal tuo profilo (Avvisami via email quando qualcuno condivide con me)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Sistema di archiviazione cloud minimalista" - }, - "nav": { - "files": "File", - "shared": "Condivisioni", - "recent": "Recenti", - "favorites": "Preferiti", - "photos": "Foto", - "music": "Musica", - "trash": "Cestino", - "sharedwithme": "Condivisi con me" - }, - "photos": { - "empty_state": "Nessuna foto ancora", - "empty_hint": "Carica immagini o video per vederli qui", - "items_selected": "selezionati", - "view_daily": "Giorno", - "view_monthly": "Mese", - "view_yearly": "Anno" - }, - "music": { - "create_playlist": "Crea Playlist", - "playlists": "Playlist", - "no_playlists": "Nessuna playlist", - "select_playlist": "Seleziona una playlist", - "select_hint": "Scegli una playlist dalla barra laterale o creane una nuova", - "add_tracks": "Aggiungi Tracce", - "no_tracks": "Nessuna traccia in questa playlist", - "unknown_artist": "Artista Sconosciuto", - "unknown_title": "Sconosciuto", - "confirm_delete": "Eliminare questa playlist?", - "playlist_name": "Nome playlist", - "create": "Crea", - "delete": "Elimina", - "share": "Condividi", - "edit": "Modifica", - "play_all": "Riproduci Tutto", - "shuffle": "Casuale", - "repeat": "Ripeti", - "repeat_one": "Ripeti Una", - "queue": "Coda", - "queue_empty": "Coda vuota", - "not_playing": "Non in riproduzione", - "play": "Riproduci", - "pause": "Pausa", - "previous": "Precedente", - "next": "Successivo", - "volume": "Volume", - "mute": "Muto", - "unmute": "Attiva audio", - "title": "Titolo", - "artist": "Artista", - "album": "Album", - "tracks": "tracce", - "add": "Aggiungi", - "added": "Aggiunto!", - "added_to_playlist": "aggiunto alla playlist", - "add_to_playlist": "Aggiungi alla playlist", - "load_error": "Errore nel caricamento delle playlist", - "add_error": "Impossibile aggiungere le tracce", - "no_playlists_yet": "Nessuna playlist ancora. Creane una prima!", - "selected_files": "Selezionati:", - "error": "Errore", - "search_audio": "Cerca file audio…", - "no_audio_files": "Nessun file audio trovato", - "selected": "selezionati", - "loading": "Caricamento…", - "search_error": "Impossibile caricare i file audio", - "adding": "Aggiunta in corso…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Cerca file...", - "new_folder": "Nuova cartella", - "upload": "Carica", - "upload_files": "Carica file", - "upload_folder": "Carica cartella", - "upload.uploading": "Caricamento...", - "upload.complete": "{count} / {total} caricati", - "upload.files": "file", - "rename": "Rinomina", - "move": "Sposta in...", - "move_to": "Sposta in", - "delete": "Elimina", - "download": "Scarica", - "view": "Visualizza", - "cancel": "Annulla", - "confirm": "Conferma", - "share": "Condividi", - "favorite": "Aggiungi ai preferiti", - "unfavorite": "Rimuovi dai preferiti", - "copy": "Copia", - "notify": "Notifica", - "send": "Invia", - "clear_recent": "Cancella recenti", - "logout": "Disconnetti", - "create": "Crea", - "search_btn": "Cerca", - "close": "Chiudi", - "delete_permanently": "Elimina definitivamente", - "empty_trash": "Svuota il cestino", - "open_parent_folder": "Vai alla cartella padre", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Aspetto", - "about": "Informazioni su OxiCloud", - "about_description": "Piattaforma di archiviazione cloud realizzata con Rust & Architettura Pulita. Veloce, sicura e privata.", - "admin_panel": "Pannello di amministrazione", - "profile": "Il mio profilo", - "role_user": "Utente", - "theme": { - "light": "Chiaro", - "dark": "Scuro", - "auto": "Come il sistema" - }, - "manage_groups": "Gestisci gruppi" - }, - "share": { - "dialogTitle": "Link di condivisione", - "linkLabel": "Link di condivisione:", - "copyLink": "Copia", - "permissions": "Permessi:", - "permissionRead": "Lettura", - "permissionWrite": "Scrittura", - "permissionReshare": "Ricondivisione", - "password": "Protezione password:", - "generatePassword": "Genera", - "expiration": "Data di scadenza:", - "update": "Aggiorna condivisione", - "remove": "Rimuovi condivisione", - "notifyTitle": "Invia notifica", - "notifyEmailLabel": "Indirizzo email:", - "notifyMessageLabel": "Messaggio (opzionale):", - "notifySend": "Invia notifica", - "shareWithOthers": "Condividi con altri", - "sharePublicly": "Condividi pubblicamente", - "shareSettings": "Impostazioni di condivisione", - "shareCopied": "Link copiato negli appunti", - "shareCreated": "Link di condivisione creato con successo", - "shareUpdated": "Impostazioni di condivisione aggiornate con successo", - "shareRemoved": "Condivisione rimossa con successo", - "inviteByEmail": "Invita via email — verrà inviato un invito", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link di condivisione", - "share_linkLabel": "Link di condivisione:", - "share_copyLink": "Copia", - "share_permissions": "Permessi:", - "share_permissionRead": "Lettura", - "share_permissionWrite": "Scrittura", - "share_permissionReshare": "Ricondivisione", - "share_password": "Protezione password:", - "share_generatePassword": "Genera", - "share_expiration": "Data di scadenza:", - "share_update": "Aggiorna condivisione", - "share_remove": "Rimuovi condivisione", - "share_notifyTitle": "Invia notifica", - "share_notifyEmailLabel": "Indirizzo email:", - "share_notifyMessageLabel": "Messaggio (opzionale):", - "share_notifySend": "Invia notifica", - "shared": { - "backToFiles": "Torna ai file", - "pageTitle": "Risorse condivise", - "pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", - "filterType": "Tipo:", - "filterAll": "Tutti", - "filterFiles": "File", - "filterFolders": "Cartelle", - "sortBy": "Ordina per:", - "sortByName": "Nome", - "sortByDate": "Data di condivisione", - "sortByExpiration": "Scadenza", - "search": "Cerca", - "colName": "Nome", - "colType": "Tipo", - "colDateShared": "Data di condivisione", - "colExpiration": "Scadenza", - "colPermissions": "Permessi", - "colPassword": "Password", - "colActions": "Azioni", - "emptyStateTitle": "Ancora nessuna risorsa condivisa", - "emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", - "goToFiles": "Vai ai file", - "typeFile": "File", - "typeFolder": "Cartella", - "noExpiration": "Nessuna scadenza", - "hasPassword": "Sì", - "noPassword": "No", - "editShare": "Modifica condivisione", - "notifyShare": "Notifica a qualcuno", - "copyLink": "Copia link", - "removeShare": "Rimuovi condivisione", - "linkCopied": "Link copiato negli appunti!", - "linkCopyFailed": "Impossibile copiare il link", - "itemUpdated": "Impostazioni di condivisione aggiornate con successo", - "itemRemoved": "Condivisione rimossa con successo", - "invalidEmail": "Inserisci un indirizzo email valido", - "notificationSent": "Notifica inviata con successo", - "notificationFailed": "Impossibile inviare la notifica", - "shared_backToFiles": "Torna ai file", - "shared_pageTitle": "Risorse condivise", - "shared_pageDescription": "Gestisci i tuoi file e le tue cartelle condivise", - "shared_filterType": "Tipo:", - "shared_filterAll": "Tutti", - "shared_filterFiles": "File", - "shared_filterFolders": "Cartelle", - "shared_sortBy": "Ordina per:", - "shared_sortByName": "Nome", - "shared_sortByDate": "Data di condivisione", - "shared_sortByExpiration": "Scadenza", - "shared_search": "Cerca", - "shared_colName": "Nome", - "shared_colType": "Tipo", - "shared_colDateShared": "Data di condivisione", - "shared_colExpiration": "Scadenza", - "shared_colPermissions": "Permessi", - "shared_colPassword": "Password", - "shared_colActions": "Azioni", - "shared_emptyStateTitle": "Ancora nessuna risorsa condivisa", - "shared_emptyStateDesc": "Quando condividi file o cartelle, appariranno qui", - "shared_goToFiles": "Vai ai file", - "shared_typeFile": "File", - "shared_typeFolder": "Cartella", - "shared_noExpiration": "Nessuna scadenza", - "shared_hasPassword": "Sì", - "shared_noPassword": "No", - "shared_editShare": "Modifica condivisione", - "shared_notifyShare": "Notifica a qualcuno", - "shared_copyLink": "Copia link", - "shared_removeShare": "Rimuovi condivisione", - "shared_linkCopied": "Link copiato negli appunti!", - "shared_linkCopyFailed": "Impossibile copiare il link", - "shared_itemUpdated": "Impostazioni di condivisione aggiornate con successo", - "shared_itemRemoved": "Condivisione rimossa con successo", - "shared_invalidEmail": "Inserisci un indirizzo email valido", - "shared_notificationSent": "Notifica inviata con successo", - "shared_notificationFailed": "Impossibile inviare la notifica" - }, - "files": { - "name": "Nome", - "type": "Tipo", - "size": "Dimensione", - "modified": "Modificato", - "no_files": "Nessun file in questa cartella", - "empty_hint": "Carica file o crea cartelle per iniziare", - "loading": "Caricamento file…", - "view_grid": "Visualizzazione griglia", - "view_list": "Visualizzazione elenco", - "file_types": { - "document": "Documento", - "image": "Immagine", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Testo", - "folder": "Cartella", - "spreadsheet": "Foglio di calcolo", - "presentation": "Presentazione", - "archive": "Archivio", - "installer": "Programma di installazione", - "code": "Codice" - }, - "owner": "Proprietario" - }, - "dialogs": { - "rename_folder": "Rinomina cartella", - "rename_file": "Rinomina file", - "new_name": "Nuovo nome", - "new_folder_title": "Nuova cartella", - "folder_name": "Nome cartella", - "folder_placeholder": "La mia cartella", - "rename_title": "Rinomina", - "move_file": "Sposta file", - "move_folder": "Sposta cartella", - "select_destination": "Seleziona cartella di destinazione:", - "root": "Root", - "delete_confirmation": "Sei sicuro di voler eliminare", - "and_contents": "e tutto il suo contenuto", - "no_undo": "Questa azione non può essere annullata", - "confirm_title": "Conferma azione", - "confirm_delete": "Sposta nel cestino", - "confirm_delete_file": "Sei sicuro di voler spostare il file \"{{name}}\" nel cestino?", - "confirm_delete_folder": "Sei sicuro di voler spostare la cartella \"{{name}}\" e tutto il suo contenuto nel cestino?", - "confirm_permanent_delete": "Elimina definitivamente", - "confirm_permanent_delete_msg": "Sei sicuro di voler eliminare definitivamente questo elemento? Questa azione non può essere annullata.", - "confirm_empty_trash": "Svuota il cestino", - "confirm_delete_share": "Elimina link di condivisione", - "confirm_delete_share_msg": "Sei sicuro di voler eliminare questo link di condivisione?", - "share_file": "Condividi File", - "share_folder": "Condividi Cartella", - "existing_shares": "Condivisioni Esistenti", - "share_options": "Opzioni di Condivisione", - "password": "Password", - "expiration": "Scadenza", - "permissions": "Permessi", - "generated_link": "Link Generato", - "notify": "Invia Notifica", - "recipient": "Destinatario", - "message": "Messaggio", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Sposta nella cartella home" - }, - "dropzone": { - "drag_files": "Trascina i file qui o clicca per selezionare", - "drop_files": "Rilascia i file per caricarli" - }, - "permissions": { - "read": "Lettura", - "write": "Scrittura", - "reshare": "Ricondividi" - }, - "errors": { - "file_not_found": "File non trovato", - "folder_not_found": "Cartella non trovata", - "delete_error": "Errore durante l'eliminazione", - "upload_error": "Errore durante il caricamento del file", - "rename_error": "Errore durante la rinomina", - "move_error": "Errore durante lo spostamento", - "empty_name": "Il nome non può essere vuoto", - "name_exists": "Un file o una cartella con quel nome esiste già", - "generic_error": "Si è verificato un errore", - "group_name_invalid": "Il nome del gruppo deve rispettare il formato del prefisso email (lettere, cifre, punto, trattino, trattino basso; 1–64 caratteri).", - "group_cycle": "Questo membro creerebbe un riferimento circolare tra gruppi.", - "group_depth_exceeded": "Questa profondità di annidamento supera il massimo consentito (8).", - "group_virtual_immutable": "Il gruppo «Internal» è gestito dal sistema e non può essere modificato.", - "group_not_found": "Gruppo non trovato.", - "group_name_taken": "Un gruppo con questo nome esiste già." - }, - "breadcrumb": { - "home": "Home" - }, - "trash": { - "empty_trash": "Svuota il cestino", - "empty_state": "Il cestino è vuoto", - "original_location": "Posizione originale", - "deleted_date": "Data di eliminazione", - "remaining": "Rimanente", - "actions": "Azioni", - "restore": "Ripristina", - "delete_permanently": "Elimina definitivamente", - "empty_confirm": "Sei sicuro di voler svuotare il cestino? Questa operazione eliminerà definitivamente tutti gli elementi.", - "groupby": { - "remaining_days": "Giorni rimanenti", - "trashed_time": "Data di eliminazione" - } - }, - "daysRemaining": { - "expired": "Scaduto", - "today": "Oggi", - "tomorrow": "Domani", - "inDays": "{{count}} giorni" - }, - "expiryChip": { - "never": "Non scade mai", - "expired": "Scaduto", - "today": "Scade oggi", - "tomorrow": "Scade domani", - "inDays": "Scade tra {{count}} giorni", - "onDate": "Scade il {{date}}" - }, - "auth": { - "login_title": "Accedi", - "username": "Nome utente", - "username_placeholder": "Inserisci il tuo nome utente", - "login_identifier": "Nome utente o email", - "login_identifier_placeholder": "Inserisci il tuo nome utente o email", - "password": "Password", - "password_placeholder": "Inserisci la tua password", - "login_button": "Accedi", - "no_account": "Non hai un account?", - "register": "Registrati", - "admin_setup": "È la prima volta?", - "setup": "Configura amministratore", - "register_title": "Crea account", - "email": "Email", - "email_placeholder": "Inserisci la tua email", - "confirm_password": "Conferma password", - "confirm_password_placeholder": "Conferma la tua password", - "register_button": "Crea account", - "have_account": "Hai già un account?", - "login": "Accedi", - "setup_title": "Configurazione iniziale", - "setup_step1": "Amministratore", - "setup_step2": "Sistema", - "setup_step3": "Completa", - "admin_username": "Nome utente amministratore", - "admin_email": "Email amministratore", - "admin_password": "Password amministratore", - "create_admin": "Crea amministratore", - "back_to_login": "Già configurato?", - "admin_success": "Account amministratore creato con successo! Ora puoi accedere.", - "account_success": "Account creato con successo! Ora puoi accedere.", - "passwords_mismatch": "Le password non corrispondono", - "admin_create_error": "Errore durante la creazione dell'account amministratore", - "or": "o", - "sso_login": "Accedi con SSO", - "sso_login_provider": "Accedi con {{provider}}", - "magicLinkHint": "Niente password? Inserisci la tua email e ti invieremo un link di accesso monouso.", - "magicLinkEmailLabel": "Indirizzo email", - "magicLinkEmailPlaceholder": "tu@esempio.com", - "magicLinkSubmit": "Invia link di accesso", - "magicLinkSent": "Se esiste un account per questa email, è stato inviato un link di accesso. Controlla la tua casella di posta.", - "magicLinkUnavailable": "L'accesso tramite email non è disponibile su questo server.", - "magicLinkNetworkError": "Impossibile raggiungere il server: {{message}}", - "magicLinkToggle": "Nessuna password? Ricevi un link via e-mail", - "passwordsMatch": "Le password corrispondono", - "capsLock": "Bloc Maiusc attivo" - }, - "storage": { - "title": "Archiviazione", - "calculating": "Calcolo in corso...", - "used": "{{percentage}}% utilizzato ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Questo tipo di file non può essere visualizzato in anteprima.", - "download_file": "Scarica file", - "zoom_in": "Ingrandisci", - "zoom_out": "Riduci", - "zoom_reset": "Reimposta zoom" - }, - "language_selector": { - "title": "Benvenuto!", - "subtitle": "Seleziona la tua lingua per continuare", - "continue": "Continua", - "languages": { - "en": "Inglese", - "es": "Spagnolo", - "zh": "Cinese", - "fa": "Persiano", - "fr": "Francese", - "de": "Tedesco", - "pt": "Portoghese", - "it": "Italiano", - "ar": "العربية", - "hi": "हिन्दी", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Ancora nessun preferito", - "empty_hint": "Aggiungi file o cartelle ai preferiti per inserirli qui", - "add": "Aggiungi ai preferiti", - "remove": "Rimuovi dai preferiti", - "added_title": "Aggiunto ai preferiti", - "added_msg": "aggiunto ai preferiti", - "removed_title": "Rimosso dai preferiti", - "removed_msg": "rimosso dai preferiti" - }, - "recent": { - "title": "Recenti", - "clear": "Cancella recenti", - "accessed": "Accesso", - "empty_state": "Nessun file recente", - "empty_hint": "I file che apri appariranno qui", - "loadMore": "Carica altri" - }, - "notifications": { - "file_renamed": "File rinominato", - "file_renamed_to": "File rinominato in \"{{name}}\"", - "folder_renamed": "Cartella rinominata", - "folder_renamed_to": "Cartella rinominata in \"{{name}}\"", - "file_uploaded": "File caricato", - "file_deleted": "File spostato nel cestino", - "folder_deleted": "Cartella spostata nel cestino", - "item_deleted_permanently": "Elemento eliminato definitivamente", - "trash_emptied": "Cestino svuotato con successo", - "empty": "No notifications", - "title": "Notifications", - "link_created": "Link creato", - "share_success": "Link di condivisione creato con successo", - "upload_files_section_title": "Caricamento non disponibile qui", - "upload_files_section_body": "Vai alla sezione File per caricare i file" - }, - "batch": { - "one_selected": "1 elemento selezionato", - "n_selected": "{{count}} elementi selezionati", - "confirm_delete": "Sei sicuro di voler spostare {{count}} elementi nel cestino?", - "move_title": "Sposta {{count}} elemento/i", - "add_favorites": "Aggiungi ai preferiti", - "move_copy": "Sposta o copia" - }, - "admin": { - "page_title": "Pannello di Amministrazione", - "back_to_app": "Torna a OxiCloud", - "loading": "Caricamento…", - "access_denied": "Accesso Negato", - "access_denied_desc": "Privilegi di amministratore necessari.", - "sign_in": "Accedi", - "tab_dashboard": "Dashboard", - "tab_users": "Utenti", - "tab_oidc": "SSO / OIDC", - "total_users": "Utenti Totali", - "active_users": "Utenti Attivi", - "admins": "Amministratori", - "version": "Versione", - "storage_overview": "Panoramica Archiviazione", - "used": "Usato", - "total_quota": "Quota Totale", - "usage_pct": "Utilizzo %", - "users_over_80": "Utenti >80% quota", - "users_over_quota": "Utenti oltre la quota", - "system": "Sistema", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quote", - "enabled": "Abilitato", - "disabled": "Disabilitato", - "active": "Attivo", - "off": "Spento", - "allow_registration": "Consenti registrazione pubblica", - "registration_warning": "La registrazione pubblica è disabilitata. Solo gli admin possono creare utenti.", - "user_management": "Gestione Utenti", - "create_user": "Crea Utente", - "col_user": "Utente", - "col_role": "Ruolo", - "col_auth": "Auth", - "col_status": "Stato", - "col_storage": "Archiviazione", - "col_last_login": "Ultimo Accesso", - "col_actions": "Azioni", - "loading_users": "Caricamento utenti…", - "failed_load_users": "Impossibile caricare", - "no_users_found": "Nessun utente trovato", - "showing_users": "Mostrando {{from}}-{{to}} di {{total}}", - "prev": "Precedente", - "next": "Successivo", - "inactive": "Inattivo", - "you_badge": "(tu)", - "local": "Locale", - "never": "Mai", - "just_now": "Proprio adesso", - "minutes_ago": "{{n}}min fa", - "hours_ago": "{{n}}h fa", - "days_ago": "{{n}}g fa", - "edit_quota_title": "Modifica quota", - "reset_password_title": "Reimposta password", - "toggle_role_title": "Cambia ruolo", - "deactivate_title": "Disattiva", - "activate_title": "Attiva", - "delete_title": "Elimina", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "Abilita autenticazione SSO", - "provider_name": "Nome Provider", - "issuer_url": "URL Emittente", - "issuer_url_hint": "URL dell'emittente OpenID Connect", - "auto_discover": "Auto-scoperta", - "discovering": "Scoperta…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Lascia vuoto per mantenere il valore", - "secret_configured": "Un client secret è già configurato", - "callback_url": "URL di Callback", - "callback_url_hint": "(registra nel tuo IdP)", - "advanced_settings": "Impostazioni Avanzate", - "scopes": "Scopes", - "auto_provision": "Provisioning automatico degli utenti", - "admin_groups": "Gruppi Admin", - "admin_groups_hint": "Nomi di gruppi OIDC separati da virgola", - "disable_password": "Disabilita accesso con password (solo OIDC)", - "password_warning": "Questo impedirà TUTTI gli accessi tramite password!", - "test_btn": "Test", - "save_btn": "Salva", - "saving": "Salvataggio…", - "settings_saved": "Impostazioni salvate — OIDC ora è {{status}}", - "quota_modal_title": "Aggiorna Quota", - "quota_user_label": "Utente:", - "new_quota": "Nuova Quota", - "quota_unlimited_hint": "0 per illimitato", - "cancel": "Annulla", - "create_user_title": "Crea Nuovo Utente", - "username_label": "Nome utente", - "username_placeholder": "mariorossi", - "username_hint": "3–32 caratteri", - "password_label": "Password", - "password_placeholder": "Min 8 caratteri", - "email_label": "Email", - "email_optional": "(facoltativo)", - "email_placeholder": "utente@esempio.com (auto-generata se vuoto)", - "role_label": "Ruolo", - "role_user": "Utente", - "role_admin": "Admin", - "quota_label": "Quota", - "creating": "Creazione…", - "reset_pw_title": "Reimposta Password", - "new_password_label": "Nuova Password", - "resetting": "Reimpostazione…", - "reset_btn": "Reimposta", - "confirm_role_change": "Cambiare ruolo a {{role}}?", - "confirm_deactivate": "Sei sicuro di voler disattivare questo utente?", - "confirm_activate": "Sei sicuro di voler attivare questo utente?", - "confirm_delete_user": "ELIMINARE l'utente \"{{name}}\"? Azione irreversibile!", - "confirm_action": "Conferma Azione", - "confirm_yes": "Conferma", - "confirm_no": "Annulla", - "error_username_short": "Il nome utente deve avere almeno 3 caratteri", - "error_password_short": "La password deve avere almeno 8 caratteri", - "error_generic": "Fallito", - "error_network": "Errore di rete: {{message}}", - "error_create_user": "Impossibile creare l'utente", - "tab_storage": "Archiviazione", - "storage_title": "Configurazione archiviazione", - "storage_current_backend": "Backend corrente", - "storage_total_blobs": "Blob totali", - "storage_total_size": "Dimensione totale", - "storage_dedup_ratio": "Rapporto deduplicazione", - "storage_backend": "Backend", - "storage_local": "Locale", - "storage_s3": "Compatibile S3", - "storage_provider_preset": "Preset fornitore", - "storage_preset_custom": "Personalizzato", - "storage_endpoint_url": "URL endpoint", - "storage_endpoint_hint": "Lasciare vuoto per AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Regione", - "storage_access_key": "Chiave di accesso", - "storage_secret_key": "Chiave segreta", - "storage_secret_configured": "Chiave configurata", - "storage_key_placeholder": "Inserisci nuova chiave", - "storage_path_style": "Forza stile percorso", - "storage_path_style_hint": "Richiesto per MinIO e alcuni servizi compatibili S3", - "storage_test_connection": "Testa connessione", - "storage_test_success": "Connessione riuscita", - "storage_test_failure": "Connessione fallita", - "storage_save": "Salva configurazione", - "storage_saved": "Configurazione salvata", - "storage_migration": "Migrazione dati", - "storage_migration_coming_soon": "Strumenti di migrazione in arrivo", - "migration_status_label": "Stato migrazione", - "migration_start": "Avvia migrazione", - "migration_pause": "Pausa", - "migration_resume": "Riprendi", - "migration_verify": "Verifica", - "migration_complete": "Completa", - "migration_started": "Migrazione avviata", - "migration_paused_msg": "Migrazione in pausa", - "migration_resumed_msg": "Migrazione ripresa", - "migration_completed_msg": "Migrazione completata con successo", - "migration_verifying": "Verifica in corso...", - "migration_verify_passed": "Verifica superata", - "migration_verify_failed": "Verifica fallita", - "migration_failed_blobs": "Blob falliti", - "testing": "Test in corso...", - "smtp_disabled": "Disabilitato (host non impostato)", - "smtp_enabled": "Abilitato", - "smtp_enabled_label": "Stato", - "smtp_intro": "SMTP è configurato esclusivamente tramite variabili d'ambiente (OXICLOUD_SMTP_*). I valori sottostanti sono letti dal server in esecuzione — per modificarli, modifica l'ambiente e riavvia OxiCloud.", - "smtp_not_configured": "SMTP non è configurato su questo server.", - "smtp_send_failed": "Invio non riuscito.", - "smtp_send_test": "Invia email di prova", - "smtp_sending": "Invio in corso…", - "smtp_sent": "Email di prova inviata.", - "smtp_server_code": "Risposta del server", - "smtp_test_intro": "Invia un messaggio diagnostico predefinito al destinatario indicato sotto e riporta la risposta del server SMTP, così puoi correlarla con i log del tuo relay.", - "smtp_test_missing_to": "Inserisci un indirizzo destinatario.", - "smtp_test_title": "Invia un'email di prova", - "smtp_test_to": "Indirizzo destinatario", - "smtp_title": "Email in uscita (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profilo", - "back_to_app": "Torna a OxiCloud", - "loading": "Caricamento…", - "not_authenticated": "Non Autenticato", - "not_authenticated_desc": "Accedi per visualizzare il tuo profilo.", - "sign_in": "Accedi", - "role_admin": "Amministratore", - "role_user": "Utente", - "account_details": "Dettagli Account", - "username": "Nome utente", - "email": "Email", - "role": "Ruolo", - "last_login": "Ultimo accesso", - "storage": "Archiviazione", - "used": "Usato", - "quota": "Quota", - "usage": "Utilizzo", - "unlimited": "Illimitato", - "app_passwords": "Password Applicazione", - "app_pw_desc": "Genera password per client WebDAV, CalDAV e CardDAV. Ogni password viene mostrata una sola volta.", - "app_pw_label_placeholder": "Etichetta (es. Thunderbird, macOS)", - "generate": "Genera", - "generating": "Generazione…", - "new_password_for": "Nuova password per", - "copy_warning": "Copia questa password ora. Non potrai rivederla.", - "copy_to_clipboard": "Copia negli appunti", - "col_label": "Etichetta", - "col_created": "Creato", - "col_last_used": "Ultimo utilizzo", - "col_status": "Stato", - "active": "Attiva", - "revoked": "Revocata", - "revoke_title": "Revoca", - "no_app_passwords": "Nessuna password applicazione ancora.", - "client_sessions": "Sessioni client", - "client_sessions_desc": "Generate automaticamente quando connetti un client compatibile Nextcloud.", - "col_client": "Client", - "never": "Mai", - "just_now": "Proprio adesso", - "minutes_ago": "{{n}} min fa", - "hours_ago": "{{n}}h fa", - "days_ago": "{{n}} giorni fa", - "edit_profile": "Modifica profilo", - "edit_oidc_managed": "Per modificare le tue informazioni (nome, cognome, foto profilo, …), aggiornale presso il tuo identity provider. Le modifiche compariranno al prossimo accesso.", - "username_claim_hint": "Da 2 a 64 caratteri, lettere / cifre / punto / trattino / sottolineatura. Una volta scelto, il nome utente non può essere modificato (i client DAV/NextCloud dipendono da esso).", - "username_already_claimed": "Nome utente impostato e non modificabile (i client DAV/NextCloud dipendono da esso).", - "given_name": "Nome", - "family_name": "Cognome", - "notify_on_share": "Avvisami via email quando qualcuno condivide con me", - "notify_on_share_hint": "Se deselezionato, le condivisioni continueranno ad apparire nel tuo account — semplicemente non riceverai un'email a riguardo.", - "save_profile": "Salva modifiche", - "profile_saved": "Profilo aggiornato", - "profile_no_changes": "Nessuna modifica da salvare.", - "profile_save_failed": "Salvataggio non riuscito", - "username_taken_error": "Questo nome utente è già in uso.", - "username_immutable_error": "Il tuo nome utente è già impostato e non può essere cambiato qui. Contatta un amministratore se desideri rinominarlo.", - "change_password": "Cambia Password", - "current_password": "Password Attuale", - "new_password": "Nuova Password", - "min_8_chars": "Almeno 8 caratteri", - "confirm_password": "Conferma Nuova Password", - "update_password": "Aggiorna Password", - "updating": "Aggiornamento…", - "password_updated": "Password aggiornata con successo", - "passwords_no_match": "Le password non corrispondono", - "password_too_short": "La password deve avere almeno 8 caratteri", - "password_change_failed": "Impossibile cambiare la password", - "error_network": "Errore di rete: {{message}}", - "error_label_required": "Inserisci un'etichetta", - "error_create_pw": "Impossibile creare la password", - "confirm_revoke": "Revocare la password \"{{label}}\"? I client che la usano smetteranno di funzionare.", - "error_revoke": "Revoca fallita", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Caricamento in corso...", - "files": "file", - "complete": "{{count}} / {{total}} caricati" - }, - "storage_quota_exceeded": "Quota di archiviazione superata", - "sharedwithme": { - "pageTitle": "Condiviso con me", - "pageDescription": "File e cartelle che altri utenti hanno condiviso con te", - "emptyStateTitle": "Niente è ancora condiviso con te", - "emptyStateDesc": "Gli elementi condivisi con te da altri utenti appariranno qui", - "loadMore": "Carica altri", - "sharedBy": "Condiviso da", - "colName": "Nome", - "colType": "Tipo", - "colSharedBy": "Condiviso da", - "colDate": "Data condivisione", - "colPermissions": "Permessi" - }, - "groupby": { - "none": "Nessuno", - "title": "Raggruppa per", - "owner": "Proprietario", - "shareDate": "Data condivisione", - "type": "Tipo", - "type.folders": "Cartelle", - "accessedAt": "Data di accesso", - "modifiedAt": "Data di modifica", - "createdAt": "Data di creazione", - "size": "Dimensione", - "favoriteDate": "Data preferito", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nuovo" - }, - "dateBucket": { - "today": "Oggi", - "last7days": "Ultimi 7 giorni", - "last30days": "Ultimi 30 giorni" - }, - "groups": { - "title": "Gestisci gruppi", - "create_button": "Crea gruppo", - "create_dialog_title": "Nuovo gruppo", - "edit_dialog_title": "Rinomina gruppo", - "name_label": "Nome", - "name_placeholder": "ingegneria", - "description_label": "Descrizione (opzionale)", - "members_section": "Membri", - "add_member_placeholder": "Aggiungi un utente o un gruppo…", - "no_members": "Nessun membro al momento.", - "remove_member": "Rimuovi", - "delete_group": "Elimina gruppo", - "delete_confirm": "Eliminare il gruppo \"{name}\"? Le autorizzazioni che fanno riferimento a questo gruppo saranno revocate.", - "empty_state": "Nessun gruppo al momento.", - "load_more": "Carica altro", - "back_to_list": "Indietro", - "loading": "Caricamento…", - "virtual_badge": "Sistema", - "member_count_zero": "Nessun membro", - "member_count_one": "1 membro", - "member_count_other": "{count} membri", - "delete_confirm_label": "Digita il nome del gruppo per confermare:", - "delete_confirm_mismatch": "Digita esattamente il nome del gruppo per confermare.", - "virtual_internal_name": "Interno", - "members_loading": "Caricamento membri…", - "members_empty": "Nessun membro", - "virtual_internal_explanation": "Ogni utente interno su questo server" - }, - "myshares": { - "copyLink": "Copia link", - "deleteLink": "Elimina link", - "notifyByEmail": "Notifica via email", - "notifyFailed": "Impossibile inviare la notifica.", - "notifyGroupMembers": "Notifica i membri del gruppo", - "notifyRateLimited": "Troppe notifiche per questo destinatario — riprova più tardi.", - "removeAccess": "Rimuovi accesso", - "resendInvitation": "Reinvia email di invito" - }, - "sort": { - "asc": "crescente", - "desc": "decrescente" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/ja.json b/static/locales/ja.json deleted file mode 100644 index 66edf237..00000000 --- a/static/locales/ja.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "このサインインリンクは無効になりました", - "expired_body": "リンクは期限切れか、すでに使用された可能性があります。新しいリンクをお送りできます — 数秒以内にメールが届きます。", - "resend_to": "{{email}} に新しいリンクを送信", - "generic_unavailable": "このサインインリンクは無効になりました。すでに使用されたか、期限が切れた可能性があります。ログインページから新しいリンクをリクエストしてください。", - "service_unavailable": "マジックリンクサインインは、このサーバーで有効になっていません。", - "internal_error": "サインイン中にエラーが発生しました。もう一度お試しください。", - "resend_failure": "リンクの送信中にエラーが発生しました。もう一度お試しください。", - "cross_browser_title": "このデバイスでサインインを続けますか?", - "cross_browser_body": "このサインインリンクを、リクエストしたものとは別のブラウザーまたはデバイスで開きました。", - "cross_browser_warning": "このリンクをあなたがリクエストしたのであれば、続行しても安全です。そうでない場合は、このページを閉じてください — 「続行」をクリックすると、他の人があなたのアカウントにサインインしてしまいます。", - "cross_browser_continue": "続行してサインイン", - "resend_confirmation_title": "受信トレイをご確認ください", - "resend_confirmation_body": "サインインリンクがアクティブなアカウントのものであれば、新しいリンクが今送信されました。受信トレイをご確認ください。", - "return_link": "OxiCloud に戻る" - }, - "email": { - "invitation": { - "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", - "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n以下のリンクをクリックして開いてください:\n{{link}}\n\nリンクは一度のみ有効で、{{ttl_hours}} 時間で期限切れになります。\nこの招待に心当たりがない場合は、このメッセージを無視していただいて結構です。\n\n— OxiCloud" - }, - "login": { - "subject": "OxiCloud にサインイン", - "body": "こんにちは、\n\n以下のリンクから OxiCloud にサインインしてください。リンクは一度のみ有効で、{{ttl_minutes}} 分で期限切れになります。リクエストしたものと同じデバイスで開いてください。\n\n{{link}}\n\nこのサインインリンクをリクエストしていない場合は、このメッセージを無視していただいて結構です — それ以上の操作は必要ありません。\n\n— OxiCloud" - }, - "kind_file": "ファイル", - "kind_folder": "フォルダー", - "english_fallback_divider": "--- 以下は英語版 ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} さんが OxiCloud で {{kind}} をあなたと共有しました", - "body": "{{inviter_full}} さんが OxiCloud で {{kind}} をあなたと共有しました。\n\n新しい共有を確認するには OxiCloud を開いてください:\n{{login_link}}\n\n{{inviter}} さんから他にも新しい共有があるかもしれません — サインインしてあなたと共有されたすべての項目を確認してください。\n\n— OxiCloud\n\nOxiCloud のアカウントをお持ちで、共有通知の設定が有効になっているため、このメッセージが届いています。プロフィールでオフにできます(誰かが共有したときにメールで通知する)。" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "ミニマリストクラウドストレージシステム" - }, - "nav": { - "files": "ファイル", - "shared": "共有", - "recent": "最近", - "favorites": "お気に入り", - "photos": "写真", - "music": "音楽", - "trash": "ゴミ箱", - "sharedwithme": "自分と共有" - }, - "photos": { - "empty_state": "写真はまだありません", - "empty_hint": "画像や動画をアップロードするとここに表示されます", - "items_selected": "件選択中", - "view_daily": "日", - "view_monthly": "月", - "view_yearly": "年" - }, - "music": { - "create_playlist": "プレイリストを作成", - "playlists": "プレイリスト", - "no_playlists": "プレイリストがありません", - "select_playlist": "プレイリストを選択", - "select_hint": "サイドバーからプレイリストを選択するか、新しいものを作成してください", - "add_tracks": "トラックを追加", - "no_tracks": "このプレイリストにトラックがありません", - "unknown_artist": "不明なアーティスト", - "unknown_title": "不明", - "confirm_delete": "このプレイリストを削除しますか?", - "playlist_name": "プレイリスト名", - "create": "作成", - "delete": "削除", - "share": "共有", - "edit": "編集", - "play_all": "すべて再生", - "shuffle": "シャッフル", - "repeat": "リピート", - "repeat_one": "1曲リピート", - "queue": "キュー", - "queue_empty": "キューが空です", - "not_playing": "再生していません", - "play": "再生", - "pause": "一時停止", - "previous": "前へ", - "next": "次へ", - "volume": "音量", - "mute": "ミュート", - "unmute": "ミュート解除", - "title": "タイトル", - "artist": "アーティスト", - "album": "アルバム", - "tracks": "曲", - "add": "追加", - "added": "追加しました!", - "added_to_playlist": "プレイリストに追加しました", - "add_to_playlist": "プレイリストに追加", - "load_error": "プレイリストの読み込みエラー", - "add_error": "曲を追加できませんでした", - "no_playlists_yet": "プレイリストがありません。最初に作成してください!", - "selected_files": "選択中:", - "error": "エラー", - "search_audio": "オーディオファイルを検索…", - "no_audio_files": "オーディオファイルが見つかりません", - "selected": "件選択中", - "loading": "読み込み中…", - "search_error": "オーディオファイルを読み込めませんでした", - "adding": "追加中…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "ファイルを検索...", - "new_folder": "新しいフォルダ", - "upload": "アップロード", - "upload_files": "ファイルをアップロード", - "upload_folder": "フォルダをアップロード", - "upload.uploading": "アップロード中...", - "upload.complete": "{count} / {total} アップロード完了", - "upload.files": "ファイル", - "rename": "名前を変更", - "move": "移動先...", - "move_to": "移動先", - "delete": "削除", - "download": "ダウンロード", - "view": "表示", - "cancel": "キャンセル", - "confirm": "確認", - "share": "共有", - "favorite": "お気に入りに追加", - "unfavorite": "お気に入りから削除", - "copy": "コピー", - "notify": "通知", - "send": "送信", - "clear_recent": "最近をクリア", - "logout": "ログアウト", - "create": "作成", - "search_btn": "検索", - "close": "閉じる", - "delete_permanently": "完全に削除", - "empty_trash": "ゴミ箱を空にする", - "open_parent_folder": "親フォルダへ移動", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "外観", - "about": "OxiCloudについて", - "about_description": "RustとClean Architectureで構築されたクラウドストレージプラットフォーム。高速・安全・プライベート。", - "admin_panel": "管理パネル", - "profile": "マイプロフィール", - "role_user": "ユーザー", - "theme": { - "light": "ライト", - "dark": "ダーク", - "auto": "システムに合わせる" - }, - "manage_groups": "グループを管理" - }, - "share": { - "dialogTitle": "共有リンク", - "linkLabel": "共有リンク:", - "copyLink": "コピー", - "permissions": "権限:", - "permissionRead": "読み取り", - "permissionWrite": "書き込み", - "permissionReshare": "再共有", - "password": "パスワード保護:", - "generatePassword": "生成", - "expiration": "有効期限:", - "update": "共有を更新", - "remove": "共有を削除", - "notifyTitle": "通知を送信", - "notifyEmailLabel": "メールアドレス:", - "notifyMessageLabel": "メッセージ(任意):", - "notifySend": "通知を送信", - "shareWithOthers": "他のユーザーと共有", - "sharePublicly": "公開共有", - "shareSettings": "共有設定", - "shareCopied": "リンクがクリップボードにコピーされました", - "shareCreated": "共有リンクが正常に作成されました", - "shareUpdated": "共有設定が正常に更新されました", - "shareRemoved": "共有が正常に削除されました", - "inviteByEmail": "メールで招待 — 招待を送信します", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "共有リンク", - "share_linkLabel": "共有リンク:", - "share_copyLink": "コピー", - "share_permissions": "権限:", - "share_permissionRead": "読み取り", - "share_permissionWrite": "書き込み", - "share_permissionReshare": "再共有", - "share_password": "パスワード保護:", - "share_generatePassword": "生成", - "share_expiration": "有効期限:", - "share_update": "共有を更新", - "share_remove": "共有を削除", - "share_notifyTitle": "通知を送信", - "share_notifyEmailLabel": "メールアドレス:", - "share_notifyMessageLabel": "メッセージ(任意):", - "share_notifySend": "通知を送信", - "shared": { - "backToFiles": "ファイルに戻る", - "pageTitle": "共有リソース", - "pageDescription": "共有ファイルとフォルダの管理", - "filterType": "種類:", - "filterAll": "すべて", - "filterFiles": "ファイル", - "filterFolders": "フォルダ", - "sortBy": "並び替え:", - "sortByName": "名前", - "sortByDate": "共有日", - "sortByExpiration": "有効期限", - "search": "検索", - "colName": "名前", - "colType": "種類", - "colDateShared": "共有日", - "colExpiration": "有効期限", - "colPermissions": "権限", - "colPassword": "パスワード", - "colActions": "操作", - "emptyStateTitle": "共有リソースはまだありません", - "emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", - "goToFiles": "ファイルへ移動", - "typeFile": "ファイル", - "typeFolder": "フォルダ", - "noExpiration": "期限なし", - "hasPassword": "あり", - "noPassword": "なし", - "editShare": "共有を編集", - "notifyShare": "通知する", - "copyLink": "リンクをコピー", - "removeShare": "共有を削除", - "linkCopied": "リンクがクリップボードにコピーされました!", - "linkCopyFailed": "リンクのコピーに失敗しました", - "itemUpdated": "共有設定が正常に更新されました", - "itemRemoved": "共有が正常に削除されました", - "invalidEmail": "有効なメールアドレスを入力してください", - "notificationSent": "通知が正常に送信されました", - "notificationFailed": "通知の送信に失敗しました", - "shared_backToFiles": "ファイルに戻る", - "shared_pageTitle": "共有リソース", - "shared_pageDescription": "共有ファイルとフォルダの管理", - "shared_filterType": "種類:", - "shared_filterAll": "すべて", - "shared_filterFiles": "ファイル", - "shared_filterFolders": "フォルダ", - "shared_sortBy": "並び替え:", - "shared_sortByName": "名前", - "shared_sortByDate": "共有日", - "shared_sortByExpiration": "有効期限", - "shared_search": "検索", - "shared_colName": "名前", - "shared_colType": "種類", - "shared_colDateShared": "共有日", - "shared_colExpiration": "有効期限", - "shared_colPermissions": "権限", - "shared_colPassword": "パスワード", - "shared_colActions": "操作", - "shared_emptyStateTitle": "共有リソースはまだありません", - "shared_emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます", - "shared_goToFiles": "ファイルへ移動", - "shared_typeFile": "ファイル", - "shared_typeFolder": "フォルダ", - "shared_noExpiration": "期限なし", - "shared_hasPassword": "あり", - "shared_noPassword": "なし", - "shared_editShare": "共有を編集", - "shared_notifyShare": "通知する", - "shared_copyLink": "リンクをコピー", - "shared_removeShare": "共有を削除", - "shared_linkCopied": "リンクがクリップボードにコピーされました!", - "shared_linkCopyFailed": "リンクのコピーに失敗しました", - "shared_itemUpdated": "共有設定が正常に更新されました", - "shared_itemRemoved": "共有が正常に削除されました", - "shared_invalidEmail": "有効なメールアドレスを入力してください", - "shared_notificationSent": "通知が正常に送信されました", - "shared_notificationFailed": "通知の送信に失敗しました" - }, - "files": { - "name": "名前", - "type": "種類", - "size": "サイズ", - "modified": "更新日", - "no_files": "このフォルダにファイルはありません", - "empty_hint": "ファイルをアップロードするかフォルダを作成して始めましょう", - "loading": "ファイルを読み込み中…", - "view_grid": "グリッド表示", - "view_list": "リスト表示", - "file_types": { - "document": "ドキュメント", - "image": "画像", - "video": "動画", - "audio": "音声", - "pdf": "PDF", - "text": "テキスト", - "folder": "フォルダ", - "spreadsheet": "スプレッドシート", - "presentation": "プレゼンテーション", - "archive": "アーカイブ", - "installer": "インストーラー", - "code": "コード" - }, - "owner": "オーナー" - }, - "dialogs": { - "rename_folder": "フォルダ名を変更", - "rename_file": "ファイル名を変更", - "new_name": "新しい名前", - "new_folder_title": "新しいフォルダ", - "folder_name": "フォルダ名", - "folder_placeholder": "マイフォルダ", - "rename_title": "名前を変更", - "move_file": "ファイルを移動", - "move_folder": "フォルダを移動", - "select_destination": "移動先フォルダを選択:", - "select_this_folder": "このフォルダを選択", - "go_to_parent": ".. (親フォルダ)", - "no_subfolders": "サブフォルダなし", - "root": "ルート", - "delete_confirmation": "本当に削除しますか", - "and_contents": "およびすべての内容", - "no_undo": "この操作は元に戻せません", - "confirm_title": "操作の確認", - "confirm_delete": "ゴミ箱に移動", - "confirm_delete_file": "ファイル「{{name}}」をゴミ箱に移動しますか?", - "confirm_delete_folder": "フォルダ「{{name}}」とそのすべての内容をゴミ箱に移動しますか?", - "confirm_permanent_delete": "完全に削除", - "confirm_permanent_delete_msg": "このアイテムを完全に削除しますか?この操作は元に戻せません。", - "confirm_empty_trash": "ゴミ箱を空にする", - "confirm_delete_share": "共有リンクを削除", - "confirm_delete_share_msg": "この共有リンクを削除しますか?", - "share_file": "ファイルを共有", - "share_folder": "フォルダを共有", - "existing_shares": "既存の共有", - "share_options": "共有オプション", - "password": "パスワード", - "expiration": "有効期限", - "permissions": "権限", - "generated_link": "生成されたリンク", - "notify": "通知を送信", - "recipient": "宛先", - "message": "メッセージ", - "move_to_home": "ホームフォルダへ移動" - }, - "dropzone": { - "drag_files": "ファイルをここにドラッグするか、クリックして選択", - "drop_files": "ファイルをドロップしてアップロード" - }, - "permissions": { - "read": "読み取り", - "write": "書き込み", - "reshare": "再共有" - }, - "errors": { - "file_not_found": "ファイルが見つかりません", - "folder_not_found": "フォルダが見つかりません", - "delete_error": "削除エラー", - "upload_error": "ファイルのアップロードエラー", - "rename_error": "名前変更エラー", - "move_error": "移動エラー", - "empty_name": "名前を空にすることはできません", - "name_exists": "同じ名前のファイルまたはフォルダが既に存在します", - "generic_error": "エラーが発生しました", - "group_name_invalid": "グループ名はメールプレフィックス形式に一致している必要があります(文字、数字、ドット、ダッシュ、アンダースコア;1~64文字)。", - "group_cycle": "このメンバーはグループ間で循環参照を作成します。", - "group_depth_exceeded": "ネストの深さが許容されている最大値(8)を超えています。", - "group_virtual_immutable": "「Internal」グループはシステム管理であり、変更できません。", - "group_not_found": "グループが見つかりません。", - "group_name_taken": "この名前のグループはすでに存在します。" - }, - "breadcrumb": { - "home": "ホーム" - }, - "trash": { - "empty_trash": "ゴミ箱を空にする", - "empty_state": "ゴミ箱は空です", - "original_location": "元の場所", - "deleted_date": "削除日", - "remaining": "残り", - "actions": "操作", - "restore": "復元", - "delete_permanently": "完全に削除", - "empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。", - "groupby": { - "remaining_days": "残り日数", - "trashed_time": "削除日時" - } - }, - "daysRemaining": { - "expired": "期限切れ", - "today": "今日", - "tomorrow": "明日", - "inDays": "{{count}}日" - }, - "expiryChip": { - "never": "期限なし", - "expired": "期限切れ", - "today": "今日で期限切れ", - "tomorrow": "明日で期限切れ", - "inDays": "{{count}}日後に期限切れ", - "onDate": "{{date}}に期限切れ" - }, - "auth": { - "login_title": "サインイン", - "username": "ユーザー名", - "username_placeholder": "ユーザー名を入力", - "login_identifier": "ユーザー名またはメールアドレス", - "login_identifier_placeholder": "ユーザー名またはメールアドレスを入力", - "password": "パスワード", - "password_placeholder": "パスワードを入力", - "login_button": "サインイン", - "no_account": "アカウントをお持ちでないですか?", - "register": "登録", - "admin_setup": "初回ですか?", - "setup": "管理者をセットアップ", - "register_title": "アカウント作成", - "email": "メール", - "email_placeholder": "メールアドレスを入力", - "confirm_password": "パスワードの確認", - "confirm_password_placeholder": "パスワードを再入力", - "register_button": "アカウント作成", - "have_account": "既にアカウントをお持ちですか?", - "login": "サインイン", - "setup_title": "初期設定", - "setup_step1": "管理者", - "setup_step2": "システム", - "setup_step3": "完了", - "admin_username": "管理者ユーザー名", - "admin_email": "管理者メール", - "admin_password": "管理者パスワード", - "create_admin": "管理者を作成", - "back_to_login": "設定済みですか?", - "admin_success": "管理者アカウントが正常に作成されました!サインインできます。", - "account_success": "アカウントが正常に作成されました!サインインできます。", - "passwords_mismatch": "パスワードが一致しません", - "admin_create_error": "管理者アカウントの作成エラー", - "or": "または", - "sso_login": "SSOでサインイン", - "sso_login_provider": "{{provider}}でサインイン", - "magicLinkHint": "パスワードをお持ちでない方は、メールアドレスを入力するとワンタイムサインインリンクをお送りします。", - "magicLinkEmailLabel": "メールアドレス", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "サインインリンクを送信", - "magicLinkSent": "そのメールアドレスのアカウントが存在する場合、サインインリンクが送信されました。受信トレイをご確認ください。", - "magicLinkUnavailable": "このサーバーではメールでのサインインは利用できません。", - "magicLinkNetworkError": "サーバーに接続できませんでした: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "ストレージ", - "calculating": "計算中...", - "used": "{{percentage}}% 使用中 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "このファイル形式はプレビューできません。", - "download_file": "ファイルをダウンロード", - "zoom_in": "拡大", - "zoom_out": "縮小", - "zoom_reset": "ズームリセット" - }, - "language_selector": { - "title": "ようこそ!", - "subtitle": "続行するには言語を選択してください", - "continue": "続行", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ja": "日本語", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "お気に入りはまだありません", - "empty_hint": "ファイルやフォルダにスターを付けてお気に入りに追加", - "add": "お気に入りに追加", - "remove": "お気に入りから削除", - "added_title": "お気に入りに追加しました", - "added_msg": "お気に入りに追加しました", - "removed_title": "お気に入りから削除しました", - "removed_msg": "お気に入りから削除しました" - }, - "recent": { - "title": "最近", - "clear": "最近をクリア", - "accessed": "アクセス日", - "empty_state": "最近のファイルはありません", - "empty_hint": "開いたファイルがここに表示されます", - "loadMore": "さらに読み込む" - }, - "notifications": { - "file_renamed": "ファイル名を変更しました", - "file_renamed_to": "ファイル名を「{{name}}」に変更しました", - "folder_renamed": "フォルダ名を変更しました", - "folder_renamed_to": "フォルダ名を「{{name}}」に変更しました", - "file_uploaded": "ファイルをアップロードしました", - "file_deleted": "ファイルをゴミ箱に移動しました", - "folder_deleted": "フォルダをゴミ箱に移動しました", - "item_deleted_permanently": "アイテムを完全に削除しました", - "trash_emptied": "ゴミ箱を正常に空にしました", - "title": "通知", - "empty": "通知はありません", - "link_created": "リンクを作成しました", - "share_success": "共有リンクを正常に作成しました", - "upload_files_section_title": "ここではアップロードできません", - "upload_files_section_body": "ファイルをアップロードするには「ファイル」セクションに移動してください" - }, - "batch": { - "one_selected": "1件選択中", - "n_selected": "{{count}}件選択中", - "confirm_delete": "{{count}}件のアイテムをゴミ箱に移動しますか?", - "move_title": "{{count}}件のアイテムを移動", - "add_favorites": "お気に入りに追加", - "move_copy": "移動またはコピー" - }, - "admin": { - "page_title": "管理パネル", - "back_to_app": "OxiCloudに戻る", - "loading": "読み込み中…", - "access_denied": "アクセス拒否", - "access_denied_desc": "管理者権限が必要です。", - "sign_in": "サインイン", - "tab_dashboard": "ダッシュボード", - "tab_users": "ユーザー", - "tab_oidc": "SSO / OIDC", - "total_users": "総ユーザー数", - "active_users": "アクティブ", - "admins": "管理者", - "version": "バージョン", - "storage_overview": "ストレージ概要", - "used": "使用済み", - "total_quota": "合計クォータ", - "usage_pct": "使用率", - "users_over_80": "クォータ80%超", - "users_over_quota": "クォータ超過", - "system": "システム", - "auth_label": "認証", - "oidc_label": "OIDC", - "quotas_label": "クォータ", - "enabled": "有効", - "disabled": "無効", - "active": "アクティブ", - "off": "オフ", - "allow_registration": "公開セルフ登録を許可", - "registration_warning": "公開登録は無効です。管理者のみがユーザーを作成できます。", - "user_management": "ユーザー管理", - "create_user": "ユーザー作成", - "col_user": "ユーザー", - "col_role": "役割", - "col_auth": "認証", - "col_status": "ステータス", - "col_storage": "ストレージ", - "col_last_login": "最終ログイン", - "col_actions": "操作", - "loading_users": "ユーザーを読み込み中…", - "failed_load_users": "読み込み失敗", - "no_users_found": "ユーザーなし", - "showing_users": "{{from}}-{{to}} / {{total}} を表示", - "prev": "前へ", - "next": "次へ", - "inactive": "非アクティブ", - "you_badge": "(あなた)", - "local": "ローカル", - "never": "未ログイン", - "just_now": "たった今", - "minutes_ago": "{{n}}分前", - "hours_ago": "{{n}}時間前", - "days_ago": "{{n}}日前", - "edit_quota_title": "クォータを編集", - "reset_password_title": "パスワードリセット", - "toggle_role_title": "役割を切替", - "deactivate_title": "無効化", - "activate_title": "有効化", - "delete_title": "削除", - "sso_title": "シングルサインオン (OIDC / SSO)", - "enable_sso": "SSO認証を有効化", - "provider_name": "プロバイダー名", - "issuer_url": "発行者URL", - "issuer_url_hint": "OpenID Connect発行者URL", - "auto_discover": "自動検出", - "discovering": "検出中…", - "client_id": "クライアントID", - "client_secret": "クライアントシークレット", - "client_secret_placeholder": "現在の値を維持するには空に", - "secret_configured": "クライアントシークレット設定済み", - "callback_url": "コールバックURL", - "callback_url_hint": "(IdPに登録)", - "advanced_settings": "詳細設定", - "scopes": "スコープ", - "auto_provision": "初回ログイン時に自動プロビジョニング", - "admin_groups": "管理者グループ", - "admin_groups_hint": "カンマ区切りのOIDCグループ名", - "disable_password": "パスワードログイン無効化(OIDCのみ)", - "password_warning": "すべてのパスワードログインが無効に!", - "test_btn": "テスト", - "save_btn": "保存", - "saving": "保存中…", - "settings_saved": "設定が保存されました — OIDC: {{status}}", - "quota_modal_title": "ストレージクォータ更新", - "quota_user_label": "ユーザー:", - "new_quota": "新しいクォータ", - "quota_unlimited_hint": "0で無制限", - "cancel": "キャンセル", - "create_user_title": "新規ユーザー作成", - "username_label": "ユーザー名", - "username_placeholder": "taro", - "username_hint": "3〜32文字", - "password_label": "パスワード", - "password_placeholder": "8文字以上", - "email_label": "メール", - "email_optional": "(任意)", - "email_placeholder": "user@example.com(空なら自動生成)", - "role_label": "役割", - "role_user": "ユーザー", - "role_admin": "管理者", - "quota_label": "クォータ", - "creating": "作成中…", - "reset_pw_title": "パスワードリセット", - "new_password_label": "新しいパスワード", - "resetting": "リセット中…", - "reset_btn": "リセット", - "confirm_role_change": "役割を{{role}}に変更?", - "confirm_deactivate": "このユーザーを無効化しますか?", - "confirm_activate": "このユーザーを有効化しますか?", - "confirm_delete_user": "ユーザー「{{name}}」を削除?取り消せません!", - "confirm_action": "操作の確認", - "confirm_yes": "確認", - "confirm_no": "キャンセル", - "error_username_short": "ユーザー名は3文字以上", - "error_password_short": "パスワードは8文字以上", - "error_generic": "失敗", - "error_network": "ネットワークエラー: {{message}}", - "error_create_user": "ユーザー作成失敗", - "tab_storage": "ストレージ", - "storage_title": "ストレージ設定", - "storage_current_backend": "現在のバックエンド", - "storage_total_blobs": "総ブロブ数", - "storage_total_size": "合計サイズ", - "storage_dedup_ratio": "重複排除率", - "storage_backend": "バックエンド", - "storage_local": "ローカル", - "storage_s3": "S3互換", - "storage_provider_preset": "プロバイダープリセット", - "storage_preset_custom": "カスタム", - "storage_endpoint_url": "エンドポイントURL", - "storage_endpoint_hint": "AWS S3の場合は空欄のまま", - "storage_bucket": "バケット", - "storage_region": "リージョン", - "storage_access_key": "アクセスキー", - "storage_secret_key": "シークレットキー", - "storage_secret_configured": "キーが設定済み", - "storage_key_placeholder": "新しいキーを入力", - "storage_path_style": "パススタイルを強制", - "storage_path_style_hint": "MinIOおよび一部のS3互換サービスに必要", - "storage_test_connection": "接続テスト", - "storage_test_success": "接続成功", - "storage_test_failure": "接続失敗", - "storage_save": "設定を保存", - "storage_saved": "設定を保存しました", - "storage_migration": "データ移行", - "storage_migration_coming_soon": "移行ツールは近日公開予定", - "migration_status_label": "移行状況", - "migration_start": "移行を開始", - "migration_pause": "一時停止", - "migration_resume": "再開", - "migration_verify": "検証", - "migration_complete": "完了", - "migration_started": "移行を開始しました", - "migration_paused_msg": "移行を一時停止しました", - "migration_resumed_msg": "移行を再開しました", - "migration_completed_msg": "移行が正常に完了しました", - "migration_verifying": "検証中...", - "migration_verify_passed": "検証に合格", - "migration_verify_failed": "検証に失敗", - "migration_failed_blobs": "失敗したブロブ", - "testing": "テスト中...", - "smtp_disabled": "無効 (ホスト未設定)", - "smtp_enabled": "有効", - "smtp_enabled_label": "ステータス", - "smtp_intro": "SMTP は環境変数 (OXICLOUD_SMTP_*) でのみ設定します。以下の値は稼働中のサーバーから読み取られます — 変更するには環境を編集して OxiCloud を再起動してください。", - "smtp_not_configured": "このサーバーでは SMTP が設定されていません。", - "smtp_send_failed": "送信に失敗しました。", - "smtp_send_test": "テストメールを送信", - "smtp_sending": "送信中…", - "smtp_sent": "テストメールを送信しました。", - "smtp_server_code": "サーバーの応答", - "smtp_test_intro": "あらかじめ定義された診断メッセージを下記の宛先に送信し、SMTP サーバーの応答を表示します。これを使ってリレーのログと突き合わせて確認できます。", - "smtp_test_missing_to": "宛先アドレスを入力してください。", - "smtp_test_title": "テストメールを送信", - "smtp_test_to": "宛先アドレス", - "smtp_title": "送信メール (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "プロフィール", - "back_to_app": "OxiCloudに戻る", - "loading": "読み込み中…", - "not_authenticated": "未認証", - "not_authenticated_desc": "プロフィールを表示するにはサインインしてください。", - "sign_in": "サインイン", - "role_admin": "管理者", - "role_user": "ユーザー", - "account_details": "アカウント詳細", - "username": "ユーザー名", - "email": "メール", - "role": "役割", - "last_login": "最終ログイン", - "storage": "ストレージ", - "used": "使用済み", - "quota": "クォータ", - "usage": "使用率", - "unlimited": "無制限", - "app_passwords": "アプリパスワード", - "app_pw_desc": "WebDAV、CalDAV、CardDAVクライアント用のパスワードを生成します。各パスワードは一度だけ表示されます。", - "app_pw_label_placeholder": "ラベル(例:Thunderbird、macOS)", - "generate": "生成", - "generating": "生成中…", - "new_password_for": "新しいパスワード:", - "copy_warning": "このパスワードを今コピーしてください。再度表示できません。", - "copy_to_clipboard": "クリップボードにコピー", - "col_label": "ラベル", - "col_created": "作成日", - "col_last_used": "最終使用", - "col_status": "ステータス", - "active": "アクティブ", - "revoked": "失効済み", - "revoke_title": "失効", - "no_app_passwords": "アプリパスワードはまだありません。", - "client_sessions": "クライアントセッション", - "client_sessions_desc": "Nextcloud互換クライアント接続時に自動生成されます。", - "col_client": "クライアント", - "never": "未ログイン", - "just_now": "たった今", - "minutes_ago": "{{n}}分前", - "hours_ago": "{{n}}時間前", - "days_ago": "{{n}}日前", - "edit_profile": "プロフィールを編集", - "edit_oidc_managed": "情報(姓、名、プロフィール写真など)を変更するには、IDプロバイダーで更新してください。次回サインイン時に反映されます。", - "username_claim_hint": "2〜64文字、英数字 / ドット / ハイフン / アンダースコア。一度選択すると、ユーザー名は変更できません(DAV/NextCloudクライアントが依存します)。", - "username_already_claimed": "ユーザー名は設定済みで変更できません(DAV/NextCloudクライアントが依存します)。", - "given_name": "名", - "family_name": "姓", - "notify_on_share": "誰かが共有したときにメールで通知する", - "notify_on_share_hint": "チェックを外しても、共有はアカウントに表示されますが、メールでの通知は届きません。", - "save_profile": "変更を保存", - "profile_saved": "プロフィールを更新しました", - "profile_no_changes": "保存する変更はありません。", - "profile_save_failed": "保存に失敗しました", - "username_taken_error": "このユーザー名はすでに使用されています。", - "username_immutable_error": "ユーザー名はすでに設定されており、ここでは変更できません。名前を変更したい場合は管理者にお問い合わせください。", - "change_password": "パスワード変更", - "current_password": "現在のパスワード", - "new_password": "新しいパスワード", - "min_8_chars": "8文字以上", - "confirm_password": "新しいパスワードの確認", - "update_password": "パスワードを更新", - "updating": "更新中…", - "password_updated": "パスワードが正常に更新されました", - "passwords_no_match": "パスワードが一致しません", - "password_too_short": "パスワードは8文字以上必要です", - "password_change_failed": "パスワードの変更に失敗しました", - "error_network": "ネットワークエラー: {{message}}", - "error_label_required": "ラベルを入力してください", - "error_create_pw": "アプリパスワードの作成に失敗しました", - "confirm_revoke": "アプリパスワード「{{label}}」を失効させますか?使用中のクライアントは動作しなくなります。", - "error_revoke": "失効に失敗しました", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "アップロード中...", - "files": "ファイル", - "complete": "{{count}} / {{total}} アップロード済み" - }, - "storage_quota_exceeded": "ストレージ容量を超過しました", - "sharedwithme": { - "pageTitle": "自分と共有", - "pageDescription": "他のユーザーがあなたと共有したファイルとフォルダー", - "emptyStateTitle": "まだ何も共有されていません", - "emptyStateDesc": "他のユーザーがあなたと共有したアイテムがここに表示されます", - "loadMore": "さらに読み込む", - "sharedBy": "共有者", - "colName": "名前", - "colType": "タイプ", - "colSharedBy": "共有者", - "colDate": "共有日", - "colPermissions": "権限" - }, - "groupby": { - "none": "なし", - "title": "グループ化", - "owner": "オーナー", - "shareDate": "共有日", - "type": "種類", - "type.folders": "フォルダー", - "accessedAt": "アクセス日", - "modifiedAt": "更新日", - "createdAt": "作成日", - "size": "サイズ", - "favoriteDate": "お気に入り登録日", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "新規" - }, - "dateBucket": { - "today": "今日", - "last7days": "過去7日間", - "last30days": "過去30日間" - }, - "groups": { - "title": "グループを管理", - "create_button": "グループを作成", - "create_dialog_title": "新規グループ", - "edit_dialog_title": "グループ名を変更", - "name_label": "名前", - "name_placeholder": "engineering", - "description_label": "説明(任意)", - "members_section": "メンバー", - "add_member_placeholder": "ユーザーまたはグループを追加…", - "no_members": "メンバーはまだいません。", - "remove_member": "削除", - "delete_group": "グループを削除", - "delete_confirm": "グループ「{name}」を削除しますか?このグループを参照しているすべての権限が取り消されます。", - "empty_state": "グループはまだありません。", - "load_more": "もっと読み込む", - "back_to_list": "戻る", - "loading": "読み込み中…", - "virtual_badge": "システム", - "member_count_zero": "メンバーなし", - "member_count_one": "1 メンバー", - "member_count_other": "{count} メンバー", - "delete_confirm_label": "確認のためにグループ名を入力してください:", - "delete_confirm_mismatch": "確認のためにグループ名を正確に入力してください。", - "virtual_internal_name": "内部", - "members_loading": "メンバーを読み込み中…", - "members_empty": "メンバーなし", - "virtual_internal_explanation": "このサーバー上のすべての内部ユーザー" - }, - "myshares": { - "copyLink": "リンクをコピー", - "deleteLink": "リンクを削除", - "notifyByEmail": "メールで通知", - "notifyFailed": "通知を送信できませんでした。", - "notifyGroupMembers": "グループメンバーに通知", - "notifyRateLimited": "この受信者への通知が多すぎます — しばらくしてから再試行してください。", - "removeAccess": "アクセスを削除", - "resendInvitation": "招待メールを再送信" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/ko.json b/static/locales/ko.json deleted file mode 100644 index b8fa12c0..00000000 --- a/static/locales/ko.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "이 로그인 링크는 더 이상 유효하지 않습니다", - "expired_body": "링크가 만료되었거나 이미 사용되었을 수 있습니다. 새 링크를 보내드릴 수 있습니다 — 몇 초 안에 받은편지함에 도착합니다.", - "resend_to": "{{email}}로 새 링크 보내기", - "generic_unavailable": "이 로그인 링크는 더 이상 유효하지 않습니다. 이미 사용되었거나 만료되었을 수 있습니다. 로그인 페이지에서 새 링크를 요청하세요.", - "service_unavailable": "이 서버에서는 매직 링크 로그인이 활성화되어 있지 않습니다.", - "internal_error": "로그인 중 오류가 발생했습니다. 다시 시도해 주세요.", - "resend_failure": "링크 전송 중 오류가 발생했습니다. 다시 시도해 주세요.", - "cross_browser_title": "이 기기에서 로그인을 계속하시겠습니까?", - "cross_browser_body": "요청한 곳과 다른 브라우저나 기기에서 이 로그인 링크를 열었습니다.", - "cross_browser_warning": "이 링크를 본인이 요청했다면 안전하게 계속할 수 있습니다. 그렇지 않다면 이 페이지를 닫으세요 — 계속을 클릭하면 다른 사람이 당신의 계정에 로그인하게 됩니다.", - "cross_browser_continue": "계속하고 로그인", - "resend_confirmation_title": "받은편지함을 확인하세요", - "resend_confirmation_body": "로그인 링크가 활성 계정의 것이었다면 새 링크가 방금 전송되었습니다. 받은편지함을 확인하세요.", - "return_link": "OxiCloud로 돌아가기" - }, - "email": { - "invitation": { - "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", - "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n아래 링크를 클릭하여 여세요:\n{{link}}\n\n링크는 한 번만 사용 가능하며 {{ttl_hours}}시간 후에 만료됩니다.\n이 초대를 예상하지 못했다면 이 메시지를 무시하셔도 됩니다.\n\n— OxiCloud" - }, - "login": { - "subject": "OxiCloud 로그인", - "body": "안녕하세요,\n\n아래 링크를 사용하여 OxiCloud에 로그인하세요. 링크는 한 번만 사용 가능하며 {{ttl_minutes}}분 후에 만료됩니다. 요청한 것과 동일한 기기에서 여세요.\n\n{{link}}\n\n이 로그인 링크를 요청하지 않으셨다면 이 메시지를 무시하셔도 됩니다 — 추가 조치가 필요하지 않습니다.\n\n— OxiCloud" - }, - "kind_file": "파일", - "kind_folder": "폴더", - "english_fallback_divider": "--- 영어 버전은 아래 ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다", - "body": "{{inviter_full}}님이 OxiCloud에서 {{kind}}을(를) 공유했습니다.\n\n새 공유 항목을 확인하려면 OxiCloud를 여세요:\n{{login_link}}\n\n{{inviter}}님이 추가로 공유한 항목이 있을 수 있습니다 — 로그인하여 공유받은 모든 항목을 확인하세요.\n\n— OxiCloud\n\nOxiCloud 계정이 있고 공유 알림 기본 설정이 켜져 있어 이 메시지를 받았습니다. 프로필에서 끌 수 있습니다(다른 사람이 나에게 공유할 때 이메일로 알림 받기)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "미니멀리스트 클라우드 스토리지 시스템" - }, - "nav": { - "files": "파일", - "shared": "공유", - "recent": "최근", - "favorites": "즐겨찾기", - "photos": "사진", - "music": "음악", - "trash": "휴지통", - "sharedwithme": "나와 공유됨" - }, - "photos": { - "empty_state": "아직 사진이 없습니다", - "empty_hint": "이미지나 동영상을 업로드하면 여기에 표시됩니다", - "items_selected": "개 선택됨", - "view_daily": "일", - "view_monthly": "월", - "view_yearly": "년" - }, - "music": { - "create_playlist": "재생목록 만들기", - "playlists": "재생목록", - "no_playlists": "아직 재생목록이 없습니다", - "select_playlist": "재생목록 선택", - "select_hint": "사이드바에서 재생목록을 선택하거나 새로 만드세요", - "add_tracks": "트랙 추가", - "no_tracks": "이 재생목록에 트랙이 없습니다", - "unknown_artist": "알 수 없는 아티스트", - "unknown_title": "알 수 없음", - "confirm_delete": "이 재생목록을 삭제하시겠습니까?", - "playlist_name": "재생목록 이름", - "create": "만들기", - "delete": "삭제", - "share": "공유", - "edit": "편집", - "play_all": "전체 재생", - "shuffle": "셔플", - "repeat": "반복", - "repeat_one": "한 곡 반복", - "queue": "대기열", - "queue_empty": "대기열이 비어 있습니다", - "not_playing": "재생 중이 아닙니다", - "play": "재생", - "pause": "일시정지", - "previous": "이전", - "next": "다음", - "volume": "볼륨", - "mute": "음소거", - "unmute": "음소거 해제", - "title": "제목", - "artist": "아티스트", - "album": "앨범", - "tracks": "개 트랙", - "add": "추가", - "added": "추가됨!", - "added_to_playlist": "플레이리스트에 추가됨", - "add_to_playlist": "플레이리스트에 추가", - "load_error": "플레이리스트 로드 오류", - "add_error": "트랙을 플레이리스트에 추가할 수 없습니다", - "no_playlists_yet": "플레이리스트가 없습니다. 먼저 하나를 만드세요!", - "selected_files": "선택됨:", - "error": "오류", - "search_audio": "오디오 파일 검색…", - "no_audio_files": "오디오 파일을 찾을 수 없습니다", - "selected": "선택됨", - "loading": "로딩 중…", - "search_error": "오디오 파일을 불러올 수 없습니다", - "adding": "추가 중…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "파일 검색...", - "new_folder": "새 폴더", - "upload": "업로드", - "upload_files": "파일 업로드", - "upload_folder": "폴더 업로드", - "upload.uploading": "업로드 중...", - "upload.complete": "{count} / {total} 업로드 완료", - "upload.files": "파일", - "rename": "이름 변경", - "move": "이동...", - "move_to": "이동 대상", - "delete": "삭제", - "download": "다운로드", - "view": "보기", - "cancel": "취소", - "confirm": "확인", - "share": "공유", - "favorite": "즐겨찾기 추가", - "unfavorite": "즐겨찾기 해제", - "copy": "복사", - "notify": "알림", - "send": "보내기", - "clear_recent": "최근 항목 지우기", - "logout": "로그아웃", - "create": "만들기", - "search_btn": "검색", - "close": "닫기", - "delete_permanently": "영구 삭제", - "empty_trash": "휴지통 비우기", - "open_parent_folder": "상위 폴더로 이동", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "외관", - "about": "OxiCloud 정보", - "about_description": "Rust와 Clean Architecture로 구축된 클라우드 스토리지 플랫폼. 빠르고, 안전하고, 프라이빗합니다.", - "admin_panel": "관리자 패널", - "profile": "내 프로필", - "role_user": "사용자", - "theme": { - "light": "라이트", - "dark": "다크", - "auto": "시스템과 동일" - }, - "manage_groups": "그룹 관리" - }, - "share": { - "dialogTitle": "공유 링크", - "linkLabel": "공유 링크:", - "copyLink": "복사", - "permissions": "권한:", - "permissionRead": "읽기", - "permissionWrite": "쓰기", - "permissionReshare": "재공유", - "password": "비밀번호 보호:", - "generatePassword": "생성", - "expiration": "만료일:", - "update": "공유 업데이트", - "remove": "공유 삭제", - "notifyTitle": "알림 보내기", - "notifyEmailLabel": "이메일 주소:", - "notifyMessageLabel": "메시지 (선택사항):", - "notifySend": "알림 보내기", - "shareWithOthers": "다른 사용자와 공유", - "sharePublicly": "공개 공유", - "shareSettings": "공유 설정", - "shareCopied": "링크가 클립보드에 복사되었습니다", - "shareCreated": "공유 링크가 성공적으로 생성되었습니다", - "shareUpdated": "공유 설정이 성공적으로 업데이트되었습니다", - "shareRemoved": "공유가 성공적으로 삭제되었습니다", - "inviteByEmail": "이메일로 초대 — 초대장이 전송됩니다", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "공유 링크", - "share_linkLabel": "공유 링크:", - "share_copyLink": "복사", - "share_permissions": "권한:", - "share_permissionRead": "읽기", - "share_permissionWrite": "쓰기", - "share_permissionReshare": "재공유", - "share_password": "비밀번호 보호:", - "share_generatePassword": "생성", - "share_expiration": "만료일:", - "share_update": "공유 업데이트", - "share_remove": "공유 삭제", - "share_notifyTitle": "알림 보내기", - "share_notifyEmailLabel": "이메일 주소:", - "share_notifyMessageLabel": "메시지 (선택사항):", - "share_notifySend": "알림 보내기", - "shared": { - "backToFiles": "파일로 돌아가기", - "pageTitle": "공유 리소스", - "pageDescription": "공유 파일 및 폴더 관리", - "filterType": "유형:", - "filterAll": "전체", - "filterFiles": "파일", - "filterFolders": "폴더", - "sortBy": "정렬:", - "sortByName": "이름", - "sortByDate": "공유일", - "sortByExpiration": "만료일", - "search": "검색", - "colName": "이름", - "colType": "유형", - "colDateShared": "공유일", - "colExpiration": "만료일", - "colPermissions": "권한", - "colPassword": "비밀번호", - "colActions": "작업", - "emptyStateTitle": "아직 공유된 리소스가 없습니다", - "emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", - "goToFiles": "파일로 이동", - "typeFile": "파일", - "typeFolder": "폴더", - "noExpiration": "만료 없음", - "hasPassword": "있음", - "noPassword": "없음", - "editShare": "공유 편집", - "notifyShare": "알림", - "copyLink": "링크 복사", - "removeShare": "공유 삭제", - "linkCopied": "링크가 클립보드에 복사되었습니다!", - "linkCopyFailed": "링크 복사에 실패했습니다", - "itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", - "itemRemoved": "공유가 성공적으로 삭제되었습니다", - "invalidEmail": "유효한 이메일 주소를 입력하세요", - "notificationSent": "알림이 성공적으로 전송되었습니다", - "notificationFailed": "알림 전송에 실패했습니다", - "shared_backToFiles": "파일로 돌아가기", - "shared_pageTitle": "공유 리소스", - "shared_pageDescription": "공유 파일 및 폴더 관리", - "shared_filterType": "유형:", - "shared_filterAll": "전체", - "shared_filterFiles": "파일", - "shared_filterFolders": "폴더", - "shared_sortBy": "정렬:", - "shared_sortByName": "이름", - "shared_sortByDate": "공유일", - "shared_sortByExpiration": "만료일", - "shared_search": "검색", - "shared_colName": "이름", - "shared_colType": "유형", - "shared_colDateShared": "공유일", - "shared_colExpiration": "만료일", - "shared_colPermissions": "권한", - "shared_colPassword": "비밀번호", - "shared_colActions": "작업", - "shared_emptyStateTitle": "아직 공유된 리소스가 없습니다", - "shared_emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다", - "shared_goToFiles": "파일로 이동", - "shared_typeFile": "파일", - "shared_typeFolder": "폴더", - "shared_noExpiration": "만료 없음", - "shared_hasPassword": "있음", - "shared_noPassword": "없음", - "shared_editShare": "공유 편집", - "shared_notifyShare": "알림", - "shared_copyLink": "링크 복사", - "shared_removeShare": "공유 삭제", - "shared_linkCopied": "링크가 클립보드에 복사되었습니다!", - "shared_linkCopyFailed": "링크 복사에 실패했습니다", - "shared_itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다", - "shared_itemRemoved": "공유가 성공적으로 삭제되었습니다", - "shared_invalidEmail": "유효한 이메일 주소를 입력하세요", - "shared_notificationSent": "알림이 성공적으로 전송되었습니다", - "shared_notificationFailed": "알림 전송에 실패했습니다" - }, - "files": { - "name": "이름", - "type": "유형", - "size": "크기", - "modified": "수정일", - "no_files": "이 폴더에 파일이 없습니다", - "empty_hint": "파일을 업로드하거나 폴더를 만들어 시작하세요", - "loading": "파일 로딩 중…", - "view_grid": "그리드 보기", - "view_list": "목록 보기", - "file_types": { - "document": "문서", - "image": "이미지", - "video": "동영상", - "audio": "오디오", - "pdf": "PDF", - "text": "텍스트", - "folder": "폴더", - "spreadsheet": "스프레드시트", - "presentation": "프레젠테이션", - "archive": "아카이브", - "installer": "설치 프로그램", - "code": "코드" - }, - "owner": "소유자" - }, - "dialogs": { - "rename_folder": "폴더 이름 변경", - "rename_file": "파일 이름 변경", - "new_name": "새 이름", - "new_folder_title": "새 폴더", - "folder_name": "폴더 이름", - "folder_placeholder": "내 폴더", - "rename_title": "이름 변경", - "move_file": "파일 이동", - "move_folder": "폴더 이동", - "select_destination": "대상 폴더를 선택하세요:", - "select_this_folder": "이 폴더 선택", - "go_to_parent": ".. (상위 폴더)", - "no_subfolders": "하위 폴더 없음", - "root": "루트", - "delete_confirmation": "정말 삭제하시겠습니까", - "and_contents": "및 모든 내용", - "no_undo": "이 작업은 되돌릴 수 없습니다", - "confirm_title": "작업 확인", - "confirm_delete": "휴지통으로 이동", - "confirm_delete_file": "파일 «{{name}}»을(를) 휴지통으로 이동하시겠습니까?", - "confirm_delete_folder": "폴더 «{{name}}» 및 모든 내용을 휴지통으로 이동하시겠습니까?", - "confirm_permanent_delete": "영구 삭제", - "confirm_permanent_delete_msg": "이 항목을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", - "confirm_empty_trash": "휴지통 비우기", - "confirm_delete_share": "공유 링크 삭제", - "confirm_delete_share_msg": "이 공유 링크를 삭제하시겠습니까?", - "share_file": "파일 공유", - "share_folder": "폴��� 공유", - "existing_shares": "기존 공유", - "share_options": "공유 옵션", - "password": "비밀번호", - "expiration": "만료일", - "permissions": "권한", - "generated_link": "생성된 링크", - "notify": "알림 보내기", - "recipient": "수신자", - "message": "메시지", - "move_to_home": "홈 폴더로 이동" - }, - "dropzone": { - "drag_files": "여기에 파일을 드래그하거나 클릭하여 선택하세요", - "drop_files": "파일을 놓아 업로드하세요" - }, - "permissions": { - "read": "읽기", - "write": "쓰기", - "reshare": "재공유" - }, - "errors": { - "file_not_found": "파일을 찾을 수 없습니다", - "folder_not_found": "폴더를 찾을 수 없습니다", - "delete_error": "삭제 오류", - "upload_error": "파일 업로드 오류", - "rename_error": "이름 변경 오류", - "move_error": "이동 오류", - "empty_name": "이름은 비워둘 수 없습니다", - "name_exists": "같은 이름의 파일 또는 폴더가 이미 존재합니다", - "generic_error": "오류가 발생했습니다", - "group_name_invalid": "그룹 이름은 이메일 접두사 형식과 일치해야 합니다(문자, 숫자, 점, 대시, 밑줄; 1–64자).", - "group_cycle": "이 구성원은 그룹 간 순환 참조를 만들 것입니다.", - "group_depth_exceeded": "중첩 깊이가 허용 최대값(8)을 초과합니다.", - "group_virtual_immutable": "«Internal» 그룹은 시스템이 관리하며 수정할 수 없습니다.", - "group_not_found": "그룹을 찾을 수 없습니다.", - "group_name_taken": "이 이름의 그룹이 이미 존재합니다." - }, - "breadcrumb": { - "home": "홈" - }, - "trash": { - "empty_trash": "휴지통 비우기", - "empty_state": "휴지통이 비어 있습니다", - "original_location": "원래 위치", - "deleted_date": "삭제일", - "remaining": "남음", - "actions": "작업", - "restore": "복원", - "delete_permanently": "영구 삭제", - "empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다.", - "groupby": { - "remaining_days": "남은 일수", - "trashed_time": "삭제 시간" - } - }, - "daysRemaining": { - "expired": "만료됨", - "today": "오늘", - "tomorrow": "내일", - "inDays": "{{count}}일" - }, - "expiryChip": { - "never": "만료되지 않음", - "expired": "만료됨", - "today": "오늘 만료", - "tomorrow": "내일 만료", - "inDays": "{{count}}일 후 만료", - "onDate": "{{date}}에 만료" - }, - "auth": { - "login_title": "로그인", - "username": "사용자 이름", - "username_placeholder": "사용자 이름을 입력하세요", - "login_identifier": "사용자 이름 또는 이메일", - "login_identifier_placeholder": "사용자 이름 또는 이메일을 입력하세요", - "password": "비밀번호", - "password_placeholder": "비밀번호를 입력하세요", - "login_button": "로그인", - "no_account": "계정이 없으신가요?", - "register": "가입하기", - "admin_setup": "처음이신가요?", - "setup": "관리자 설정", - "register_title": "계정 만들기", - "email": "이메일", - "email_placeholder": "이메일 주소를 입력하세요", - "confirm_password": "비밀번호 확인", - "confirm_password_placeholder": "비밀번호를 다시 입력하세요", - "register_button": "계정 만들기", - "have_account": "이미 계정이 있으신가요?", - "login": "로그인", - "setup_title": "초기 설정", - "setup_step1": "관리자", - "setup_step2": "시스템", - "setup_step3": "완료", - "admin_username": "관리자 사용자 이름", - "admin_email": "관리자 이메일", - "admin_password": "관리자 비밀번호", - "create_admin": "관리자 생성", - "back_to_login": "이미 설정하셨나요?", - "admin_success": "관리자 계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", - "account_success": "계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.", - "passwords_mismatch": "비밀번호가 일치하지 않습니다", - "admin_create_error": "관리자 계정 생성 오류", - "or": "또는", - "sso_login": "SSO로 로그인", - "sso_login_provider": "{{provider}}(으)로 로그인", - "magicLinkHint": "비밀번호가 없으신가요? 이메일을 입력하시면 일회용 로그인 링크를 보내드립니다.", - "magicLinkEmailLabel": "이메일 주소", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "로그인 링크 보내기", - "magicLinkSent": "해당 이메일에 대한 계정이 있는 경우 로그인 링크가 전송되었습니다. 받은편지함을 확인하세요.", - "magicLinkUnavailable": "이 서버에서는 이메일 로그인을 사용할 수 없습니다.", - "magicLinkNetworkError": "서버에 연결할 수 없습니다: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "저장소", - "calculating": "계산 중...", - "used": "{{percentage}}% 사용 중 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "이 파일 형식은 미리보기를 지원하지 않습니다.", - "download_file": "파일 다운로드", - "zoom_in": "확대", - "zoom_out": "축소", - "zoom_reset": "줌 초기화" - }, - "language_selector": { - "title": "환영합니다!", - "subtitle": "계속하려면 언어를 선택하세요", - "continue": "계속", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ko": "한국어", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "아직 즐겨찾기가 없습니다", - "empty_hint": "파일이나 폴더에 별표를 눌러 즐겨찾기에 추가하세요", - "add": "즐겨찾기 추가", - "remove": "즐겨찾기 해제", - "added_title": "즐겨찾기에 추가됨", - "added_msg": "즐겨찾기에 추가되었습니다", - "removed_title": "즐겨찾기에서 삭제됨", - "removed_msg": "즐겨찾기에서 삭제되었습니다" - }, - "recent": { - "title": "최근", - "clear": "최근 항목 지우기", - "accessed": "접근일", - "empty_state": "최근 파일이 없습니다", - "empty_hint": "열어본 파일이 여기에 표시됩니다", - "loadMore": "더 불러오기" - }, - "notifications": { - "file_renamed": "파일 이름이 변경되었습니다", - "file_renamed_to": "파일 이름이 «{{name}}»(으)로 변경되었습니다", - "folder_renamed": "폴더 이름이 변경되었습니다", - "folder_renamed_to": "폴더 이름이 «{{name}}»(으)로 변경되었습니다", - "file_uploaded": "파일이 업로드되었습니다", - "file_deleted": "파일이 휴지통으로 이동되었습니다", - "folder_deleted": "폴더가 휴지통으로 이동되었습니다", - "item_deleted_permanently": "항목이 영구적으로 삭제되었습니다", - "trash_emptied": "휴지통이 성공적으로 비워졌습니다", - "title": "알림", - "empty": "알림이 없습니다", - "link_created": "링크 생성됨", - "share_success": "공유 링크가 성공적으로 생성되었습니다", - "upload_files_section_title": "여기서는 업로드할 수 없습니다", - "upload_files_section_body": "파일을 업로드하려면 파일 섹션으로 이동하세요" - }, - "batch": { - "one_selected": "1개 선택됨", - "n_selected": "{{count}}개 선택됨", - "confirm_delete": "{{count}}개 항목을 휴지통으로 이동하시겠습니까?", - "move_title": "{{count}}개 항목 이동", - "add_favorites": "즐겨찾기에 추가", - "move_copy": "이동 또는 복사" - }, - "admin": { - "page_title": "관리자 패널", - "back_to_app": "OxiCloud로 돌아가기", - "loading": "로딩 중…", - "access_denied": "접근 거부", - "access_denied_desc": "관리자 권한이 필요합니다.", - "sign_in": "로그인", - "tab_dashboard": "대시보드", - "tab_users": "사용자", - "tab_oidc": "SSO / OIDC", - "total_users": "전체 사용자", - "active_users": "활성 사용자", - "admins": "관리자", - "version": "버전", - "storage_overview": "스토리지 개요", - "used": "사용됨", - "total_quota": "총 할당량", - "usage_pct": "사용률", - "users_over_80": "할당량 80% 초과", - "users_over_quota": "할당량 초과", - "system": "시스템", - "auth_label": "인증", - "oidc_label": "OIDC", - "quotas_label": "할당량", - "enabled": "활성화됨", - "disabled": "비활성화됨", - "active": "활성", - "off": "꺼짐", - "allow_registration": "공개 자가 등록 허용", - "registration_warning": "공개 등록이 비활성화되어 있습니다. 관리자만 사용자를 만들 수 있습니다.", - "user_management": "사용자 관리", - "create_user": "사용자 생성", - "col_user": "사용자", - "col_role": "역할", - "col_auth": "인증", - "col_status": "상태", - "col_storage": "스토리지", - "col_last_login": "마지막 로그인", - "col_actions": "작업", - "loading_users": "사용자 로딩 중…", - "failed_load_users": "로드 실패", - "no_users_found": "사용자 없음", - "showing_users": "{{from}}-{{to}} / {{total}} 표시", - "prev": "이전", - "next": "다음", - "inactive": "비활성", - "you_badge": "(나)", - "local": "로컬", - "never": "없음", - "just_now": "방금", - "minutes_ago": "{{n}}분 전", - "hours_ago": "{{n}}시간 전", - "days_ago": "{{n}}일 전", - "edit_quota_title": "할당량 편집", - "reset_password_title": "비밀번호 재설정", - "toggle_role_title": "역할 전환", - "deactivate_title": "비활성화", - "activate_title": "활성화", - "delete_title": "삭제", - "sso_title": "싱글 사인온 (OIDC / SSO)", - "enable_sso": "SSO 인증 활성화", - "provider_name": "제공자 이름", - "issuer_url": "발급자 URL", - "issuer_url_hint": "OpenID Connect 발급자 URL", - "auto_discover": "자동 검색", - "discovering": "검색 중…", - "client_id": "클라이언트 ID", - "client_secret": "클라이언트 시크릿", - "client_secret_placeholder": "현재 값 유지하려면 비워두세요", - "secret_configured": "클라이언트 시크릿 구성됨", - "callback_url": "콜백 URL", - "callback_url_hint": "(IdP에 등록)", - "advanced_settings": "고급 설정", - "scopes": "스코프", - "auto_provision": "첫 로그인 시 자동 프로비저닝", - "admin_groups": "관리자 그룹", - "admin_groups_hint": "쉼표로 구분된 OIDC 그룹 이름", - "disable_password": "비밀번호 로그인 비활성화 (OIDC만)", - "password_warning": "모든 비밀번호 로그인이 차단됩니다!", - "test_btn": "테스트", - "save_btn": "저장", - "saving": "저장 중…", - "settings_saved": "설정 저장됨 — OIDC: {{status}}", - "quota_modal_title": "스토리지 할당량 업데이트", - "quota_user_label": "사용자:", - "new_quota": "새 할당량", - "quota_unlimited_hint": "무제한은 0", - "cancel": "취소", - "create_user_title": "새 사용자 생성", - "username_label": "사용자 이름", - "username_placeholder": "username", - "username_hint": "3–32자", - "password_label": "비밀번호", - "password_placeholder": "최소 8자", - "email_label": "이메일", - "email_optional": "(선택사항)", - "email_placeholder": "user@example.com (비어있으면 자동 생성)", - "role_label": "역할", - "role_user": "사용자", - "role_admin": "관리자", - "quota_label": "할당량", - "creating": "생성 중…", - "reset_pw_title": "비밀번호 재설정", - "new_password_label": "새 비밀번호", - "resetting": "재설정 중…", - "reset_btn": "재설정", - "confirm_role_change": "역할을 {{role}}(으)로 변경?", - "confirm_deactivate": "이 사용자를 비활성화하시겠습니까?", - "confirm_activate": "이 사용자를 활성화하시겠습니까?", - "confirm_delete_user": "사용자 \"{{name}}\" 삭제? 되돌릴 수 없습니다!", - "confirm_action": "작업 확인", - "confirm_yes": "확인", - "confirm_no": "취소", - "error_username_short": "사용자 이름 최소 3자", - "error_password_short": "비밀번호 최소 8자", - "error_generic": "실패", - "error_network": "네트워크 오류: {{message}}", - "error_create_user": "사용자 생성 실패", - "tab_storage": "저장소", - "storage_title": "저장소 구성", - "storage_current_backend": "현재 백엔드", - "storage_total_blobs": "총 블롭 수", - "storage_total_size": "총 크기", - "storage_dedup_ratio": "중복 제거 비율", - "storage_backend": "백엔드", - "storage_local": "로컬", - "storage_s3": "S3 호환", - "storage_provider_preset": "공급자 프리셋", - "storage_preset_custom": "사용자 지정", - "storage_endpoint_url": "엔드포인트 URL", - "storage_endpoint_hint": "AWS S3의 경우 비워두세요", - "storage_bucket": "버킷", - "storage_region": "지역", - "storage_access_key": "액세스 키", - "storage_secret_key": "시크릿 키", - "storage_secret_configured": "키 구성됨", - "storage_key_placeholder": "새 키 입력", - "storage_path_style": "경로 스타일 강제", - "storage_path_style_hint": "MinIO 및 일부 S3 호환 서비스에 필요", - "storage_test_connection": "연결 테스트", - "storage_test_success": "연결 성공", - "storage_test_failure": "연결 실패", - "storage_save": "구성 저장", - "storage_saved": "구성이 저장되었습니다", - "storage_migration": "데이터 마이그레이션", - "storage_migration_coming_soon": "마이그레이션 도구 곧 출시", - "migration_status_label": "마이그레이션 상태", - "migration_start": "마이그레이션 시작", - "migration_pause": "일시 중지", - "migration_resume": "재개", - "migration_verify": "확인", - "migration_complete": "완료", - "migration_started": "마이그레이션 시작됨", - "migration_paused_msg": "마이그레이션 일시 중지됨", - "migration_resumed_msg": "마이그레이션 재개됨", - "migration_completed_msg": "마이그레이션이 성공적으로 완료되었습니다", - "migration_verifying": "확인 중...", - "migration_verify_passed": "확인 통과", - "migration_verify_failed": "확인 실패", - "migration_failed_blobs": "실패한 블롭", - "testing": "테스트 중...", - "smtp_disabled": "비활성화됨 (호스트 미설정)", - "smtp_enabled": "활성화됨", - "smtp_enabled_label": "상태", - "smtp_intro": "SMTP는 환경 변수(OXICLOUD_SMTP_*)로만 구성됩니다. 아래 값들은 실행 중인 서버에서 읽어옵니다 — 변경하려면 환경을 수정하고 OxiCloud를 다시 시작하세요.", - "smtp_not_configured": "이 서버에는 SMTP가 구성되어 있지 않습니다.", - "smtp_send_failed": "전송 실패.", - "smtp_send_test": "테스트 이메일 보내기", - "smtp_sending": "보내는 중…", - "smtp_sent": "테스트 이메일을 보냈습니다.", - "smtp_server_code": "서버 응답", - "smtp_test_intro": "아래 수신자에게 미리 정의된 진단 메시지를 보내고 SMTP 서버의 응답을 표시합니다. 이를 통해 릴레이 로그와 대조하여 확인할 수 있습니다.", - "smtp_test_missing_to": "수신자 주소를 입력하세요.", - "smtp_test_title": "테스트 이메일 보내기", - "smtp_test_to": "수신자 주소", - "smtp_title": "발신 이메일 (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "프로필", - "back_to_app": "OxiCloud로 돌아가기", - "loading": "로딩 중…", - "not_authenticated": "인증되지 않음", - "not_authenticated_desc": "프로필을 보려면 로그인하세요.", - "sign_in": "로그인", - "role_admin": "관리자", - "role_user": "사용자", - "account_details": "계정 정보", - "username": "사용자 이름", - "email": "이메일", - "role": "역할", - "last_login": "마지막 로그인", - "storage": "스토리지", - "used": "사용됨", - "quota": "할당량", - "usage": "사용률", - "unlimited": "무제한", - "app_passwords": "앱 비밀번호", - "app_pw_desc": "WebDAV, CalDAV, CardDAV 클라이언트용 비밀번호를 생성합니다. 각 비밀번호는 한 번만 표시됩니다.", - "app_pw_label_placeholder": "라벨 (예: Thunderbird, macOS)", - "generate": "생성", - "generating": "생성 중…", - "new_password_for": "새 비밀번호:", - "copy_warning": "지금 이 비밀번호를 복사하세요. 다시 볼 수 없습니다.", - "copy_to_clipboard": "클립보드에 복사", - "col_label": "라벨", - "col_created": "생성일", - "col_last_used": "마지막 사용", - "col_status": "상태", - "active": "활성", - "revoked": "취소됨", - "revoke_title": "취소", - "no_app_passwords": "앱 비밀번호가 아직 없습니다.", - "client_sessions": "클라이언트 세션", - "client_sessions_desc": "Nextcloud 호환 클라이언트 연결 시 자동 생성됩니다.", - "col_client": "클라이언트", - "never": "없음", - "just_now": "방금", - "minutes_ago": "{{n}}분 전", - "hours_ago": "{{n}}시간 전", - "days_ago": "{{n}}일 전", - "edit_profile": "프로필 편집", - "edit_oidc_managed": "정보(이름, 성, 프로필 사진 등)를 변경하려면 ID 공급자에서 업데이트하세요. 변경 사항은 다음 로그인 시 반영됩니다.", - "username_claim_hint": "2~64자, 영문자 / 숫자 / 점 / 하이픈 / 밑줄. 선택한 후에는 사용자 이름을 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", - "username_already_claimed": "사용자 이름이 설정되어 있어 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).", - "given_name": "이름", - "family_name": "성", - "notify_on_share": "다른 사람이 나에게 공유할 때 이메일로 알림 받기", - "notify_on_share_hint": "선택을 해제해도 공유 항목은 계정에 계속 표시되지만, 이메일 알림은 받지 않습니다.", - "save_profile": "변경 사항 저장", - "profile_saved": "프로필이 업데이트되었습니다", - "profile_no_changes": "저장할 변경 사항이 없습니다.", - "profile_save_failed": "저장 실패", - "username_taken_error": "이미 사용 중인 사용자 이름입니다.", - "username_immutable_error": "사용자 이름이 이미 설정되어 있어 여기서 변경할 수 없습니다. 이름을 변경하려면 관리자에게 문의하세요.", - "change_password": "비밀번호 변경", - "current_password": "현재 비밀번호", - "new_password": "새 비밀번호", - "min_8_chars": "최소 8자", - "confirm_password": "새 비밀번호 확인", - "update_password": "비밀번호 업데이트", - "updating": "업데이트 중…", - "password_updated": "비밀번호가 성공적으로 업데이트되었습니다", - "passwords_no_match": "비밀번호가 일치하지 않습니다", - "password_too_short": "비밀번호는 최소 8자여야 합니다", - "password_change_failed": "비밀번호 변경 실패", - "error_network": "네트워크 오류: {{message}}", - "error_label_required": "라벨을 입력하세요", - "error_create_pw": "앱 비밀번호 생성 실패", - "confirm_revoke": "앱 비밀번호 \"{{label}}\"을(를) 취소하시겠습니까? 이 비밀번호를 사용하는 클라이언트가 작동하지 않게 됩니다.", - "error_revoke": "취소 실패", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "업로드 중...", - "files": "파일", - "complete": "{{count}} / {{total}} 업로드됨" - }, - "storage_quota_exceeded": "저장 공간 할당량 초과", - "sharedwithme": { - "pageTitle": "나와 공유됨", - "pageDescription": "다른 사용자가 나와 공유한 파일 및 폴더", - "emptyStateTitle": "아직 공유된 항목이 없습니다", - "emptyStateDesc": "다른 사용자가 공유한 항목이 여기에 표시됩니다", - "loadMore": "더 불러오기", - "sharedBy": "공유한 사람", - "colName": "이름", - "colType": "유형", - "colSharedBy": "공유한 사람", - "colDate": "공유 날짜", - "colPermissions": "권한" - }, - "groupby": { - "none": "없음", - "title": "그룹화 기준", - "owner": "소유자", - "shareDate": "공유 날짜", - "type": "유형", - "type.folders": "폴더", - "accessedAt": "접근 날짜", - "modifiedAt": "수정 날짜", - "createdAt": "생성 날짜", - "size": "크기", - "favoriteDate": "즐겨찾기 날짜", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "새 항목" - }, - "dateBucket": { - "today": "오늘", - "last7days": "최근 7일", - "last30days": "최근 30일" - }, - "groups": { - "title": "그룹 관리", - "create_button": "그룹 생성", - "create_dialog_title": "새 그룹", - "edit_dialog_title": "그룹 이름 변경", - "name_label": "이름", - "name_placeholder": "engineering", - "description_label": "설명(선택사항)", - "members_section": "구성원", - "add_member_placeholder": "사용자 또는 그룹 추가…", - "no_members": "아직 구성원이 없습니다.", - "remove_member": "제거", - "delete_group": "그룹 삭제", - "delete_confirm": "\"{name}\" 그룹을 삭제하시겠습니까? 이 그룹을 참조하는 모든 권한이 해제됩니다.", - "empty_state": "아직 그룹이 없습니다.", - "load_more": "더 보기", - "back_to_list": "뒤로", - "loading": "로딩 중…", - "virtual_badge": "시스템", - "member_count_zero": "구성원 없음", - "member_count_one": "구성원 1명", - "member_count_other": "구성원 {count}명", - "delete_confirm_label": "확인을 위해 그룹 이름을 입력하세요:", - "delete_confirm_mismatch": "확인을 위해 그룹 이름을 정확히 입력하세요.", - "virtual_internal_name": "내부", - "members_loading": "구성원 로딩 중…", - "members_empty": "구성원 없음", - "virtual_internal_explanation": "이 서버의 모든 내부 사용자" - }, - "myshares": { - "copyLink": "링크 복사", - "deleteLink": "링크 삭제", - "notifyByEmail": "이메일로 알림", - "notifyFailed": "알림을 보낼 수 없습니다.", - "notifyGroupMembers": "그룹 구성원에게 알림", - "notifyRateLimited": "이 수신자에게 알림이 너무 많습니다 — 나중에 다시 시도하세요.", - "removeAccess": "액세스 제거", - "resendInvitation": "초대 이메일 다시 보내기" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/nl.json b/static/locales/nl.json deleted file mode 100644 index 468c93fd..00000000 --- a/static/locales/nl.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Deze aanmeldlink is niet meer geldig", - "expired_body": "De link is mogelijk verlopen of al gebruikt. We kunnen je een nieuwe sturen — die komt binnen enkele seconden in je inbox.", - "resend_to": "Stuur een nieuwe link naar {{email}}", - "generic_unavailable": "Deze aanmeldlink is niet meer geldig. Hij is mogelijk al gebruikt of verlopen. Vraag een nieuwe link aan via de aanmeldpagina.", - "service_unavailable": "Aanmelden via magic link is niet ingeschakeld op deze server.", - "internal_error": "Er is iets misgegaan bij het aanmelden. Probeer het opnieuw.", - "resend_failure": "Er is iets misgegaan bij het versturen van de link. Probeer het opnieuw.", - "cross_browser_title": "Doorgaan met aanmelden op dit apparaat?", - "cross_browser_body": "Je hebt deze aanmeldlink geopend in een andere browser of op een ander apparaat dan waar je hem hebt aangevraagd.", - "cross_browser_warning": "Als jij deze link hebt aangevraagd, kun je veilig doorgaan. Zo niet, sluit deze pagina — op Doorgaan klikken zou iemand anders bij je account aanmelden.", - "cross_browser_continue": "Doorgaan en aanmelden", - "resend_confirmation_title": "Controleer je inbox", - "resend_confirmation_body": "Als de aanmeldlink bij een actief account hoorde, is er zojuist een nieuwe link verstuurd. Controleer je inbox.", - "return_link": "Terug naar OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", - "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen het door op de onderstaande link te klikken:\n{{link}}\n\nDe link werkt eenmalig en verloopt over {{ttl_hours}} uur.\nAls je deze uitnodiging niet verwacht, kun je dit bericht negeren.\n\n— OxiCloud" - }, - "login": { - "subject": "Aanmelden bij OxiCloud", - "body": "Hallo,\n\nGebruik de onderstaande link om je aan te melden bij OxiCloud. De link werkt eenmalig en verloopt over {{ttl_minutes}} minuten. Open hem op hetzelfde apparaat waarop je hem hebt aangevraagd.\n\n{{link}}\n\nAls je deze aanmeldlink niet hebt aangevraagd, kun je dit bericht negeren — er is geen verdere actie nodig.\n\n— OxiCloud" - }, - "kind_file": "bestand", - "kind_folder": "map", - "english_fallback_divider": "--- Engelse versie hieronder ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} heeft een {{kind}} met je gedeeld op OxiCloud", - "body": "{{inviter_full}} heeft een {{kind}} met je gedeeld op OxiCloud.\n\nOpen OxiCloud om je nieuwe gedeelde item te bekijken:\n{{login_link}}\n\nMisschien heb je nog meer nieuwe gedeelde items van {{inviter}} — meld je aan om al je gedeelde items te zien.\n\n— OxiCloud\n\nJe ontvangt dit bericht omdat je een OxiCloud-account hebt en je voorkeur voor deelmeldingen aanstaat. Je kunt het uitzetten in je profiel (Stuur me een e-mail wanneer iemand iets met mij deelt)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalistisch cloudopslagsysteem" - }, - "nav": { - "files": "Bestanden", - "shared": "Gedeeld", - "recent": "Recente", - "favorites": "Favorieten", - "photos": "Foto's", - "music": "Muziek", - "trash": "Prullenbak", - "sharedwithme": "Gedeeld met mij" - }, - "photos": { - "empty_state": "Nog geen foto's", - "empty_hint": "Upload afbeeldingen of video's om ze hier te zien", - "items_selected": "geselecteerd", - "view_daily": "Dag", - "view_monthly": "Maand", - "view_yearly": "Jaar" - }, - "music": { - "create_playlist": "Afspeellijst Maken", - "playlists": "Afspeellijsten", - "no_playlists": "Nog geen afspeellijsten", - "select_playlist": "Selecteer een afspeellijst", - "select_hint": "Kies een afspeellijst uit de zijbalk of maak een nieuwe", - "add_tracks": "Tracks Toevoegen", - "no_tracks": "Geen tracks in deze afspeellijst", - "unknown_artist": "Onbekende Artiest", - "unknown_title": "Onbekend", - "confirm_delete": "Deze afspeellijst verwijderen?", - "playlist_name": "Naam afspeellijst", - "create": "Maken", - "delete": "Verwijderen", - "share": "Delen", - "edit": "Bewerken", - "play_all": "Alles Afspelen", - "shuffle": "Shuffle", - "repeat": "Herhalen", - "repeat_one": "Een Herhalen", - "queue": "Wachtrij", - "queue_empty": "Wachtrij is leeg", - "not_playing": "Niet afspelend", - "play": "Afspelen", - "pause": "Pauzeren", - "previous": "Vorige", - "next": "Volgende", - "volume": "Volume", - "mute": "Dempen", - "unmute": "Geluid aan", - "title": "Titel", - "artist": "Artiest", - "album": "Album", - "tracks": "tracks", - "add": "Toevoegen", - "added": "Toegevoegd!", - "added_to_playlist": "toegevoegd aan playlist", - "add_to_playlist": "Aan playlist toevoegen", - "load_error": "Fout bij laden van playlists", - "add_error": "Kon tracks niet toevoegen aan playlist", - "no_playlists_yet": "Nog geen playlists. Maak er eerst een!", - "selected_files": "Geselecteerd:", - "error": "Fout", - "search_audio": "Audiobestanden zoeken…", - "no_audio_files": "Geen audiobestanden gevonden", - "selected": "geselecteerd", - "loading": "Laden…", - "search_error": "Kan audiobestanden niet laden", - "adding": "Toevoegen…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Zoek bestanden...", - "new_folder": "Nieuwe map", - "upload": "Uploaden", - "upload_files": "Bestanden uploaden", - "upload_folder": "Map uploaden", - "upload.uploading": "Uploaden...", - "upload.complete": "{count} / {total} geüpload", - "upload.files": "bestanden", - "rename": "Hernoemen", - "move": "Verplaatsen naar...", - "move_to": "Verplaatsen naar", - "delete": "Verwijderen", - "download": "Downloaden", - "view": "Bekijken", - "cancel": "Annuleren", - "confirm": "Bevestigen", - "share": "Delen", - "favorite": "Toevoegen aan favorieten", - "unfavorite": "Verwijderen uit favorieten", - "copy": "Kopiëren", - "notify": "Melden", - "send": "Verzenden", - "clear_recent": "Recente wissen", - "logout": "Uitloggen", - "create": "Maken", - "search_btn": "Zoeken", - "close": "Sluiten", - "delete_permanently": "Permanent verwijderen", - "empty_trash": "Prullenbak legen", - "open_parent_folder": "Naar bovenliggende map", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Weergave", - "about": "Over OxiCloud", - "about_description": "Cloudopslagplatform gebouwd met Rust & Clean Architecture. Snel, veilig en privé.", - "admin_panel": "Beheerpaneel", - "profile": "Mijn profiel", - "role_user": "Gebruiker", - "theme": { - "light": "Licht", - "dark": "Donker", - "auto": "Zoals systeem" - }, - "manage_groups": "Groepen beheren" - }, - "share": { - "dialogTitle": "Deellink", - "linkLabel": "Deellink:", - "copyLink": "Kopiëren", - "permissions": "Rechten:", - "permissionRead": "Lezen", - "permissionWrite": "Schrijven", - "permissionReshare": "Opnieuw delen", - "password": "Wachtwoordbeveiliging:", - "generatePassword": "Genereren", - "expiration": "Verloopdatum:", - "update": "Delen bijwerken", - "remove": "Delen verwijderen", - "notifyTitle": "Notificatie verzenden", - "notifyEmailLabel": "E-mailadres:", - "notifyMessageLabel": "Bericht (optioneel):", - "notifySend": "Notificatie verzenden", - "shareWithOthers": "Met anderen delen", - "sharePublicly": "Openbaar delen", - "shareSettings": "Deelinstellingen", - "shareCopied": "Link gekopieerd naar klembord", - "shareCreated": "Deellink succesvol aangemaakt", - "shareUpdated": "Deelinstellingen bijgewerkt", - "shareRemoved": "Delen verwijderd", - "inviteByEmail": "Uitnodigen via e-mail — uitnodiging wordt verzonden", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Deellink", - "share_linkLabel": "Deellink:", - "share_copyLink": "Kopiëren", - "share_permissions": "Rechten:", - "share_permissionRead": "Lezen", - "share_permissionWrite": "Schrijven", - "share_permissionReshare": "Opnieuw delen", - "share_password": "Wachtwoordbeveiliging:", - "share_generatePassword": "Genereren", - "share_expiration": "Verloopdatum:", - "share_update": "Share bijwerken", - "share_remove": "Share verwijderen", - "share_notifyTitle": "Notificatie verzenden", - "share_notifyEmailLabel": "E-mailadres:", - "share_notifyMessageLabel": "Bericht (optioneel):", - "share_notifySend": "Notificatie verzenden", - "shared": { - "backToFiles": "Terug naar Bestanden", - "pageTitle": "Gedeelde items", - "pageDescription": "Beheer je gedeelde bestanden en mappen", - "filterType": "Type:", - "filterAll": "Alles", - "filterFiles": "Bestanden", - "filterFolders": "Mappen", - "sortBy": "Sorteren op:", - "sortByName": "Naam", - "sortByDate": "Datum gedeeld", - "sortByExpiration": "Verloop", - "search": "Zoeken", - "colName": "Naam", - "colType": "Type", - "colDateShared": "Gedeeld op", - "colExpiration": "Verloop", - "colPermissions": "Rechten", - "colPassword": "Wachtwoord", - "colActions": "Acties", - "emptyStateTitle": "Nog geen gedeelde items", - "emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", - "goToFiles": "Ga naar Bestanden", - "typeFile": "Bestand", - "typeFolder": "Map", - "noExpiration": "Geen verloop", - "hasPassword": "Ja", - "noPassword": "Nee", - "editShare": "Delen bewerken", - "notifyShare": "Iemand informeren", - "copyLink": "Link kopiëren", - "removeShare": "Delen verwijderen", - "linkCopied": "Link gekopieerd naar klembord!", - "linkCopyFailed": "Kopiëren van link mislukt", - "itemUpdated": "Deelinstellingen bijgewerkt", - "itemRemoved": "Delen verwijderd", - "invalidEmail": "Voer een geldig e-mailadres in", - "notificationSent": "Notificatie verzonden", - "notificationFailed": "Notificatie verzenden mislukt", - "shared_backToFiles": "Terug naar Bestanden", - "shared_pageTitle": "Gedeelde items", - "shared_pageDescription": "Beheer je gedeelde bestanden en mappen", - "shared_filterType": "Type:", - "shared_filterAll": "Alles", - "shared_filterFiles": "Bestanden", - "shared_filterFolders": "Mappen", - "shared_sortBy": "Sorteren op:", - "shared_sortByName": "Naam", - "shared_sortByDate": "Datum gedeeld", - "shared_sortByExpiration": "Verloop", - "shared_search": "Zoeken", - "shared_colName": "Naam", - "shared_colType": "Type", - "shared_colDateShared": "Gedeeld op", - "shared_colExpiration": "Verloop", - "shared_colPermissions": "Rechten", - "shared_colPassword": "Wachtwoord", - "shared_colActions": "Acties", - "shared_emptyStateTitle": "Nog geen gedeelde items", - "shared_emptyStateDesc": "Als je bestanden of mappen deelt, verschijnen ze hier", - "shared_goToFiles": "Ga naar Bestanden", - "shared_typeFile": "Bestand", - "shared_typeFolder": "Map", - "shared_noExpiration": "Geen verloop", - "shared_hasPassword": "Ja", - "shared_noPassword": "Nee", - "shared_editShare": "Delen bewerken", - "shared_notifyShare": "Iemand informeren", - "shared_copyLink": "Link kopiëren", - "shared_removeShare": "Delen verwijderen", - "shared_linkCopied": "Link gekopieerd naar klembord!", - "shared_linkCopyFailed": "Kopiëren van link mislukt", - "shared_itemUpdated": "Deelinstellingen bijgewerkt", - "shared_itemRemoved": "Delen verwijderd", - "shared_invalidEmail": "Voer een geldig e-mailadres in", - "shared_notificationSent": "Notificatie verzonden", - "shared_notificationFailed": "Notificatie verzenden mislukt" - }, - "files": { - "name": "Naam", - "type": "Type", - "size": "Grootte", - "modified": "Gewijzigd", - "no_files": "Geen bestanden in deze map", - "empty_hint": "Upload bestanden of maak mappen aan om te beginnen", - "loading": "Bestanden laden…", - "view_grid": "Rasterweergave", - "view_list": "Lijstweergave", - "file_types": { - "document": "Document", - "image": "Afbeelding", - "video": "Video", - "audio": "Audio", - "pdf": "PDF", - "text": "Tekst", - "folder": "Map", - "spreadsheet": "Spreadsheet", - "presentation": "Presentatie", - "archive": "Archief", - "installer": "Installatiebestand", - "code": "Code" - }, - "owner": "Eigenaar" - }, - "dialogs": { - "rename_folder": "Map hernoemen", - "rename_file": "Bestand hernoemen", - "new_name": "Nieuwe naam", - "new_folder_title": "Nieuwe map", - "folder_name": "Mapnaam", - "folder_placeholder": "Mijn map", - "rename_title": "Hernoemen", - "move_file": "Bestand verplaatsen", - "move_folder": "Map verplaatsen", - "select_destination": "Selecteer doelmap:", - "root": "Hoofdmap", - "delete_confirmation": "Weet je zeker dat je wilt verwijderen", - "and_contents": "en alle inhoud", - "no_undo": "Deze actie kan niet ongedaan gemaakt worden", - "confirm_title": "Actie bevestigen", - "confirm_delete": "Verplaatsen naar prullenbak", - "confirm_delete_file": "Weet je zeker dat je het bestand \"{{name}}\" naar de prullenbak wilt verplaatsen?", - "confirm_delete_folder": "Weet je zeker dat je de map \"{{name}}\" en alle inhoud naar de prullenbak wilt verplaatsen?", - "confirm_permanent_delete": "Permanent verwijderen", - "confirm_permanent_delete_msg": "Weet je zeker dat je dit item permanent wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden.", - "confirm_empty_trash": "Prullenbak legen", - "confirm_delete_share": "Deellink verwijderen", - "confirm_delete_share_msg": "Weet je zeker dat je deze deellink wilt verwijderen?", - "share_file": "Bestand delen", - "share_folder": "Map delen", - "existing_shares": "Bestaande delingen", - "share_options": "Deelopties", - "password": "Wachtwoord", - "expiration": "Verloop", - "permissions": "Rechten", - "generated_link": "Gegenereerde link", - "notify": "Notificatie verzenden", - "recipient": "Ontvanger", - "message": "Bericht", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Verplaatsen naar de thuismap" - }, - "dropzone": { - "drag_files": "Sleep bestanden hierheen of klik om te selecteren", - "drop_files": "Laat bestanden vallen om te uploaden" - }, - "permissions": { - "read": "Lezen", - "write": "Schrijven", - "reshare": "Opnieuw delen" - }, - "errors": { - "file_not_found": "Bestand niet gevonden", - "folder_not_found": "Map niet gevonden", - "delete_error": "Fout bij verwijderen", - "upload_error": "Fout bij uploaden van bestand", - "rename_error": "Fout bij hernoemen", - "move_error": "Fout bij verplaatsen", - "empty_name": "Naam mag niet leeg zijn", - "name_exists": "Een bestand of map met deze naam bestaat al", - "generic_error": "Er is een fout opgetreden", - "group_name_invalid": "De groepsnaam moet voldoen aan het e-mailprefix-formaat (letters, cijfers, punt, streepje, underscore; 1–64 tekens).", - "group_cycle": "Dit lid zou een circulaire groepsverwijzing veroorzaken.", - "group_depth_exceeded": "Deze nestdiepte overschrijdt het maximum (8).", - "group_virtual_immutable": "De groep 'Internal' wordt door het systeem beheerd en kan niet worden gewijzigd.", - "group_not_found": "Groep niet gevonden.", - "group_name_taken": "Er bestaat al een groep met deze naam." - }, - "breadcrumb": { - "home": "Start" - }, - "trash": { - "empty_trash": "Prullenbak legen", - "empty_state": "Prullenbak is leeg", - "original_location": "Oorspronkelijke locatie", - "deleted_date": "Verwijderdatum", - "remaining": "Resterend", - "actions": "Acties", - "restore": "Herstellen", - "delete_permanently": "Permanent verwijderen", - "empty_confirm": "Weet je zeker dat je de prullenbak wilt legen? Dit verwijdert alle items permanent.", - "groupby": { - "remaining_days": "Resterende dagen", - "trashed_time": "Verwijderd op" - } - }, - "daysRemaining": { - "expired": "Verlopen", - "today": "Vandaag", - "tomorrow": "Morgen", - "inDays": "{{count}} dagen" - }, - "expiryChip": { - "never": "Verloopt nooit", - "expired": "Verlopen", - "today": "Verloopt vandaag", - "tomorrow": "Verloopt morgen", - "inDays": "Verloopt over {{count}} dagen", - "onDate": "Verloopt op {{date}}" - }, - "auth": { - "login_title": "Inloggen", - "username": "Gebruikersnaam", - "username_placeholder": "Voer je gebruikersnaam in", - "login_identifier": "Gebruikersnaam of e-mail", - "login_identifier_placeholder": "Voer uw gebruikersnaam of e-mailadres in", - "password": "Wachtwoord", - "password_placeholder": "Voer je wachtwoord in", - "login_button": "Inloggen", - "no_account": "Nog geen account?", - "register": "Aanmelden", - "admin_setup": "Eerste keer?", - "setup": "Administrator instellen", - "register_title": "Account aanmaken", - "email": "E-mailadres", - "email_placeholder": "Voer je e-mailadres in", - "confirm_password": "Bevestig wachtwoord", - "confirm_password_placeholder": "Bevestig je wachtwoord", - "register_button": "Account aanmaken", - "have_account": "Heb je al een account?", - "login": "Inloggen", - "setup_title": "Eerste setup", - "setup_step1": "Admin", - "setup_step2": "Systeem", - "setup_step3": "Voltooid", - "admin_username": "Admin gebruikersnaam", - "admin_email": "Admin e-mailadres", - "admin_password": "Admin wachtwoord", - "create_admin": "Administrator aanmaken", - "back_to_login": "Al ingesteld?", - "admin_success": "Administrator account succesvol aangemaakt! Je kunt nu inloggen.", - "account_success": "Account succesvol aangemaakt! Je kunt nu inloggen.", - "passwords_mismatch": "Wachtwoorden komen niet overeen", - "admin_create_error": "Fout bij het aanmaken van het administratoraccount", - "or": "of", - "sso_login": "Inloggen met SSO", - "sso_login_provider": "Inloggen met {{provider}}", - "magicLinkHint": "Geen wachtwoord? Voer uw e-mailadres in en we sturen u een eenmalige aanmeldlink.", - "magicLinkEmailLabel": "E-mailadres", - "magicLinkEmailPlaceholder": "jij@voorbeeld.nl", - "magicLinkSubmit": "Aanmeldlink versturen", - "magicLinkSent": "Als er een account bestaat voor dat e-mailadres, is een aanmeldlink verzonden. Controleer uw inbox.", - "magicLinkUnavailable": "Aanmelden per e-mail is niet beschikbaar op deze server.", - "magicLinkNetworkError": "Kan de server niet bereiken: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Opslag", - "calculating": "Bezig met berekenen...", - "used": "{{percentage}}% gebruikt ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Dit bestandstype kan niet bekeken worden.", - "download_file": "Bestand downloaden", - "zoom_in": "Inzoomen", - "zoom_out": "Uitzoomen", - "zoom_reset": "Zoom terugzetten" - }, - "language_selector": { - "title": "Welkom!", - "subtitle": "Selecteer je taal om door te gaan", - "continue": "Doorgaan", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Nog geen favorieten", - "empty_hint": "Markeer bestanden of mappen om ze aan je favorieten toe te voegen", - "add": "Toevoegen aan favorieten", - "remove": "Verwijderen uit favorieten", - "added_title": "Toegevoegd aan favorieten", - "added_msg": "toegevoegd aan favorieten", - "removed_title": "Verwijderd uit favorieten", - "removed_msg": "verwijderd uit favorieten" - }, - "recent": { - "title": "Recent", - "clear": "Recente wissen", - "accessed": "Geopend", - "empty_state": "Geen recente bestanden", - "empty_hint": "Bestanden die je opent verschijnen hier", - "loadMore": "Meer laden" - }, - "notifications": { - "file_renamed": "Bestand hernoemd", - "file_renamed_to": "Bestand hernoemd naar \"{{name}}\"", - "folder_renamed": "Map hernoemd", - "folder_renamed_to": "Map hernoemd naar \"{{name}}\"", - "file_uploaded": "Bestand geüpload", - "file_deleted": "Bestand verplaatst naar prullenbak", - "folder_deleted": "Map verplaatst naar prullenbak", - "item_deleted_permanently": "Item permanent verwijderd", - "trash_emptied": "Prullenbak succesvol geleegd", - "title": "Notificaties", - "empty": "Geen notificaties", - "link_created": "Link aangemaakt", - "share_success": "Deellink succesvol aangemaakt", - "upload_files_section_title": "Uploaden hier niet beschikbaar", - "upload_files_section_body": "Ga naar de sectie Bestanden om bestanden te uploaden" - }, - "batch": { - "one_selected": "1 item geselecteerd", - "n_selected": "{{count}} items geselecteerd", - "confirm_delete": "Weet je zeker dat je {{count}} items naar de prullenbak wilt verplaatsen?", - "move_title": "Verplaats {{count}} item(s)", - "add_favorites": "Toevoegen aan favorieten", - "move_copy": "Verplaatsen of kopiëren" - }, - "admin": { - "page_title": "Beheerderspaneel", - "back_to_app": "Terug naar OxiCloud", - "loading": "Laden…", - "access_denied": "Toegang geweigerd", - "access_denied_desc": "Beheerdersrechten vereist.", - "sign_in": "Inloggen", - "tab_dashboard": "Dashboard", - "tab_users": "Gebruikers", - "tab_oidc": "SSO / OIDC", - "total_users": "Totaal gebruikers", - "active_users": "Actieve gebruikers", - "admins": "Beheerders", - "version": "Versie", - "storage_overview": "Opslagoverzicht", - "used": "Gebruikt", - "total_quota": "Totaal quotum", - "usage_pct": "Gebruik %", - "users_over_80": "Gebruikers >80% quotum", - "users_over_quota": "Gebruikers boven quotum", - "system": "Systeem", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Quota", - "enabled": "Ingeschakeld", - "disabled": "Uitgeschakeld", - "active": "Actief", - "off": "Uit", - "allow_registration": "Openbare zelfregistratie toestaan", - "registration_warning": "Openbare registratie is uitgeschakeld. Alleen beheerders kunnen gebruikers aanmaken.", - "user_management": "Gebruikersbeheer", - "create_user": "Gebruiker aanmaken", - "col_user": "Gebruiker", - "col_role": "Rol", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Opslag", - "col_last_login": "Laatste login", - "col_actions": "Acties", - "loading_users": "Gebruikers laden…", - "failed_load_users": "Laden mislukt", - "no_users_found": "Geen gebruikers gevonden", - "showing_users": "Toont {{from}}-{{to}} van {{total}}", - "prev": "Vorige", - "next": "Volgende", - "inactive": "Inactief", - "you_badge": "(jij)", - "local": "Lokaal", - "never": "Nooit", - "just_now": "Zojuist", - "minutes_ago": "{{n}}min geleden", - "hours_ago": "{{n}}u geleden", - "days_ago": "{{n}}d geleden", - "edit_quota_title": "Quotum bewerken", - "reset_password_title": "Wachtwoord resetten", - "toggle_role_title": "Rol wisselen", - "deactivate_title": "Deactiveren", - "activate_title": "Activeren", - "delete_title": "Verwijderen", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "SSO-authenticatie inschakelen", - "provider_name": "Providernaam", - "issuer_url": "Uitgever-URL", - "issuer_url_hint": "OpenID Connect uitgever-URL", - "auto_discover": "Auto-ontdekking", - "discovering": "Ontdekken…", - "client_id": "Client-ID", - "client_secret": "Client-secret", - "client_secret_placeholder": "Laat leeg om huidige waarde te behouden", - "secret_configured": "Een client-secret is al geconfigureerd", - "callback_url": "Callback-URL", - "callback_url_hint": "(registreer bij uw IdP)", - "advanced_settings": "Geavanceerde instellingen", - "scopes": "Scopes", - "auto_provision": "Gebruikers automatisch aanmaken bij eerste login", - "admin_groups": "Beheergroepen", - "admin_groups_hint": "Kommagescheiden OIDC-groepsnamen", - "disable_password": "Wachtwoord-login uitschakelen (alleen OIDC)", - "password_warning": "Dit voorkomt ALLE logins op basis van wachtwoord!", - "test_btn": "Testen", - "save_btn": "Opslaan", - "saving": "Opslaan…", - "settings_saved": "Instellingen opgeslagen — OIDC is nu {{status}}", - "quota_modal_title": "Opslagquotum bijwerken", - "quota_user_label": "Gebruiker:", - "new_quota": "Nieuw quotum", - "quota_unlimited_hint": "0 voor onbeperkt", - "cancel": "Annuleren", - "create_user_title": "Nieuwe gebruiker aanmaken", - "username_label": "Gebruikersnaam", - "username_placeholder": "jandevries", - "username_hint": "3–32 tekens", - "password_label": "Wachtwoord", - "password_placeholder": "Min 8 tekens", - "email_label": "E-mail", - "email_optional": "(optioneel)", - "email_placeholder": "gebruiker@voorbeeld.nl (automatisch indien leeg)", - "role_label": "Rol", - "role_user": "Gebruiker", - "role_admin": "Beheerder", - "quota_label": "Quotum", - "creating": "Aanmaken…", - "reset_pw_title": "Wachtwoord resetten", - "new_password_label": "Nieuw wachtwoord", - "resetting": "Resetten…", - "reset_btn": "Resetten", - "confirm_role_change": "Rol wijzigen naar {{role}}?", - "confirm_deactivate": "Weet u zeker dat u deze gebruiker wilt deactiveren?", - "confirm_activate": "Weet u zeker dat u deze gebruiker wilt activeren?", - "confirm_delete_user": "Gebruiker \"{{name}}\" VERWIJDEREN? Kan niet ongedaan worden gemaakt!", - "confirm_action": "Actie bevestigen", - "confirm_yes": "Bevestigen", - "confirm_no": "Annuleren", - "error_username_short": "Gebruikersnaam moet minimaal 3 tekens bevatten", - "error_password_short": "Wachtwoord moet minimaal 8 tekens bevatten", - "error_generic": "Mislukt", - "error_network": "Netwerkfout: {{message}}", - "error_create_user": "Kan gebruiker niet aanmaken", - "tab_storage": "Opslag", - "storage_title": "Opslagconfiguratie", - "storage_current_backend": "Huidig backend", - "storage_total_blobs": "Totaal blobs", - "storage_total_size": "Totale grootte", - "storage_dedup_ratio": "Deduplicatieverhouding", - "storage_backend": "Backend", - "storage_local": "Lokaal", - "storage_s3": "S3-compatibel", - "storage_provider_preset": "Providerinstelling", - "storage_preset_custom": "Aangepast", - "storage_endpoint_url": "Eindpunt-URL", - "storage_endpoint_hint": "Leeg laten voor AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Regio", - "storage_access_key": "Toegangssleutel", - "storage_secret_key": "Geheime sleutel", - "storage_secret_configured": "Sleutel geconfigureerd", - "storage_key_placeholder": "Nieuwe sleutel invoeren", - "storage_path_style": "Padstijl forceren", - "storage_path_style_hint": "Vereist voor MinIO en sommige S3-compatibele diensten", - "storage_test_connection": "Verbinding testen", - "storage_test_success": "Verbinding geslaagd", - "storage_test_failure": "Verbinding mislukt", - "storage_save": "Configuratie opslaan", - "storage_saved": "Configuratie opgeslagen", - "storage_migration": "Gegevensmigratie", - "storage_migration_coming_soon": "Migratietools binnenkort beschikbaar", - "migration_status_label": "Migratiestatus", - "migration_start": "Migratie starten", - "migration_pause": "Pauzeren", - "migration_resume": "Hervatten", - "migration_verify": "Verifiëren", - "migration_complete": "Voltooien", - "migration_started": "Migratie gestart", - "migration_paused_msg": "Migratie gepauzeerd", - "migration_resumed_msg": "Migratie hervat", - "migration_completed_msg": "Migratie succesvol voltooid", - "migration_verifying": "Bezig met verifiëren...", - "migration_verify_passed": "Verificatie geslaagd", - "migration_verify_failed": "Verificatie mislukt", - "migration_failed_blobs": "Mislukte blobs", - "testing": "Bezig met testen...", - "smtp_disabled": "Uitgeschakeld (host niet ingesteld)", - "smtp_enabled": "Ingeschakeld", - "smtp_enabled_label": "Status", - "smtp_intro": "SMTP wordt uitsluitend geconfigureerd via omgevingsvariabelen (OXICLOUD_SMTP_*). De onderstaande waarden worden gelezen uit de actieve server — om ze te wijzigen, bewerk de omgeving en herstart OxiCloud.", - "smtp_not_configured": "SMTP is niet geconfigureerd op deze server.", - "smtp_send_failed": "Verzenden mislukt.", - "smtp_send_test": "Test-e-mail verzenden", - "smtp_sending": "Bezig met verzenden…", - "smtp_sent": "Test-e-mail verzonden.", - "smtp_server_code": "Serverantwoord", - "smtp_test_intro": "Verzendt een vooraf gedefinieerd diagnostisch bericht naar de onderstaande ontvanger en rapporteert het antwoord van de SMTP-server, zodat je het kunt correleren met je relay-logboeken.", - "smtp_test_missing_to": "Voer een ontvangeradres in.", - "smtp_test_title": "Test-e-mail verzenden", - "smtp_test_to": "Ontvangeradres", - "smtp_title": "Uitgaande e-mail (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profiel", - "back_to_app": "Terug naar OxiCloud", - "loading": "Laden…", - "not_authenticated": "Niet geauthenticeerd", - "not_authenticated_desc": "Log in om uw profiel te bekijken.", - "sign_in": "Inloggen", - "role_admin": "Beheerder", - "role_user": "Gebruiker", - "account_details": "Accountgegevens", - "username": "Gebruikersnaam", - "email": "E-mail", - "role": "Rol", - "last_login": "Laatste login", - "storage": "Opslag", - "used": "Gebruikt", - "quota": "Quotum", - "usage": "Gebruik", - "unlimited": "Onbeperkt", - "app_passwords": "App-wachtwoorden", - "app_pw_desc": "Genereer wachtwoorden voor WebDAV-, CalDAV- en CardDAV-clients. Elk wachtwoord wordt slechts één keer getoond.", - "app_pw_label_placeholder": "Label (bijv. Thunderbird, macOS)", - "generate": "Genereren", - "generating": "Genereren…", - "new_password_for": "Nieuw wachtwoord voor", - "copy_warning": "Kopieer dit wachtwoord nu. U kunt het niet opnieuw bekijken.", - "copy_to_clipboard": "Kopiëren naar klembord", - "col_label": "Label", - "col_created": "Aangemaakt", - "col_last_used": "Laatst gebruikt", - "col_status": "Status", - "active": "Actief", - "revoked": "Ingetrokken", - "revoke_title": "Intrekken", - "no_app_passwords": "Nog geen app-wachtwoorden.", - "client_sessions": "Clientsessies", - "client_sessions_desc": "Automatisch gegenereerd bij het verbinden van een Nextcloud-compatibele client.", - "col_client": "Client", - "never": "Nooit", - "just_now": "Zojuist", - "minutes_ago": "{{n}} min geleden", - "hours_ago": "{{n}}u geleden", - "days_ago": "{{n}} dagen geleden", - "edit_profile": "Profiel bewerken", - "edit_oidc_managed": "Om uw gegevens (naam, voornaam, profielfoto, …) te wijzigen, werk ze bij bij uw identity provider. De wijzigingen verschijnen bij uw volgende aanmelding.", - "username_claim_hint": "2–64 tekens, letters / cijfers / punt / streepje / underscore. Eenmaal gekozen kan de gebruikersnaam niet meer worden gewijzigd (DAV/NextCloud-clients zijn ervan afhankelijk).", - "username_already_claimed": "Gebruikersnaam ingesteld en niet wijzigbaar (DAV/NextCloud-clients zijn ervan afhankelijk).", - "given_name": "Voornaam", - "family_name": "Achternaam", - "notify_on_share": "Stuur me een e-mail wanneer iemand iets met mij deelt", - "notify_on_share_hint": "Wanneer uitgevinkt, verschijnen gedeelde items nog steeds in je account — je krijgt er alleen geen e-mail over.", - "save_profile": "Wijzigingen opslaan", - "profile_saved": "Profiel bijgewerkt", - "profile_no_changes": "Geen wijzigingen om op te slaan.", - "profile_save_failed": "Opslaan mislukt", - "username_taken_error": "Die gebruikersnaam is al in gebruik.", - "username_immutable_error": "Uw gebruikersnaam is al ingesteld en kan hier niet worden gewijzigd. Neem contact op met een beheerder als u wilt hernoemen.", - "change_password": "Wachtwoord wijzigen", - "current_password": "Huidig wachtwoord", - "new_password": "Nieuw wachtwoord", - "min_8_chars": "Minimaal 8 tekens", - "confirm_password": "Bevestig nieuw wachtwoord", - "update_password": "Wachtwoord bijwerken", - "updating": "Bijwerken…", - "password_updated": "Wachtwoord succesvol bijgewerkt", - "passwords_no_match": "Wachtwoorden komen niet overeen", - "password_too_short": "Wachtwoord moet minimaal 8 tekens bevatten", - "password_change_failed": "Wachtwoord wijzigen mislukt", - "error_network": "Netwerkfout: {{message}}", - "error_label_required": "Voer een label in", - "error_create_pw": "App-wachtwoord aanmaken mislukt", - "confirm_revoke": "App-wachtwoord \"{{label}}\" intrekken? Clients die dit wachtwoord gebruiken zullen stoppen.", - "error_revoke": "Intrekken mislukt", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Bezig met uploaden...", - "files": "bestanden", - "complete": "{{count}} / {{total}} geüpload" - }, - "storage_quota_exceeded": "Opslagquotum overschreden", - "sharedwithme": { - "pageTitle": "Gedeeld met mij", - "pageDescription": "Bestanden en mappen die andere gebruikers met u hebben gedeeld", - "emptyStateTitle": "Er is nog niets met u gedeeld", - "emptyStateDesc": "Items die andere gebruikers met u delen, verschijnen hier", - "loadMore": "Meer laden", - "sharedBy": "Gedeeld door", - "colName": "Naam", - "colType": "Type", - "colSharedBy": "Gedeeld door", - "colDate": "Datum gedeeld", - "colPermissions": "Machtigingen" - }, - "groupby": { - "none": "Geen", - "title": "Groeperen op", - "owner": "Eigenaar", - "shareDate": "Deeldatum", - "type": "Type", - "type.folders": "Mappen", - "accessedAt": "Toegangsdatum", - "modifiedAt": "Wijzigingsdatum", - "createdAt": "Aanmaakdatum", - "size": "Grootte", - "favoriteDate": "Favoritendatum", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nieuw" - }, - "dateBucket": { - "today": "Vandaag", - "last7days": "Afgelopen 7 dagen", - "last30days": "Afgelopen 30 dagen" - }, - "groups": { - "title": "Groepen beheren", - "create_button": "Groep maken", - "create_dialog_title": "Nieuwe groep", - "edit_dialog_title": "Groep hernoemen", - "name_label": "Naam", - "name_placeholder": "engineering", - "description_label": "Beschrijving (optioneel)", - "members_section": "Leden", - "add_member_placeholder": "Een gebruiker of groep toevoegen…", - "no_members": "Nog geen leden.", - "remove_member": "Verwijderen", - "delete_group": "Groep verwijderen", - "delete_confirm": "De groep \"{name}\" verwijderen? Aan deze groep gekoppelde rechten worden ingetrokken.", - "empty_state": "Nog geen groepen.", - "load_more": "Meer laden", - "back_to_list": "Terug", - "loading": "Bezig met laden…", - "virtual_badge": "Systeem", - "member_count_zero": "Geen leden", - "member_count_one": "1 lid", - "member_count_other": "{count} leden", - "delete_confirm_label": "Typ de groepsnaam ter bevestiging:", - "delete_confirm_mismatch": "Typ de groepsnaam exact om te bevestigen.", - "virtual_internal_name": "Intern", - "members_loading": "Leden laden…", - "members_empty": "Geen leden", - "virtual_internal_explanation": "Iedere interne gebruiker op deze server" - }, - "myshares": { - "copyLink": "Link kopiëren", - "deleteLink": "Link verwijderen", - "notifyByEmail": "Per e-mail notificeren", - "notifyFailed": "Notificatie kon niet worden verzonden.", - "notifyGroupMembers": "Groepsleden notificeren", - "notifyRateLimited": "Te veel notificaties voor deze ontvanger — probeer het later opnieuw.", - "removeAccess": "Toegang verwijderen", - "resendInvitation": "Uitnodigingsmail opnieuw verzenden" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/pl.json b/static/locales/pl.json deleted file mode 100644 index 320e34b8..00000000 --- a/static/locales/pl.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Ten link logowania nie jest już ważny", - "expired_body": "Link mógł wygasnąć lub został już użyty. Możemy wysłać Ci nowy — dotrze do Twojej skrzynki odbiorczej w ciągu kilku sekund.", - "resend_to": "Wyślij nowy link do {{email}}", - "generic_unavailable": "Ten link logowania nie jest już ważny. Mógł zostać już użyty lub wygasł. Poproś o nowy link na stronie logowania.", - "service_unavailable": "Logowanie magic link nie jest włączone na tym serwerze.", - "internal_error": "Coś poszło nie tak podczas logowania. Spróbuj ponownie.", - "resend_failure": "Coś poszło nie tak podczas wysyłania linku. Spróbuj ponownie.", - "cross_browser_title": "Kontynuować logowanie na tym urządzeniu?", - "cross_browser_body": "Otworzyłeś ten link logowania w innej przeglądarce lub urządzeniu niż to, z którego został zażądany.", - "cross_browser_warning": "Jeśli to Ty zażądałeś tego linku, możesz bezpiecznie kontynuować. W przeciwnym razie zamknij tę stronę — kliknięcie Kontynuuj zaloguje kogoś innego na Twoje konto.", - "cross_browser_continue": "Kontynuuj i zaloguj się", - "resend_confirmation_title": "Sprawdź swoją skrzynkę", - "resend_confirmation_body": "Jeśli link logowania należał do aktywnego konta, nowy link właśnie został wysłany. Sprawdź swoją skrzynkę.", - "return_link": "Powrót do OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", - "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz, klikając poniższy link:\n{{link}}\n\nLink działa raz i wygasa za {{ttl_hours}} godzin.\nJeśli nie spodziewałeś się tego zaproszenia, możesz zignorować tę wiadomość.\n\n— OxiCloud" - }, - "login": { - "subject": "Zaloguj się do OxiCloud", - "body": "Cześć,\n\nUżyj poniższego linku, aby zalogować się do OxiCloud. Link działa raz i wygasa za {{ttl_minutes}} minut. Otwórz go na tym samym urządzeniu, z którego został zażądany.\n\n{{link}}\n\nJeśli nie żądałeś tego linku logowania, możesz zignorować tę wiadomość — nie jest wymagane żadne dalsze działanie.\n\n— OxiCloud" - }, - "kind_file": "plik", - "kind_folder": "folder", - "english_fallback_divider": "--- Wersja angielska poniżej ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} udostępnił Ci {{kind}} w OxiCloud", - "body": "{{inviter_full}} udostępnił Ci {{kind}} w OxiCloud.\n\nOtwórz OxiCloud, aby zobaczyć nowe udostępnienie:\n{{login_link}}\n\nMożesz mieć dodatkowe nowe udostępnienia od {{inviter}} — zaloguj się, aby zobaczyć wszystkie udostępnione Ci elementy.\n\n— OxiCloud\n\nOtrzymujesz tę wiadomość, ponieważ masz konto OxiCloud i preferencja powiadomień o udostępnieniach jest włączona. Możesz ją wyłączyć w swoim profilu (Wyślij mi e-mail, gdy ktoś coś mi udostępni)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Minimalistyczny cloud storage" - }, - "nav": { - "files": "Pliki", - "shared": "Udostępnione", - "recent": "Ostatnie", - "favorites": "Ulubione", - "photos": "Zdjęcia", - "music": "Muzyka", - "trash": "Kosz", - "sharedwithme": "Udostępnione dla mnie" - }, - "photos": { - "empty_state": "Brak zdjęć", - "empty_hint": "Prześlij obrazy lub filmy, aby zobaczyć je tutaj", - "items_selected": "wybrane", - "view_daily": "Dzień", - "view_monthly": "Miesiąc", - "view_yearly": "Rok" - }, - "music": { - "create_playlist": "Utwórz playlistę", - "playlists": "Playlisty", - "no_playlists": "Brak playlist", - "empty_hint": "Utwórz pierwszą playlistę, aby zacząć organizować swoją muzykę", - "select_playlist": "Wybierz playlistę", - "select_hint": "Wybierz playlistę z paska bocznego lub utwórz nową", - "add_tracks": "Dodaj utwory", - "add_to_playlist": "Dodaj do playlisty", - "add": "Dodaj", - "added": "Dodano!", - "added_to_playlist": "dodano do playlisty", - "load_error": "Błąd ładowania playlist", - "add_error": "Nie można dodać utworów do playlisty", - "no_playlists_yet": "Brak playlist. Utwórz pierwszą!", - "selected_files": "Wybrane:", - "no_tracks": "Brak utworów na tej playliście", - "unknown_artist": "Nieznany wykonawca", - "unknown_title": "Nieznany", - "confirm_delete": "Usunąć tę playlistę?", - "playlist_name": "Nazwa playlisty", - "create": "Utwórz", - "delete": "Usuń", - "share": "Udostępnij", - "edit": "Edytuj", - "play_all": "Odtwórz wszystkie", - "shuffle": "Losowo", - "repeat": "Powtarzaj", - "repeat_one": "Powtarzaj jeden", - "queue": "Kolejka", - "queue_empty": "Kolejka jest pusta", - "not_playing": "Nic nie jest odtwarzane", - "play": "Odtwórz", - "pause": "Pauza", - "previous": "Poprzedni", - "next": "Następny", - "volume": "Głośność", - "mute": "Wycisz", - "unmute": "Włącz dźwięk", - "title": "Tytuł", - "artist": "Wykonawca", - "album": "Album", - "tracks": "utwory", - "share_with_user": "ID użytkownika lub e-mail", - "playback_error": "Odtwarzanie nie powiodło się", - "error": "Błąd", - "remove": "Usuń", - "track_removed": "Utwór usunięty", - "manage_shares": "Zarządzaj udostępnieniami", - "no_shares": "Brak udostępnień", - "remove_share": "Usuń udostępnienie", - "can_write": "Może edytować", - "read_only": "Tylko do odczytu", - "public": "Publiczny", - "private": "Prywatny", - "toggle_public": "Widoczność", - "make_public": "Ustaw jako publiczny", - "make_private": "Ustaw jako prywatny", - "set_cover": "Ustaw okładkę", - "cover_updated": "Okładka zaktualizowana", - "search_audio": "Szukaj plików audio…", - "no_audio_files": "Nie znaleziono plików audio", - "selected": "wybrane", - "loading": "Ładowanie…", - "search_error": "Nie można załadować plików audio", - "adding": "Dodawanie…" - }, - "actions": { - "search": "Szukaj plików...", - "new_folder": "Nowy folder", - "upload": "Prześlij", - "upload_files": "Prześlij pliki", - "upload_folder": "Prześlij folder", - "upload.uploading": "Przesyłanie...", - "upload.complete": "{count} / {total} przesłano", - "upload.files": "plików", - "rename": "Zmień nazwę", - "move": "Przenieś do...", - "move_to": "Przenieś do", - "delete": "Usuń", - "download": "Pobierz", - "view": "Pokaż", - "cancel": "Anuluj", - "confirm": "Potwierdź", - "share": "Udostępnij", - "favorite": "Dodaj do ulubionych", - "unfavorite": "Usuń z ulubionych", - "copy": "Kopiuj", - "notify": "Powiadom", - "send": "Wyślij", - "clear_recent": "Wyczyść ostatnie", - "logout": "Wyloguj się", - "create": "Utwórz", - "search_btn": "Szukaj", - "close": "Zamknij", - "delete_permanently": "Usuń trwale", - "empty_trash": "Opróżnij kosz", - "open_parent_folder": "Przejdź do folderu nadrzędnego", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Wygląd", - "about": "O OxiCloud", - "about_description": "Platforma pamięci masowej w chmurze zbudowana w oparciu o Rust & Clean Architecture. Szybka, bezpieczna i prywatna.", - "admin_panel": "Panel administratora", - "profile": "Mój profil", - "role_user": "Użytkownik", - "theme": { - "light": "Jasny", - "dark": "Ciemny", - "auto": "Jak system" - }, - "manage_groups": "Zarządzaj grupami" - }, - "share": { - "dialogTitle": "Link udostępniania", - "linkLabel": "Link udostępniania:", - "copyLink": "Kopiuj", - "permissions": "Uprawnienia:", - "permissionRead": "Odczyt", - "permissionWrite": "Zapis", - "permissionReshare": "Dalsze udostępnianie", - "password": "Ochrona hasłem:", - "generatePassword": "Wygeneruj", - "expiration": "Data wygaśnięcia:", - "update": "Zaktualizuj udostępnienie", - "remove": "Usuń udostępnienie", - "notifyTitle": "Wyślij powiadomienie", - "notifyEmailLabel": "Adres e-mail:", - "notifyMessageLabel": "Wiadomość (opcjonalnie):", - "notifySend": "Wyślij powiadomienie", - "shareWithOthers": "Udostępnij innym", - "sharePublicly": "Udostępnij publicznie", - "shareSettings": "Ustawienia udostępniania", - "shareCopied": "Link skopiowany do schowka", - "shareCreated": "Link udostępniania utworzony pomyślnie", - "shareUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", - "shareRemoved": "Udostępnienie usunięte pomyślnie", - "inviteByEmail": "Zaproś przez e-mail — zaproszenie zostanie wysłane", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link udostępniania", - "share_linkLabel": "Link udostępniania:", - "share_copyLink": "Kopiuj", - "share_permissions": "Uprawnienia:", - "share_permissionRead": "Odczyt", - "share_permissionWrite": "Zapis", - "share_permissionReshare": "Dalsze udostępnianie", - "share_password": "Ochrona hasłem:", - "share_generatePassword": "Wygeneruj", - "share_expiration": "Data wygaśnięcia:", - "share_update": "Zaktualizuj udostępnienie", - "share_remove": "Usuń udostępnienie", - "share_notifyTitle": "Wyślij powiadomienie", - "share_notifyEmailLabel": "Adres e-mail:", - "share_notifyMessageLabel": "Wiadomość (opcjonalnie):", - "share_notifySend": "Wyślij powiadomienie", - "shared": { - "backToFiles": "Powrót do plików", - "pageTitle": "Udostępnione zasoby", - "pageDescription": "Zarządzaj udostępnionymi plikami i folderami", - "filterType": "Typ:", - "filterAll": "Wszystkie", - "filterFiles": "Pliki", - "filterFolders": "Foldery", - "sortBy": "Sortuj według:", - "sortByName": "Nazwa", - "sortByDate": "Data udostępnienia", - "sortByExpiration": "Wygaśnięcie", - "search": "Szukaj", - "colName": "Nazwa", - "colType": "Typ", - "colDateShared": "Data udostępnienia", - "colExpiration": "Wygaśnięcie", - "colPermissions": "Uprawnienia", - "colPassword": "Hasło", - "colActions": "Akcje", - "emptyStateTitle": "Brak udostępnionych zasobów", - "emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", - "goToFiles": "Przejdź do plików", - "typeFile": "Plik", - "typeFolder": "Folder", - "noExpiration": "Bez wygaśnięcia", - "hasPassword": "Tak", - "noPassword": "Nie", - "editShare": "Edytuj udostępnienie", - "notifyShare": "Powiadom kogoś", - "copyLink": "Kopiuj link", - "removeShare": "Usuń udostępnienie", - "linkCopied": "Link skopiowany do schowka!", - "linkCopyFailed": "Nie udało się skopiować linku", - "itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", - "itemRemoved": "Udostępnienie usunięte pomyślnie", - "invalidEmail": "Wprowadź prawidłowy adres e-mail", - "notificationSent": "Powiadomienie wysłane pomyślnie", - "notificationFailed": "Nie udało się wysłać powiadomienia", - "shared_backToFiles": "Powrót do plików", - "shared_pageTitle": "Udostępnione zasoby", - "shared_pageDescription": "Zarządzaj udostępnionymi plikami i folderami", - "shared_filterType": "Typ:", - "shared_filterAll": "Wszystkie", - "shared_filterFiles": "Pliki", - "shared_filterFolders": "Foldery", - "shared_sortBy": "Sortuj według:", - "shared_sortByName": "Nazwa", - "shared_sortByDate": "Data udostępnienia", - "shared_sortByExpiration": "Wygaśnięcie", - "shared_search": "Szukaj", - "shared_colName": "Nazwa", - "shared_colType": "Typ", - "shared_colDateShared": "Data udostępnienia", - "shared_colExpiration": "Wygaśnięcie", - "shared_colPermissions": "Uprawnienia", - "shared_colPassword": "Hasło", - "shared_colActions": "Akcje", - "shared_emptyStateTitle": "Brak udostępnionych zasobów", - "shared_emptyStateDesc": "Gdy udostępnisz pliki lub foldery, pojawią się tutaj", - "shared_goToFiles": "Przejdź do plików", - "shared_typeFile": "Plik", - "shared_typeFolder": "Folder", - "shared_noExpiration": "Bez wygaśnięcia", - "shared_hasPassword": "Tak", - "shared_noPassword": "Nie", - "shared_editShare": "Edytuj udostępnienie", - "shared_notifyShare": "Powiadom kogoś", - "shared_copyLink": "Kopiuj link", - "shared_removeShare": "Usuń udostępnienie", - "shared_linkCopied": "Link skopiowany do schowka!", - "shared_linkCopyFailed": "Nie udało się skopiować linku", - "shared_itemUpdated": "Ustawienia udostępniania zaktualizowane pomyślnie", - "shared_itemRemoved": "Udostępnienie usunięte pomyślnie", - "shared_invalidEmail": "Wprowadź prawidłowy adres e-mail", - "shared_notificationSent": "Powiadomienie wysłane pomyślnie", - "shared_notificationFailed": "Nie udało się wysłać powiadomienia" - }, - "files": { - "name": "Nazwa", - "type": "Typ", - "size": "Rozmiar", - "modified": "Zmodyfikowano", - "no_files": "Brak plików w tym folderze", - "empty_hint": "Prześlij pliki lub utwórz foldery, aby rozpocząć", - "loading": "Ładowanie plików…", - "view_grid": "Widok siatki", - "view_list": "Widok listy", - "file_types": { - "document": "Dokument", - "image": "Obraz", - "video": "Wideo", - "audio": "Audio", - "pdf": "PDF", - "text": "Tekst", - "folder": "Folder", - "spreadsheet": "Arkusz kalkulacyjny", - "presentation": "Prezentacja", - "archive": "Archiwum", - "installer": "Instalator", - "code": "Kod" - }, - "owner": "Właściciel" - }, - "dialogs": { - "rename_folder": "Zmień nazwę folderu", - "rename_file": "Zmień nazwę pliku", - "new_name": "Nowa nazwa", - "new_folder_title": "Nowy folder", - "folder_name": "Nazwa folderu", - "folder_placeholder": "Mój folder", - "rename_title": "Zmień nazwę", - "move_file": "Przenieś plik", - "move_folder": "Przenieś folder", - "select_destination": "Wybierz folder docelowy:", - "select_this_folder": "Wybierz ten folder", - "go_to_parent": ".. (folder nadrzędny)", - "no_subfolders": "Brak podfolderów", - "root": "Główny", - "delete_confirmation": "Czy na pewno chcesz usunąć", - "and_contents": "i całą jego zawartość", - "no_undo": "Tej akcji nie można cofnąć", - "confirm_title": "Potwierdź akcję", - "confirm_delete": "Przenieś do kosza", - "confirm_delete_file": "Czy na pewno chcesz przenieść plik \"{{name}}\" do kosza?", - "confirm_delete_folder": "Czy na pewno chcesz przenieść folder \"{{name}}\" i całą jego zawartość do kosza?", - "confirm_permanent_delete": "Usuń trwale", - "confirm_permanent_delete_msg": "Czy na pewno chcesz trwale usunąć ten element? Tej akcji nie można cofnąć.", - "confirm_empty_trash": "Opróżnij kosz", - "confirm_delete_share": "Usuń link udostępniania", - "confirm_delete_share_msg": "Czy na pewno chcesz usunąć ten link udostępniania?", - "share_file": "Udostępnij plik", - "share_folder": "Udostępnij folder", - "existing_shares": "Istniejące udostępnienia", - "share_options": "Opcje udostępniania", - "password": "Hasło", - "expiration": "Wygaśnięcie", - "permissions": "Uprawnienia", - "generated_link": "Wygenerowany link", - "notify": "Wyślij powiadomienie", - "recipient": "Odbiorca", - "message": "Wiadomość", - "move_to_home": "Przenieś do folderu domowego" - }, - "dropzone": { - "drag_files": "Przeciągnij pliki tutaj lub kliknij, aby wybrać", - "drop_files": "Upuść pliki, aby przesłać" - }, - "permissions": { - "read": "Odczyt", - "write": "Zapis", - "reshare": "Dalsze udostępnianie" - }, - "errors": { - "file_not_found": "Plik nie został znaleziony", - "folder_not_found": "Folder nie został znaleziony", - "delete_error": "Błąd podczas usuwania", - "upload_error": "Błąd podczas przesyłania pliku", - "rename_error": "Błąd podczas zmiany nazwy", - "move_error": "Błąd podczas przenoszenia", - "empty_name": "Nazwa nie może być pusta", - "name_exists": "Plik lub folder o tej nazwie już istnieje", - "generic_error": "Wystąpił błąd", - "group_name_invalid": "Nazwa grupy musi spełniać format prefiksu e-mail (litery, cyfry, kropka, myślnik, podkreślnik; 1–64 znaków).", - "group_cycle": "Ten członek utworzyłby cykliczne odwołanie między grupami.", - "group_depth_exceeded": "Ta głębokość zagnieżdżenia przekracza maksymalną dozwoloną (8).", - "group_virtual_immutable": "Grupa „Internal” jest zarządzana przez system i nie może być modyfikowana.", - "group_not_found": "Grupa nie znaleziona.", - "group_name_taken": "Grupa o tej nazwie już istnieje." - }, - "breadcrumb": { - "home": "Strona główna" - }, - "trash": { - "empty_trash": "Opróżnij kosz", - "empty_state": "Kosz jest pusty", - "original_location": "Pierwotna lokalizacja", - "deleted_date": "Data usunięcia", - "remaining": "Pozostało", - "actions": "Akcje", - "restore": "Przywróć", - "delete_permanently": "Usuń trwale", - "empty_confirm": "Czy na pewno chcesz opróżnić kosz? Wszystkie elementy zostaną trwale usunięte.", - "groupby": { - "remaining_days": "Pozostałe dni", - "trashed_time": "Czas usunięcia" - } - }, - "daysRemaining": { - "expired": "Wygasł", - "today": "Dziś", - "tomorrow": "Jutro", - "inDays": "{{count}} dni" - }, - "expiryChip": { - "never": "Nigdy nie wygasa", - "expired": "Wygasł", - "today": "Wygasa dziś", - "tomorrow": "Wygasa jutro", - "inDays": "Wygasa za {{count}} dni", - "onDate": "Wygasa {{date}}" - }, - "auth": { - "login_title": "Zaloguj się", - "username": "Nazwa użytkownika", - "username_placeholder": "Wprowadź nazwę użytkownika", - "login_identifier": "Nazwa użytkownika lub e-mail", - "login_identifier_placeholder": "Wpisz nazwę użytkownika lub e-mail", - "password": "Hasło", - "password_placeholder": "Wprowadź hasło", - "login_button": "Zaloguj się", - "no_account": "Nie masz konta?", - "register": "Zarejestruj się", - "admin_setup": "Pierwszy raz?", - "setup": "Konfiguracja administratora", - "register_title": "Utwórz konto", - "email": "E-mail", - "email_placeholder": "Wprowadź adres e-mail", - "confirm_password": "Potwierdź hasło", - "confirm_password_placeholder": "Potwierdź hasło", - "register_button": "Utwórz konto", - "have_account": "Masz już konto?", - "login": "Zaloguj się", - "setup_title": "Konfiguracja początkowa", - "setup_step1": "Administrator", - "setup_step2": "System", - "setup_step3": "Gotowe", - "admin_username": "Nazwa administratora", - "admin_email": "E-mail administratora", - "admin_password": "Hasło administratora", - "create_admin": "Utwórz administratora", - "back_to_login": "Już skonfigurowane?", - "admin_success": "Konto administratora zostało utworzone! Możesz się teraz zalogować.", - "account_success": "Konto utworzone pomyślnie! Możesz się teraz zalogować.", - "passwords_mismatch": "Hasła nie są zgodne", - "admin_create_error": "Błąd podczas tworzenia konta administratora", - "or": "lub", - "sso_login": "Zaloguj się przez SSO", - "sso_login_provider": "Zaloguj się przez {{provider}}", - "magicLinkHint": "Brak hasła? Wpisz swój adres e-mail, a wyślemy Ci jednorazowy link do logowania.", - "magicLinkEmailLabel": "Adres e-mail", - "magicLinkEmailPlaceholder": "ty@przyklad.pl", - "magicLinkSubmit": "Wyślij link do logowania", - "magicLinkSent": "Jeśli konto dla tego adresu istnieje, link do logowania został wysłany. Sprawdź swoją skrzynkę odbiorczą.", - "magicLinkUnavailable": "Logowanie e-mailem nie jest dostępne na tym serwerze.", - "magicLinkNetworkError": "Nie udało się połączyć z serwerem: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Pamięć masowa", - "calculating": "Obliczanie...", - "used": "{{percentage}}% wykorzystane ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Tego typu pliku nie można wyświetlić.", - "download_file": "Pobierz plik", - "zoom_in": "Przybliż", - "zoom_out": "Oddal", - "zoom_reset": "Resetuj powiększenie" - }, - "language_selector": { - "title": "Witaj!", - "subtitle": "Wybierz język, aby kontynuować", - "continue": "Kontynuuj", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Brak ulubionych", - "empty_hint": "Oznacz pliki lub foldery gwiazdką, aby dodać je do ulubionych", - "add": "Dodaj do ulubionych", - "remove": "Usuń z ulubionych", - "added_title": "Dodano do ulubionych", - "added_msg": "dodano do ulubionych", - "removed_title": "Usunięto z ulubionych", - "removed_msg": "usunięto z ulubionych" - }, - "recent": { - "title": "Ostatnie", - "clear": "Wyczyść ostatnie", - "accessed": "Otwarte", - "empty_state": "Brak ostatnich plików", - "empty_hint": "Otwarte pliki pojawią się tutaj", - "loadMore": "Załaduj więcej" - }, - "notifications": { - "file_renamed": "Zmieniono nazwę pliku", - "file_renamed_to": "Nazwa pliku zmieniona na \"{{name}}\"", - "folder_renamed": "Zmieniono nazwę folderu", - "folder_renamed_to": "Nazwa folderu zmieniona na \"{{name}}\"", - "file_uploaded": "Plik przesłany", - "file_deleted": "Plik przeniesiony do kosza", - "folder_deleted": "Folder przeniesiony do kosza", - "item_deleted_permanently": "Element trwale usunięty", - "trash_emptied": "Kosz został opróżniony", - "title": "Powiadomienia", - "empty": "Brak powiadomień", - "link_created": "Link utworzony", - "share_success": "Link udostępniania utworzony pomyślnie", - "upload_files_section_title": "Przesyłanie niedostępne tutaj", - "upload_files_section_body": "Przejdź do sekcji Pliki, aby przesłać pliki" - }, - "batch": { - "one_selected": "Wybrano 1 element", - "n_selected": "Wybrano {{count}} elementów", - "confirm_delete": "Czy na pewno chcesz przenieść {{count}} elementów do kosza?", - "move_title": "Przenieś {{count}} element(ów)", - "add_favorites": "Dodaj do ulubionych", - "move_copy": "Przenieś lub kopiuj" - }, - "admin": { - "page_title": "Panel administratora", - "back_to_app": "Powrót do OxiCloud", - "loading": "Ładowanie…", - "access_denied": "Dostęp zabroniony", - "access_denied_desc": "Aby uzyskać dostęp do tego panelu, wymagane są uprawnienia administratora.", - "sign_in": "Zaloguj się", - "tab_dashboard": "Panel", - "tab_users": "Użytkownicy", - "tab_oidc": "SSO / OIDC", - "total_users": "Wszyscy użytkownicy", - "active_users": "Aktywni użytkownicy", - "admins": "Administratorzy", - "version": "Wersja", - "storage_overview": "Przegląd pamięci masowej", - "used": "Wykorzystane", - "total_quota": "Łączny przydział", - "usage_pct": "Wykorzystanie %", - "users_over_80": "Użytkownicy >80% przydziału", - "users_over_quota": "Użytkownicy powyżej przydziału", - "system": "System", - "auth_label": "Uwierzytelnianie", - "oidc_label": "OIDC", - "quotas_label": "Przydziały", - "enabled": "Włączone", - "disabled": "Wyłączone", - "active": "Aktywne", - "off": "Wyłączone", - "allow_registration": "Zezwalaj na publiczną samodzielną rejestrację", - "registration_warning": "Publiczna rejestracja jest wyłączona. Tylko administratorzy mogą tworzyć nowych użytkowników.", - "user_management": "Zarządzanie użytkownikami", - "create_user": "Utwórz użytkownika", - "col_user": "Użytkownik", - "col_role": "Rola", - "col_auth": "Uwierzytelnianie", - "col_status": "Status", - "col_storage": "Pamięć masowa", - "col_last_login": "Ostatnie logowanie", - "col_actions": "Akcje", - "loading_users": "Ładowanie użytkowników…", - "failed_load_users": "Nie udało się załadować użytkowników", - "no_users_found": "Nie znaleziono użytkowników", - "showing_users": "Wyświetlanie {{from}}-{{to}} z {{total}}", - "prev": "Poprzednia", - "next": "Następna", - "inactive": "Nieaktywny", - "you_badge": "(ty)", - "local": "Lokalny", - "never": "Nigdy", - "just_now": "Przed chwilą", - "minutes_ago": "{{n}} min temu", - "hours_ago": "{{n}} godz. temu", - "days_ago": "{{n}} dni temu", - "edit_quota_title": "Edytuj przydział", - "reset_password_title": "Resetuj hasło", - "toggle_role_title": "Przełącz rolę", - "deactivate_title": "Dezaktywuj", - "activate_title": "Aktywuj", - "delete_title": "Usuń", - "sso_title": "Single Sign-On (OIDC / SSO)", - "enable_sso": "Włącz uwierzytelnianie SSO", - "provider_name": "Nazwa dostawcy", - "issuer_url": "URL wystawcy", - "issuer_url_hint": "URL wystawcy OpenID Connect Twojego dostawcy tożsamości", - "auto_discover": "Automatyczne wykrywanie", - "discovering": "Wykrywanie…", - "client_id": "ID klienta", - "client_secret": "Sekret klienta", - "client_secret_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", - "secret_configured": "Sekret klienta jest już skonfigurowany", - "callback_url": "URL zwrotny", - "callback_url_hint": "(zarejestruj w swoim IdP)", - "advanced_settings": "Ustawienia zaawansowane", - "scopes": "Zakresy", - "auto_provision": "Automatycznie twórz użytkowników przy pierwszym logowaniu", - "admin_groups": "Grupy administratorów", - "admin_groups_hint": "Lista nazw grup OIDC oddzielonych przecinkami, mapowanych na rolę administratora", - "disable_password": "Wyłącz logowanie hasłem (tylko OIDC)", - "password_warning": "To uniemożliwi WSZYSTKIE logowania hasłem!", - "test_btn": "Testuj", - "save_btn": "Zapisz", - "saving": "Zapisywanie…", - "settings_saved": "Ustawienia zapisane — OIDC jest teraz {{status}}", - "quota_modal_title": "Aktualizuj przydział pamięci masowej", - "quota_user_label": "Użytkownik:", - "new_quota": "Nowy przydział", - "quota_unlimited_hint": "Ustaw 0 dla nieograniczonego", - "cancel": "Anuluj", - "create_user_title": "Utwórz nowego użytkownika", - "username_label": "Nazwa użytkownika", - "username_placeholder": "jankowalski", - "username_hint": "3–32 znaków", - "password_label": "Hasło", - "password_placeholder": "Min. 8 znaków", - "email_label": "E-mail", - "email_optional": "(opcjonalnie)", - "email_placeholder": "uzytkownik@example.com (generowany automatycznie, jeśli puste)", - "role_label": "Rola", - "role_user": "Użytkownik", - "role_admin": "Administrator", - "quota_label": "Przydział", - "creating": "Tworzenie…", - "reset_pw_title": "Resetuj hasło", - "new_password_label": "Nowe hasło", - "resetting": "Resetowanie…", - "reset_btn": "Resetuj", - "confirm_role_change": "Zmienić rolę na {{role}}?", - "confirm_deactivate": "Czy na pewno chcesz dezaktywować tego użytkownika?", - "confirm_activate": "Czy na pewno chcesz aktywować tego użytkownika?", - "confirm_delete_user": "USUNĄĆ użytkownika \"{{name}}\"? Tej akcji nie można cofnąć!", - "confirm_action": "Potwierdź akcję", - "confirm_yes": "Potwierdź", - "confirm_no": "Anuluj", - "error_username_short": "Nazwa użytkownika musi mieć co najmniej 3 znaki", - "error_password_short": "Hasło musi mieć co najmniej 8 znaków", - "error_generic": "Niepowodzenie", - "error_network": "Błąd sieci: {{message}}", - "error_create_user": "Nie udało się utworzyć użytkownika", - "tab_storage": "Pamięć masowa", - "storage_title": "Backend pamięci masowej", - "storage_current_backend": "Aktywny backend", - "storage_total_blobs": "Liczba blobów", - "storage_total_size": "Łączny rozmiar", - "storage_dedup_ratio": "Współczynnik deduplikacji", - "storage_backend": "Typ backendu", - "storage_local": "Lokalny system plików", - "storage_s3": "Kompatybilny z S3", - "storage_provider_preset": "Ustawienia dostawcy", - "storage_preset_custom": "Niestandardowy", - "storage_endpoint_url": "URL endpointu", - "storage_endpoint_hint": "Pozostaw puste dla domyślnego Amazon S3", - "storage_bucket": "Bucket", - "storage_region": "Region", - "storage_access_key": "Access Key ID", - "storage_secret_key": "Secret Access Key", - "storage_secret_configured": "Klucz tajny jest już skonfigurowany", - "storage_key_placeholder": "Pozostaw puste, aby zachować bieżącą wartość", - "storage_path_style": "Wymuś styl ścieżki", - "storage_path_style_hint": "Wymagane dla MinIO i niektórych dostawców kompatybilnych z S3", - "storage_test_connection": "Testuj połączenie", - "storage_test_success": "Połączenie udane", - "storage_test_failure": "Połączenie nieudane", - "storage_save": "Zapisz", - "storage_saved": "Ustawienia pamięci masowej zapisane pomyślnie", - "storage_migration": "Migracja backendu", - "storage_migration_coming_soon": "Migracja backendu będzie dostępna w przyszłej aktualizacji.", - "migration_status_label": "Status:", - "migration_start": "Rozpocznij migrację", - "migration_pause": "Wstrzymaj", - "migration_resume": "Wznów", - "migration_verify": "Sprawdź integralność", - "migration_complete": "Sfinalizuj", - "migration_started": "Migracja rozpoczęta", - "migration_paused_msg": "Migracja wstrzymana", - "migration_resumed_msg": "Migracja wznowiona", - "migration_completed_msg": "Migracja sfinalizowana. Uruchom ponownie serwer, aby użyć nowego backendu.", - "migration_verifying": "Weryfikowanie…", - "migration_verify_passed": "Weryfikacja zaliczona", - "migration_verify_failed": "Weryfikacja nieudana", - "migration_failed_blobs": "nieudane bloby", - "testing": "Testowanie…", - "smtp_disabled": "Wyłączone (host nieustawiony)", - "smtp_enabled": "Włączone", - "smtp_enabled_label": "Status", - "smtp_intro": "SMTP jest konfigurowany wyłącznie przez zmienne środowiskowe (OXICLOUD_SMTP_*). Poniższe wartości są odczytywane z działającego serwera — aby je zmienić, zmodyfikuj środowisko i uruchom ponownie OxiCloud.", - "smtp_not_configured": "SMTP nie jest skonfigurowany na tym serwerze.", - "smtp_send_failed": "Wysłanie nie powiodło się.", - "smtp_send_test": "Wyślij e-mail testowy", - "smtp_sending": "Wysyłanie…", - "smtp_sent": "E-mail testowy wysłany.", - "smtp_server_code": "Odpowiedź serwera", - "smtp_test_intro": "Wysyła wstępnie zdefiniowaną wiadomość diagnostyczną do podanego poniżej odbiorcy i raportuje odpowiedź serwera SMTP, abyś mógł skorelować ją z logami swojego przekaźnika.", - "smtp_test_missing_to": "Wprowadź adres odbiorcy.", - "smtp_test_title": "Wyślij e-mail testowy", - "smtp_test_to": "Adres odbiorcy", - "smtp_title": "Poczta wychodząca (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Profil", - "back_to_app": "Powrót do OxiCloud", - "loading": "Ładowanie…", - "not_authenticated": "Nieuwierzytelniony", - "not_authenticated_desc": "Zaloguj się, aby zobaczyć swój profil.", - "sign_in": "Zaloguj się", - "role_admin": "Administrator", - "role_user": "Użytkownik", - "account_details": "Szczegóły konta", - "username": "Nazwa użytkownika", - "email": "E-mail", - "role": "Rola", - "last_login": "Ostatnie logowanie", - "storage": "Pamięć masowa", - "used": "Wykorzystane", - "quota": "Przydział", - "usage": "Wykorzystanie", - "unlimited": "Nieograniczone", - "app_passwords": "Hasła aplikacji", - "app_pw_desc": "Generuj hasła dla klientów WebDAV, CalDAV i CardDAV. Każde hasło jest wyświetlane tylko raz.", - "app_pw_label_placeholder": "Etykieta (np. Thunderbird, macOS)", - "generate": "Wygeneruj", - "generating": "Generowanie…", - "new_password_for": "Nowe hasło dla", - "copy_warning": "Skopiuj to hasło teraz. Nie zobaczysz go ponownie.", - "copy_to_clipboard": "Kopiuj do schowka", - "col_label": "Etykieta", - "col_created": "Utworzone", - "col_last_used": "Ostatnio używane", - "col_status": "Status", - "active": "Aktywne", - "revoked": "Unieważnione", - "revoke_title": "Unieważnij", - "no_app_passwords": "Brak haseł aplikacji.", - "client_sessions": "Sesje klientów", - "client_sessions_desc": "Generowane automatycznie po połączeniu z klientem kompatybilnym z Nextcloud.", - "col_client": "Klient", - "never": "Nigdy", - "just_now": "Przed chwilą", - "minutes_ago": "{{n}} min temu", - "hours_ago": "{{n}} godz. temu", - "days_ago": "{{n}} dni temu", - "edit_profile": "Edytuj profil", - "edit_oidc_managed": "Aby zmienić swoje dane (nazwisko, imię, zdjęcie profilowe, …), zaktualizuj je u swojego dostawcy tożsamości. Twoje zmiany pojawią się przy następnym logowaniu.", - "username_claim_hint": "2–64 znaki, litery / cyfry / kropka / myślnik / podkreślenie. Po wybraniu nazwy użytkownika nie można jej zmienić (klienty DAV/NextCloud są od niej zależne).", - "username_already_claimed": "Nazwa użytkownika jest ustawiona i nie może być zmieniona (klienty DAV/NextCloud są od niej zależne).", - "given_name": "Imię", - "family_name": "Nazwisko", - "notify_on_share": "Wyślij mi e-mail, gdy ktoś coś mi udostępni", - "notify_on_share_hint": "Gdy odznaczone, udostępnienia nadal pojawiają się na Twoim koncie — po prostu nie otrzymasz o nich e-maila.", - "save_profile": "Zapisz zmiany", - "profile_saved": "Profil zaktualizowany", - "profile_no_changes": "Brak zmian do zapisania.", - "profile_save_failed": "Zapis nie powiódł się", - "username_taken_error": "Ta nazwa użytkownika jest już zajęta.", - "username_immutable_error": "Twoja nazwa użytkownika jest już ustawiona i nie można jej tutaj zmienić. Skontaktuj się z administratorem, jeśli chcesz ją zmienić.", - "change_password": "Zmień hasło", - "current_password": "Bieżące hasło", - "new_password": "Nowe hasło", - "min_8_chars": "Co najmniej 8 znaków", - "confirm_password": "Potwierdź nowe hasło", - "update_password": "Aktualizuj hasło", - "updating": "Aktualizowanie…", - "password_updated": "Hasło zaktualizowane pomyślnie", - "passwords_no_match": "Hasła nie są zgodne", - "password_too_short": "Hasło musi mieć co najmniej 8 znaków", - "password_change_failed": "Nie udało się zmienić hasła", - "error_network": "Błąd sieci: {{message}}", - "error_label_required": "Wprowadź etykietę", - "error_create_pw": "Nie udało się utworzyć hasła aplikacji", - "confirm_revoke": "Unieważnić hasło aplikacji \"{{label}}\"? Klienci używający tego hasła przestaną działać.", - "error_revoke": "Nie udało się unieważnić hasła aplikacji", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Przesyłanie...", - "files": "plików", - "complete": "{{count}} / {{total}} przesłano" - }, - "storage_quota_exceeded": "Przekroczono limit pamięci masowej", - "sharedwithme": { - "pageTitle": "Udostępnione dla mnie", - "pageDescription": "Pliki i foldery, które inni użytkownicy udostępnili Ci", - "emptyStateTitle": "Nic nie zostało Ci jeszcze udostępnione", - "emptyStateDesc": "Elementy udostępnione Ci przez innych użytkowników pojawią się tutaj", - "loadMore": "Załaduj więcej", - "sharedBy": "Udostępnione przez", - "colName": "Nazwa", - "colType": "Typ", - "colSharedBy": "Udostępnione przez", - "colDate": "Data udostępnienia", - "colPermissions": "Uprawnienia" - }, - "groupby": { - "none": "Brak", - "title": "Grupuj według", - "owner": "Właściciel", - "shareDate": "Data udostępnienia", - "type": "Typ", - "type.folders": "Foldery", - "accessedAt": "Data dostępu", - "modifiedAt": "Data modyfikacji", - "createdAt": "Data utworzenia", - "size": "Rozmiar", - "favoriteDate": "Data dodania do ulubionych", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Nowe" - }, - "dateBucket": { - "today": "Dzisiaj", - "last7days": "Ostatnie 7 dni", - "last30days": "Ostatnie 30 dni" - }, - "groups": { - "title": "Zarządzaj grupami", - "create_button": "Utwórz grupę", - "create_dialog_title": "Nowa grupa", - "edit_dialog_title": "Zmień nazwę grupy", - "name_label": "Nazwa", - "name_placeholder": "inzynieria", - "description_label": "Opis (opcjonalny)", - "members_section": "Członkowie", - "add_member_placeholder": "Dodaj użytkownika lub grupę…", - "no_members": "Brak członków.", - "remove_member": "Usuń", - "delete_group": "Usuń grupę", - "delete_confirm": "Usunąć grupę „{name}\"? Uprawnienia odwołujące się do tej grupy zostaną cofnięte.", - "empty_state": "Brak grup.", - "load_more": "Załaduj więcej", - "back_to_list": "Wstecz", - "loading": "Ładowanie…", - "virtual_badge": "System", - "member_count_zero": "Brak członków", - "member_count_one": "1 członek", - "member_count_other": "{count} członków", - "delete_confirm_label": "Wpisz nazwę grupy, aby potwierdzić:", - "delete_confirm_mismatch": "Wpisz nazwę grupy dokładnie, aby potwierdzić.", - "virtual_internal_name": "Wewnętrzni", - "members_loading": "Ładowanie członków…", - "members_empty": "Brak członków", - "virtual_internal_explanation": "Każdy użytkownik wewnętrzny na tym serwerze" - }, - "myshares": { - "copyLink": "Skopiuj link", - "deleteLink": "Usuń link", - "notifyByEmail": "Powiadom e-mailem", - "notifyFailed": "Nie udało się wysłać powiadomienia.", - "notifyGroupMembers": "Powiadom członków grupy", - "notifyRateLimited": "Zbyt wiele powiadomień dla tego odbiorcy — spróbuj ponownie później.", - "removeAccess": "Usuń dostęp", - "resendInvitation": "Wyślij ponownie e-mail z zaproszeniem" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/pt.json b/static/locales/pt.json deleted file mode 100644 index 1e8b3657..00000000 --- a/static/locales/pt.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Este link de início de sessão já não é válido", - "expired_body": "O link pode ter expirado ou já ter sido usado. Podemos enviar-lhe um novo — chegará à sua caixa de entrada em alguns segundos.", - "resend_to": "Enviar um novo link para {{email}}", - "generic_unavailable": "Este link de início de sessão já não é válido. Pode já ter sido usado ou ter expirado. Solicite um novo link na página de início de sessão.", - "service_unavailable": "O início de sessão por magic link não está ativado neste servidor.", - "internal_error": "Ocorreu um erro ao iniciar sessão. Por favor, tente novamente.", - "resend_failure": "Ocorreu um erro ao enviar o link. Por favor, tente novamente.", - "cross_browser_title": "Continuar o início de sessão neste dispositivo?", - "cross_browser_body": "Abriu este link de início de sessão num navegador ou dispositivo diferente daquele em que o solicitou.", - "cross_browser_warning": "Se foi você quem solicitou este link, é seguro continuar. Caso contrário, feche esta página — clicar em Continuar iniciaria sessão de outra pessoa na sua conta.", - "cross_browser_continue": "Continuar e iniciar sessão", - "resend_confirmation_title": "Verifique a sua caixa de entrada", - "resend_confirmation_body": "Se o link de início de sessão pertencia a uma conta ativa, um novo link acaba de ser enviado. Por favor, verifique a sua caixa de entrada.", - "return_link": "Voltar ao OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", - "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra-o clicando no link abaixo:\n{{link}}\n\nO link é de uso único e expira em {{ttl_hours}} horas.\nSe não esperava este convite, pode ignorar esta mensagem.\n\n— OxiCloud" - }, - "login": { - "subject": "Iniciar sessão no OxiCloud", - "body": "Olá,\n\nUse o link abaixo para iniciar sessão no OxiCloud. O link é de uso único e expira em {{ttl_minutes}} minutos. Abra-o no mesmo dispositivo em que o solicitou.\n\n{{link}}\n\nSe não solicitou este link de início de sessão, pode ignorar esta mensagem — não é necessária qualquer outra ação.\n\n— OxiCloud" - }, - "kind_file": "ficheiro", - "kind_folder": "pasta", - "english_fallback_divider": "--- Versão em inglês abaixo ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} partilhou um {{kind}} consigo no OxiCloud", - "body": "{{inviter_full}} partilhou um {{kind}} consigo no OxiCloud.\n\nAbra o OxiCloud para ver a sua nova partilha:\n{{login_link}}\n\nPode ter mais partilhas novas de {{inviter}} — inicie sessão para ver todos os itens partilhados consigo.\n\n— OxiCloud\n\nRecebeu esta mensagem porque tem uma conta OxiCloud e a preferência de notificação de partilhas está ativada. Pode desativá-la no seu perfil (Avisar-me por e-mail quando alguém compartilhar comigo)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Sistema de armazenamento em nuvem minimalista" - }, - "nav": { - "files": "Arquivos", - "shared": "Compartilhamentos", - "recent": "Recentes", - "favorites": "Favoritos", - "photos": "Fotos", - "music": "Música", - "trash": "Lixeira", - "sharedwithme": "Compartilhados comigo" - }, - "photos": { - "empty_state": "Nenhuma foto ainda", - "empty_hint": "Envie imagens ou vídeos para vê-los aqui", - "items_selected": "selecionados", - "view_daily": "Dia", - "view_monthly": "Mês", - "view_yearly": "Ano" - }, - "music": { - "create_playlist": "Criar Playlist", - "playlists": "Playlists", - "no_playlists": "Nenhuma playlist ainda", - "select_playlist": "Selecione uma playlist", - "select_hint": "Escolha uma playlist na barra lateral ou crie uma nova", - "add_tracks": "Adicionar Faixas", - "no_tracks": "Nenhuma faixa nesta playlist", - "unknown_artist": "Artista Desconhecido", - "unknown_title": "Desconhecido", - "confirm_delete": "Excluir esta playlist?", - "playlist_name": "Nome da playlist", - "create": "Criar", - "delete": "Excluir", - "share": "Compartilhar", - "edit": "Editar", - "play_all": "Reproduzir Tudo", - "shuffle": "Aleatório", - "repeat": "Repetir", - "repeat_one": "Repetir Uma", - "queue": "Fila", - "queue_empty": "Fila vazia", - "not_playing": "Não reproduzindo", - "play": "Reproduzir", - "pause": "Pausar", - "previous": "Anterior", - "next": "Próximo", - "volume": "Volume", - "mute": "Mudo", - "unmute": "Ativar som", - "title": "Título", - "artist": "Artista", - "album": "Álbum", - "tracks": "faixas", - "add": "Adicionar", - "added": "Adicionado!", - "added_to_playlist": "adicionado à playlist", - "add_to_playlist": "Adicionar à playlist", - "load_error": "Erro ao carregar playlists", - "add_error": "Não foi possível adicionar as faixas", - "no_playlists_yet": "Nenhuma playlist ainda. Crie uma primeiro!", - "selected_files": "Selecionados:", - "error": "Erro", - "search_audio": "Pesquisar ficheiros de áudio…", - "no_audio_files": "Nenhum ficheiro de áudio encontrado", - "selected": "selecionados", - "loading": "A carregar…", - "search_error": "Não foi possível carregar os ficheiros de áudio", - "adding": "A adicionar…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Pesquisar arquivos...", - "new_folder": "Nova pasta", - "upload": "Enviar", - "upload_files": "Enviar arquivos", - "upload_folder": "Enviar pasta", - "upload.uploading": "Enviando...", - "upload.complete": "{count} / {total} enviados", - "upload.files": "arquivos", - "rename": "Renomear", - "move": "Mover para...", - "move_to": "Mover para", - "delete": "Excluir", - "download": "Baixar", - "view": "Visualizar", - "cancel": "Cancelar", - "confirm": "Confirmar", - "share": "Compartilhar", - "favorite": "Adicionar aos favoritos", - "unfavorite": "Remover dos favoritos", - "copy": "Copiar", - "notify": "Notificar", - "send": "Enviar", - "clear_recent": "Limpar recentes", - "logout": "Sair", - "create": "Criar", - "search_btn": "Pesquisar", - "close": "Fechar", - "delete_permanently": "Excluir permanentemente", - "empty_trash": "Esvaziar lixeira", - "open_parent_folder": "Ir para a pasta pai", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Aparência", - "about": "Sobre o OxiCloud", - "about_description": "Plataforma de armazenamento em nuvem construída com Rust e Arquitetura Limpa. Rápida, segura e privada.", - "admin_panel": "Painel de administração", - "profile": "Meu perfil", - "role_user": "Usuário", - "theme": { - "light": "Claro", - "dark": "Escuro", - "auto": "Como o sistema" - }, - "manage_groups": "Gerenciar grupos" - }, - "share": { - "dialogTitle": "Link de compartilhamento", - "linkLabel": "Link compartilhado:", - "copyLink": "Copiar", - "permissions": "Permissões:", - "permissionRead": "Leitura", - "permissionWrite": "Escrita", - "permissionReshare": "Recompartilhar", - "password": "Proteção por senha:", - "generatePassword": "Gerar", - "expiration": "Data de expiração:", - "update": "Atualizar compartilhamento", - "remove": "Remover compartilhamento", - "notifyTitle": "Enviar notificação", - "notifyEmailLabel": "Endereço de e-mail:", - "notifyMessageLabel": "Mensagem (opcional):", - "notifySend": "Enviar notificação", - "shareWithOthers": "Compartilhar com outros", - "sharePublicly": "Compartilhar publicamente", - "shareSettings": "Configurações de compartilhamento", - "shareCopied": "Link copiado para a área de transferência", - "shareCreated": "Link de compartilhamento criado com sucesso", - "shareUpdated": "Configurações de compartilhamento atualizadas", - "shareRemoved": "Compartilhamento removido com sucesso", - "inviteByEmail": "Convidar por e-mail — o convite será enviado", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Link de compartilhamento", - "share_linkLabel": "Link compartilhado:", - "share_copyLink": "Copiar", - "share_permissions": "Permissões:", - "share_permissionRead": "Leitura", - "share_permissionWrite": "Escrita", - "share_permissionReshare": "Recompartilhar", - "share_password": "Proteção por senha:", - "share_generatePassword": "Gerar", - "share_expiration": "Data de expiração:", - "share_update": "Atualizar compartilhamento", - "share_remove": "Remover compartilhamento", - "share_notifyTitle": "Enviar notificação", - "share_notifyEmailLabel": "Endereço de e-mail:", - "share_notifyMessageLabel": "Mensagem (opcional):", - "share_notifySend": "Enviar notificação", - "shared": { - "backToFiles": "Voltar aos arquivos", - "pageTitle": "Recursos compartilhados", - "pageDescription": "Gerencie seus arquivos e pastas compartilhados", - "filterType": "Tipo:", - "filterAll": "Todos", - "filterFiles": "Arquivos", - "filterFolders": "Pastas", - "sortBy": "Ordenar por:", - "sortByName": "Nome", - "sortByDate": "Data de compartilhamento", - "sortByExpiration": "Expiração", - "search": "Pesquisar", - "colName": "Nome", - "colType": "Tipo", - "colDateShared": "Data de compartilhamento", - "colExpiration": "Expiração", - "colPermissions": "Permissões", - "colPassword": "Senha", - "colActions": "Ações", - "emptyStateTitle": "Nenhum recurso compartilhado ainda", - "emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", - "goToFiles": "Ir para arquivos", - "typeFile": "Arquivo", - "typeFolder": "Pasta", - "noExpiration": "Sem expiração", - "hasPassword": "Sim", - "noPassword": "Não", - "editShare": "Editar compartilhamento", - "notifyShare": "Notificar alguém", - "copyLink": "Copiar link", - "removeShare": "Remover compartilhamento", - "linkCopied": "Link copiado para a área de transferência!", - "linkCopyFailed": "Falha ao copiar o link", - "itemUpdated": "Configurações de compartilhamento atualizadas", - "itemRemoved": "Compartilhamento removido com sucesso", - "invalidEmail": "Por favor, insira um endereço de e-mail válido", - "notificationSent": "Notificação enviada com sucesso", - "notificationFailed": "Falha ao enviar a notificação", - "shared_backToFiles": "Voltar aos arquivos", - "shared_pageTitle": "Recursos compartilhados", - "shared_pageDescription": "Gerencie seus arquivos e pastas compartilhados", - "shared_filterType": "Tipo:", - "shared_filterAll": "Todos", - "shared_filterFiles": "Arquivos", - "shared_filterFolders": "Pastas", - "shared_sortBy": "Ordenar por:", - "shared_sortByName": "Nome", - "shared_sortByDate": "Data de compartilhamento", - "shared_sortByExpiration": "Expiração", - "shared_search": "Pesquisar", - "shared_colName": "Nome", - "shared_colType": "Tipo", - "shared_colDateShared": "Data de compartilhamento", - "shared_colExpiration": "Expiração", - "shared_colPermissions": "Permissões", - "shared_colPassword": "Senha", - "shared_colActions": "Ações", - "shared_emptyStateTitle": "Nenhum recurso compartilhado ainda", - "shared_emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui", - "shared_goToFiles": "Ir para arquivos", - "shared_typeFile": "Arquivo", - "shared_typeFolder": "Pasta", - "shared_noExpiration": "Sem expiração", - "shared_hasPassword": "Sim", - "shared_noPassword": "Não", - "shared_editShare": "Editar compartilhamento", - "shared_notifyShare": "Notificar alguém", - "shared_copyLink": "Copiar link", - "shared_removeShare": "Remover compartilhamento", - "shared_linkCopied": "Link copiado para a área de transferência!", - "shared_linkCopyFailed": "Falha ao copiar o link", - "shared_itemUpdated": "Configurações de compartilhamento atualizadas", - "shared_itemRemoved": "Compartilhamento removido com sucesso", - "shared_invalidEmail": "Por favor, insira um endereço de e-mail válido", - "shared_notificationSent": "Notificação enviada com sucesso", - "shared_notificationFailed": "Falha ao enviar a notificação" - }, - "files": { - "name": "Nome", - "type": "Tipo", - "size": "Tamanho", - "modified": "Modificado", - "no_files": "Nenhum arquivo nesta pasta", - "empty_hint": "Envie arquivos ou crie pastas para começar", - "loading": "Carregando arquivos…", - "view_grid": "Visualização em grade", - "view_list": "Visualização em lista", - "file_types": { - "document": "Documento", - "image": "Imagem", - "video": "Vídeo", - "audio": "Áudio", - "pdf": "PDF", - "text": "Texto", - "folder": "Pasta", - "spreadsheet": "Planilha", - "presentation": "Apresentação", - "archive": "Arquivo compactado", - "installer": "Instalador", - "code": "Código" - }, - "owner": "Proprietário" - }, - "dialogs": { - "rename_folder": "Renomear pasta", - "rename_file": "Renomear arquivo", - "new_name": "Novo nome", - "new_folder_title": "Nova pasta", - "folder_name": "Nome da pasta", - "folder_placeholder": "Minha pasta", - "rename_title": "Renomear", - "move_file": "Mover arquivo", - "move_folder": "Mover pasta", - "select_destination": "Selecione a pasta de destino:", - "root": "Raiz", - "delete_confirmation": "Tem certeza de que deseja excluir", - "and_contents": "e todo o seu conteúdo", - "no_undo": "Esta ação não pode ser desfeita", - "confirm_title": "Confirmar ação", - "confirm_delete": "Mover para a lixeira", - "confirm_delete_file": "Tem certeza de que deseja mover o arquivo \"{{name}}\" para a lixeira?", - "confirm_delete_folder": "Tem certeza de que deseja mover a pasta \"{{name}}\" e todo o seu conteúdo para a lixeira?", - "confirm_permanent_delete": "Excluir permanentemente", - "confirm_permanent_delete_msg": "Tem certeza de que deseja excluir permanentemente este item? Esta ação não pode ser desfeita.", - "confirm_empty_trash": "Esvaziar lixeira", - "confirm_delete_share": "Excluir link de compartilhamento", - "confirm_delete_share_msg": "Tem certeza de que deseja excluir este link de compartilhamento?", - "share_file": "Compartilhar arquivo", - "share_folder": "Compartilhar pasta", - "existing_shares": "Compartilhamentos existentes", - "share_options": "Opções de compartilhamento", - "password": "Senha", - "expiration": "Expiração", - "permissions": "Permissões", - "generated_link": "Link gerado", - "notify": "Enviar notificação", - "recipient": "Destinatário", - "message": "Mensagem", - "go_to_parent": ".. (parent folder)", - "no_subfolders": "No subfolders", - "select_this_folder": "Select this folder", - "move_to_home": "Mover para a pasta inicial" - }, - "dropzone": { - "drag_files": "Arraste arquivos aqui ou clique para selecionar", - "drop_files": "Solte os arquivos para enviar" - }, - "permissions": { - "read": "Leitura", - "write": "Escrita", - "reshare": "Recompartilhar" - }, - "errors": { - "file_not_found": "Arquivo não encontrado", - "folder_not_found": "Pasta não encontrada", - "delete_error": "Erro ao excluir", - "upload_error": "Erro ao enviar o arquivo", - "rename_error": "Erro ao renomear", - "move_error": "Erro ao mover", - "empty_name": "O nome não pode estar vazio", - "name_exists": "Já existe um arquivo ou pasta com esse nome", - "generic_error": "Ocorreu um erro", - "group_name_invalid": "O nome do grupo deve seguir o formato de prefixo de e-mail (letras, dígitos, ponto, hífen, sublinhado; 1–64 caracteres).", - "group_cycle": "Este membro criaria uma referência circular entre grupos.", - "group_depth_exceeded": "Esta profundidade de aninhamento excede o máximo permitido (8).", - "group_virtual_immutable": "O grupo «Internal» é gerenciado pelo sistema e não pode ser modificado.", - "group_not_found": "Grupo não encontrado.", - "group_name_taken": "Já existe um grupo com este nome." - }, - "breadcrumb": { - "home": "Início" - }, - "trash": { - "empty_trash": "Esvaziar lixeira", - "empty_state": "A lixeira está vazia", - "original_location": "Local original", - "deleted_date": "Data de exclusão", - "remaining": "Restante", - "actions": "Ações", - "restore": "Restaurar", - "delete_permanently": "Excluir permanentemente", - "empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente.", - "groupby": { - "remaining_days": "Dias restantes", - "trashed_time": "Data de exclusão" - } - }, - "daysRemaining": { - "expired": "Expirado", - "today": "Hoje", - "tomorrow": "Amanhã", - "inDays": "{{count}} dias" - }, - "expiryChip": { - "never": "Nunca expira", - "expired": "Expirado", - "today": "Expira hoje", - "tomorrow": "Expira amanhã", - "inDays": "Expira em {{count}} dias", - "onDate": "Expira em {{date}}" - }, - "auth": { - "login_title": "Entrar", - "username": "Usuário", - "username_placeholder": "Digite seu nome de usuário", - "login_identifier": "Usuário ou e-mail", - "login_identifier_placeholder": "Digite seu usuário ou e-mail", - "password": "Senha", - "password_placeholder": "Digite sua senha", - "login_button": "Entrar", - "no_account": "Não tem uma conta?", - "register": "Cadastre-se", - "admin_setup": "Primeira vez?", - "setup": "Configurar administrador", - "register_title": "Criar conta", - "email": "E-mail", - "email_placeholder": "Digite seu e-mail", - "confirm_password": "Confirmar senha", - "confirm_password_placeholder": "Confirme sua senha", - "register_button": "Criar conta", - "have_account": "Já tem uma conta?", - "login": "Entrar", - "setup_title": "Configuração inicial", - "setup_step1": "Admin", - "setup_step2": "Sistema", - "setup_step3": "Concluído", - "admin_username": "Usuário administrador", - "admin_email": "E-mail do administrador", - "admin_password": "Senha do administrador", - "create_admin": "Criar administrador", - "back_to_login": "Já configurado?", - "admin_success": "Conta de administrador criada com sucesso! Agora você pode entrar.", - "account_success": "Conta criada com sucesso! Agora você pode entrar.", - "passwords_mismatch": "As senhas não coincidem", - "admin_create_error": "Erro ao criar conta de administrador", - "or": "ou", - "sso_login": "Entrar com SSO", - "sso_login_provider": "Entrar com {{provider}}", - "magicLinkHint": "Sem senha? Digite seu e-mail e enviaremos um link de acesso único.", - "magicLinkEmailLabel": "Endereço de e-mail", - "magicLinkEmailPlaceholder": "voce@exemplo.com", - "magicLinkSubmit": "Enviar link de acesso", - "magicLinkSent": "Se existir uma conta para este e-mail, um link de acesso foi enviado. Verifique sua caixa de entrada.", - "magicLinkUnavailable": "O acesso por e-mail não está disponível neste servidor.", - "magicLinkNetworkError": "Não foi possível conectar ao servidor: {{message}}", - "magicLinkToggle": "Sem palavra-passe? Receba um link por e-mail", - "passwordsMatch": "As palavras-passe coincidem", - "capsLock": "Caps Lock ativado" - }, - "storage": { - "title": "Armazenamento", - "calculating": "Calculando...", - "used": "{{percentage}}% usado ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Este tipo de arquivo não pode ser visualizado.", - "download_file": "Baixar arquivo", - "zoom_in": "Ampliar", - "zoom_out": "Reduzir", - "zoom_reset": "Redefinir zoom" - }, - "language_selector": { - "title": "Bem-vindo!", - "subtitle": "Selecione seu idioma para continuar", - "continue": "Continuar", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "Nenhum favorito ainda", - "empty_hint": "Marque arquivos ou pastas com estrela para adicioná-los aos seus favoritos", - "add": "Adicionar aos favoritos", - "remove": "Remover dos favoritos", - "added_title": "Adicionado aos favoritos", - "added_msg": "adicionado aos favoritos", - "removed_title": "Removido dos favoritos", - "removed_msg": "removido dos favoritos" - }, - "recent": { - "title": "Recentes", - "clear": "Limpar recentes", - "accessed": "Acessado", - "empty_state": "Nenhum arquivo recente", - "empty_hint": "Os arquivos que você abrir aparecerão aqui", - "loadMore": "Carregar mais" - }, - "notifications": { - "file_renamed": "Arquivo renomeado", - "file_renamed_to": "Arquivo renomeado para \"{{name}}\"", - "folder_renamed": "Pasta renomeada", - "folder_renamed_to": "Pasta renomeada para \"{{name}}\"", - "file_uploaded": "Arquivo enviado", - "file_deleted": "Arquivo movido para a lixeira", - "folder_deleted": "Pasta movida para a lixeira", - "item_deleted_permanently": "Item excluído permanentemente", - "trash_emptied": "Lixeira esvaziada com sucesso", - "empty": "No notifications", - "title": "Notifications", - "link_created": "Link criado", - "share_success": "Link de partilha criado com sucesso", - "upload_files_section_title": "Upload não disponível aqui", - "upload_files_section_body": "Vá para a seção Arquivos para enviar arquivos" - }, - "batch": { - "one_selected": "1 item selecionado", - "n_selected": "{{count}} itens selecionados", - "confirm_delete": "Tem certeza de que deseja mover {{count}} itens para a lixeira?", - "move_title": "Mover {{count}} item(ns)", - "add_favorites": "Adicionar aos favoritos", - "move_copy": "Mover ou copiar" - }, - "admin": { - "page_title": "Painel de Administração", - "back_to_app": "Voltar ao OxiCloud", - "loading": "Carregando…", - "access_denied": "Acesso Negado", - "access_denied_desc": "Privilégios de administrador necessários.", - "sign_in": "Entrar", - "tab_dashboard": "Painel", - "tab_users": "Usuários", - "tab_oidc": "SSO / OIDC", - "total_users": "Total de Usuários", - "active_users": "Usuários Ativos", - "admins": "Admins", - "version": "Versão", - "storage_overview": "Visão do Armazenamento", - "used": "Usado", - "total_quota": "Cota Total", - "usage_pct": "Uso %", - "users_over_80": "Usuários >80% cota", - "users_over_quota": "Usuários acima da cota", - "system": "Sistema", - "auth_label": "Auth", - "oidc_label": "OIDC", - "quotas_label": "Cotas", - "enabled": "Habilitado", - "disabled": "Desabilitado", - "active": "Ativo", - "off": "Inativo", - "allow_registration": "Permitir registro público", - "registration_warning": "O registro público está desabilitado. Apenas administradores podem criar novos usuários.", - "user_management": "Gerenciamento de Usuários", - "create_user": "Criar Usuário", - "col_user": "Usuário", - "col_role": "Função", - "col_auth": "Auth", - "col_status": "Status", - "col_storage": "Armazenamento", - "col_last_login": "Último Login", - "col_actions": "Ações", - "loading_users": "Carregando usuários…", - "failed_load_users": "Falha ao carregar", - "no_users_found": "Nenhum usuário encontrado", - "showing_users": "Mostrando {{from}}-{{to}} de {{total}}", - "prev": "Anterior", - "next": "Próximo", - "inactive": "Inativo", - "you_badge": "(você)", - "local": "Local", - "never": "Nunca", - "just_now": "Agora mesmo", - "minutes_ago": "{{n}}min atrás", - "hours_ago": "{{n}}h atrás", - "days_ago": "{{n}}d atrás", - "edit_quota_title": "Editar cota", - "reset_password_title": "Redefinir senha", - "toggle_role_title": "Alternar função", - "deactivate_title": "Desativar", - "activate_title": "Ativar", - "delete_title": "Excluir", - "sso_title": "Login Único (OIDC / SSO)", - "enable_sso": "Habilitar autenticação SSO", - "provider_name": "Nome do Provedor", - "issuer_url": "URL do Emissor", - "issuer_url_hint": "URL do emissor OpenID Connect", - "auto_discover": "Auto-descoberta", - "discovering": "Descobrindo…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Deixe vazio para manter o valor atual", - "secret_configured": "Um client secret já está configurado", - "callback_url": "URL de Callback", - "callback_url_hint": "(registrar no seu IdP)", - "advanced_settings": "Configurações Avançadas", - "scopes": "Scopes", - "auto_provision": "Provisionar usuários automaticamente", - "admin_groups": "Grupos de Admin", - "admin_groups_hint": "Nomes de grupos OIDC separados por vírgula", - "disable_password": "Desabilitar login por senha (apenas OIDC)", - "password_warning": "Isso impedirá TODOS os logins por senha!", - "test_btn": "Testar", - "save_btn": "Salvar", - "saving": "Salvando…", - "settings_saved": "Configurações salvas — OIDC agora está {{status}}", - "quota_modal_title": "Atualizar Cota", - "quota_user_label": "Usuário:", - "new_quota": "Nova Cota", - "quota_unlimited_hint": "0 para ilimitado", - "cancel": "Cancelar", - "create_user_title": "Criar Novo Usuário", - "username_label": "Nome de usuário", - "username_placeholder": "joaosilva", - "username_hint": "3–32 caracteres", - "password_label": "Senha", - "password_placeholder": "Mín 8 caracteres", - "email_label": "E-mail", - "email_optional": "(opcional)", - "email_placeholder": "usuario@exemplo.com (gerado automaticamente se vazio)", - "role_label": "Função", - "role_user": "Usuário", - "role_admin": "Admin", - "quota_label": "Cota", - "creating": "Criando…", - "reset_pw_title": "Redefinir Senha", - "new_password_label": "Nova Senha", - "resetting": "Redefinindo…", - "reset_btn": "Redefinir", - "confirm_role_change": "Alterar função para {{role}}?", - "confirm_deactivate": "Tem certeza de que deseja desativar este usuário?", - "confirm_activate": "Tem certeza de que deseja ativar este usuário?", - "confirm_delete_user": "EXCLUIR usuário \"{{name}}\"? Não pode ser desfeito!", - "confirm_action": "Confirmar Ação", - "confirm_yes": "Confirmar", - "confirm_no": "Cancelar", - "error_username_short": "O nome de usuário deve ter pelo menos 3 caracteres", - "error_password_short": "A senha deve ter pelo menos 8 caracteres", - "error_generic": "Falha", - "error_network": "Erro de rede: {{message}}", - "error_create_user": "Falha ao criar usuário", - "tab_storage": "Armazenamento", - "storage_title": "Configuração de armazenamento", - "storage_current_backend": "Backend atual", - "storage_total_blobs": "Total de blobs", - "storage_total_size": "Tamanho total", - "storage_dedup_ratio": "Taxa de deduplicação", - "storage_backend": "Backend", - "storage_local": "Local", - "storage_s3": "Compatível com S3", - "storage_provider_preset": "Predefinição do fornecedor", - "storage_preset_custom": "Personalizado", - "storage_endpoint_url": "URL do endpoint", - "storage_endpoint_hint": "Deixar em branco para AWS S3", - "storage_bucket": "Bucket", - "storage_region": "Região", - "storage_access_key": "Chave de acesso", - "storage_secret_key": "Chave secreta", - "storage_secret_configured": "Chave configurada", - "storage_key_placeholder": "Introduzir nova chave", - "storage_path_style": "Forçar estilo de caminho", - "storage_path_style_hint": "Necessário para MinIO e alguns serviços compatíveis com S3", - "storage_test_connection": "Testar ligação", - "storage_test_success": "Ligação bem-sucedida", - "storage_test_failure": "Falha na ligação", - "storage_save": "Guardar configuração", - "storage_saved": "Configuração guardada", - "storage_migration": "Migração de dados", - "storage_migration_coming_soon": "Ferramentas de migração em breve", - "migration_status_label": "Estado da migração", - "migration_start": "Iniciar migração", - "migration_pause": "Pausar", - "migration_resume": "Retomar", - "migration_verify": "Verificar", - "migration_complete": "Concluir", - "migration_started": "Migração iniciada", - "migration_paused_msg": "Migração pausada", - "migration_resumed_msg": "Migração retomada", - "migration_completed_msg": "Migração concluída com sucesso", - "migration_verifying": "A verificar...", - "migration_verify_passed": "Verificação aprovada", - "migration_verify_failed": "Verificação falhou", - "migration_failed_blobs": "Blobs com falha", - "testing": "A testar...", - "smtp_disabled": "Desativado (host não configurado)", - "smtp_enabled": "Ativado", - "smtp_enabled_label": "Estado", - "smtp_intro": "SMTP é configurado exclusivamente através de variáveis de ambiente (OXICLOUD_SMTP_*). Os valores abaixo são lidos do servidor em execução — para alterá-los, edite o ambiente e reinicie o OxiCloud.", - "smtp_not_configured": "SMTP não está configurado neste servidor.", - "smtp_send_failed": "Falha no envio.", - "smtp_send_test": "Enviar e-mail de teste", - "smtp_sending": "A enviar…", - "smtp_sent": "E-mail de teste enviado.", - "smtp_server_code": "Resposta do servidor", - "smtp_test_intro": "Envia uma mensagem de diagnóstico pré-definida para o destinatário abaixo e reporta a resposta do servidor SMTP, para que possa correlacioná-la com os registos do seu relay.", - "smtp_test_missing_to": "Introduza um endereço de destinatário.", - "smtp_test_title": "Enviar um e-mail de teste", - "smtp_test_to": "Endereço do destinatário", - "smtp_title": "E-mail de saída (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Perfil", - "back_to_app": "Voltar ao OxiCloud", - "loading": "Carregando…", - "not_authenticated": "Não Autenticado", - "not_authenticated_desc": "Faça login para ver seu perfil.", - "sign_in": "Entrar", - "role_admin": "Administrador", - "role_user": "Usuário", - "account_details": "Detalhes da Conta", - "username": "Nome de usuário", - "email": "E-mail", - "role": "Função", - "last_login": "Último login", - "storage": "Armazenamento", - "used": "Usado", - "quota": "Cota", - "usage": "Uso", - "unlimited": "Ilimitado", - "app_passwords": "Senhas de Aplicativo", - "app_pw_desc": "Gere senhas para clientes WebDAV, CalDAV e CardDAV. Cada senha é exibida apenas uma vez.", - "app_pw_label_placeholder": "Rótulo (ex. Thunderbird, macOS)", - "generate": "Gerar", - "generating": "Gerando…", - "new_password_for": "Nova senha para", - "copy_warning": "Copie esta senha agora. Você não poderá vê-la novamente.", - "copy_to_clipboard": "Copiar para área de transferência", - "col_label": "Rótulo", - "col_created": "Criado", - "col_last_used": "Último uso", - "col_status": "Status", - "active": "Ativa", - "revoked": "Revogada", - "revoke_title": "Revogar", - "no_app_passwords": "Nenhuma senha de aplicativo ainda.", - "client_sessions": "Sessões de cliente", - "client_sessions_desc": "Geradas automaticamente ao conectar um cliente compatível com Nextcloud.", - "col_client": "Cliente", - "never": "Nunca", - "just_now": "Agora mesmo", - "minutes_ago": "{{n}} min atrás", - "hours_ago": "{{n}}h atrás", - "days_ago": "{{n}} dias atrás", - "edit_profile": "Editar perfil", - "edit_oidc_managed": "Para alterar suas informações (nome, sobrenome, foto de perfil, …), atualize-as no seu provedor de identidade. As mudanças aparecerão no próximo login.", - "username_claim_hint": "De 2 a 64 caracteres, letras / dígitos / ponto / hífen / sublinhado. Uma vez escolhido, o nome de usuário não pode ser alterado (clientes DAV/NextCloud dependem dele).", - "username_already_claimed": "Nome de usuário definido e não pode ser alterado (clientes DAV/NextCloud dependem dele).", - "given_name": "Nome", - "family_name": "Sobrenome", - "notify_on_share": "Avisar-me por e-mail quando alguém compartilhar comigo", - "notify_on_share_hint": "Quando desmarcado, os compartilhamentos continuarão aparecendo na sua conta — você apenas não receberá um e-mail sobre eles.", - "save_profile": "Salvar alterações", - "profile_saved": "Perfil atualizado", - "profile_no_changes": "Sem alterações para salvar.", - "profile_save_failed": "Falha ao salvar", - "username_taken_error": "Este nome de usuário já está em uso.", - "username_immutable_error": "Seu nome de usuário já está definido e não pode ser alterado aqui. Contate um administrador se desejar renomeá-lo.", - "change_password": "Alterar Senha", - "current_password": "Senha Atual", - "new_password": "Nova Senha", - "min_8_chars": "Pelo menos 8 caracteres", - "confirm_password": "Confirmar Nova Senha", - "update_password": "Atualizar Senha", - "updating": "Atualizando…", - "password_updated": "Senha atualizada com sucesso", - "passwords_no_match": "As senhas não coincidem", - "password_too_short": "A senha deve ter pelo menos 8 caracteres", - "password_change_failed": "Falha ao alterar a senha", - "error_network": "Erro de rede: {{message}}", - "error_label_required": "Digite um rótulo", - "error_create_pw": "Falha ao criar senha de aplicativo", - "confirm_revoke": "Revogar senha \"{{label}}\"? Clientes que usam esta senha deixarão de funcionar.", - "error_revoke": "Falha ao revogar", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "A carregar...", - "files": "ficheiros", - "complete": "{{count}} / {{total}} carregados" - }, - "storage_quota_exceeded": "Cota de armazenamento excedida", - "sharedwithme": { - "pageTitle": "Compartilhado comigo", - "pageDescription": "Arquivos e pastas que outros usuários compartilharam com você", - "emptyStateTitle": "Nada compartilhado com você ainda", - "emptyStateDesc": "Itens compartilhados com você por outros usuários aparecerão aqui", - "loadMore": "Carregar mais", - "sharedBy": "Compartilhado por", - "colName": "Nome", - "colType": "Tipo", - "colSharedBy": "Compartilhado por", - "colDate": "Data de compartilhamento", - "colPermissions": "Permissões" - }, - "groupby": { - "none": "Nenhum", - "title": "Agrupar por", - "owner": "Proprietário", - "shareDate": "Data de partilha", - "type": "Tipo", - "type.folders": "Pastas", - "accessedAt": "Data de acesso", - "modifiedAt": "Data de modificação", - "createdAt": "Data de criação", - "size": "Tamanho", - "favoriteDate": "Data de favorito", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Novo" - }, - "dateBucket": { - "today": "Hoje", - "last7days": "Últimos 7 dias", - "last30days": "Últimos 30 dias" - }, - "groups": { - "title": "Gerenciar grupos", - "create_button": "Criar grupo", - "create_dialog_title": "Novo grupo", - "edit_dialog_title": "Renomear grupo", - "name_label": "Nome", - "name_placeholder": "engenharia", - "description_label": "Descrição (opcional)", - "members_section": "Membros", - "add_member_placeholder": "Adicionar um usuário ou grupo…", - "no_members": "Ainda não há membros.", - "remove_member": "Remover", - "delete_group": "Excluir grupo", - "delete_confirm": "Excluir o grupo \"{name}\"? As concessões que referenciam este grupo serão revogadas.", - "empty_state": "Ainda não há grupos.", - "load_more": "Carregar mais", - "back_to_list": "Voltar", - "loading": "Carregando…", - "virtual_badge": "Sistema", - "member_count_zero": "Sem membros", - "member_count_one": "1 membro", - "member_count_other": "{count} membros", - "delete_confirm_label": "Digite o nome do grupo para confirmar:", - "delete_confirm_mismatch": "Digite o nome do grupo exatamente para confirmar.", - "virtual_internal_name": "Interno", - "members_loading": "A carregar membros…", - "members_empty": "Sem membros", - "virtual_internal_explanation": "Todos os utilizadores internos neste servidor" - }, - "myshares": { - "copyLink": "Copiar link", - "deleteLink": "Eliminar link", - "notifyByEmail": "Notificar por e-mail", - "notifyFailed": "Não foi possível enviar a notificação.", - "notifyGroupMembers": "Notificar membros do grupo", - "notifyRateLimited": "Demasiadas notificações para este destinatário — tente novamente mais tarde.", - "removeAccess": "Remover acesso", - "resendInvitation": "Reenviar e-mail de convite" - }, - "sort": { - "asc": "ascendente", - "desc": "descendente" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/ru.json b/static/locales/ru.json deleted file mode 100644 index 5f72caf0..00000000 --- a/static/locales/ru.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "Эта ссылка для входа больше не действительна", - "expired_body": "Срок действия ссылки мог истечь, или она уже была использована. Мы можем отправить вам новую — она придёт в ваш почтовый ящик через несколько секунд.", - "resend_to": "Отправить новую ссылку на {{email}}", - "generic_unavailable": "Эта ссылка для входа больше не действительна. Возможно, она уже использовалась или срок её действия истёк. Запросите новую ссылку на странице входа.", - "service_unavailable": "Вход по magic-ссылке не включён на этом сервере.", - "internal_error": "При входе произошла ошибка. Пожалуйста, попробуйте ещё раз.", - "resend_failure": "При отправке ссылки произошла ошибка. Пожалуйста, попробуйте ещё раз.", - "cross_browser_title": "Продолжить вход на этом устройстве?", - "cross_browser_body": "Вы открыли эту ссылку для входа в браузере или на устройстве, отличном от того, где она была запрошена.", - "cross_browser_warning": "Если эту ссылку запросили вы, можно безопасно продолжить. В противном случае закройте эту страницу — нажатие «Продолжить» приведёт к входу другого человека в вашу учётную запись.", - "cross_browser_continue": "Продолжить и войти", - "resend_confirmation_title": "Проверьте ваш почтовый ящик", - "resend_confirmation_body": "Если ссылка для входа принадлежала активной учётной записи, новая ссылка только что была отправлена. Пожалуйста, проверьте ваш почтовый ящик.", - "return_link": "Вернуться в OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", - "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте, нажав на ссылку ниже:\n{{link}}\n\nСсылка работает один раз и истекает через {{ttl_hours}} часов.\nЕсли вы не ожидали этого приглашения, можете спокойно проигнорировать это сообщение.\n\n— OxiCloud" - }, - "login": { - "subject": "Вход в OxiCloud", - "body": "Здравствуйте,\n\nИспользуйте ссылку ниже, чтобы войти в OxiCloud. Ссылка работает один раз и истекает через {{ttl_minutes}} минут. Откройте её на том же устройстве, где вы её запросили.\n\n{{link}}\n\nЕсли вы не запрашивали эту ссылку для входа, можете спокойно проигнорировать это сообщение — никаких дальнейших действий не требуется.\n\n— OxiCloud" - }, - "kind_file": "файл", - "kind_folder": "папку", - "english_fallback_divider": "--- Английская версия ниже ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} поделился(ась) с вами {{kind}} в OxiCloud", - "body": "{{inviter_full}} поделился(ась) с вами {{kind}} в OxiCloud.\n\nОткройте OxiCloud, чтобы увидеть новый общий ресурс:\n{{login_link}}\n\nВозможно, у вас есть и другие новые общие ресурсы от {{inviter}} — войдите, чтобы увидеть все элементы, которыми с вами поделились.\n\n— OxiCloud\n\nВы получаете это сообщение, потому что у вас есть учётная запись OxiCloud и предпочтение уведомлений об общих ресурсах включено. Вы можете отключить его в своём профиле (Уведомлять меня по электронной почте, когда кто-то делится со мной)." - } - } - }, - "app": { - "title": "OxiCloud", - "description": "Минималистичная система облачного хранения" - }, - "nav": { - "files": "Файлы", - "shared": "Общие", - "recent": "Недавние", - "favorites": "Избранное", - "photos": "Фото", - "music": "Музыка", - "trash": "Корзина", - "sharedwithme": "Доступно мне" - }, - "photos": { - "empty_state": "Фотографий пока нет", - "empty_hint": "Загрузите изображения или видео, чтобы увидеть их здесь", - "items_selected": "выбрано", - "view_daily": "День", - "view_monthly": "Месяц", - "view_yearly": "Год" - }, - "music": { - "create_playlist": "Создать плейлист", - "playlists": "Плейлисты", - "no_playlists": "Плейлистов пока нет", - "select_playlist": "Выберите плейлист", - "select_hint": "Выберите плейлист на панели слева или создайте новый", - "add_tracks": "Добавить треки", - "no_tracks": "В этом плейлисте нет треков", - "unknown_artist": "Неизвестный исполнитель", - "unknown_title": "Неизвестно", - "confirm_delete": "Удалить этот плейлист?", - "playlist_name": "Название плейлиста", - "create": "Создать", - "delete": "Удалить", - "share": "Поделиться", - "edit": "Редактировать", - "play_all": "Воспроизвести все", - "shuffle": "Перемешать", - "repeat": "Повтор", - "repeat_one": "Повторять один", - "queue": "Очередь", - "queue_empty": "Очередь пуста", - "not_playing": "Ничего не играет", - "play": "Воспроизвести", - "pause": "Пауза", - "previous": "Предыдущий", - "next": "Следующий", - "volume": "Громкость", - "mute": "Выключить звук", - "unmute": "Включить звук", - "title": "Название", - "artist": "Исполнитель", - "album": "Альбом", - "tracks": "треков", - "add": "Добавить", - "added": "Добавлено!", - "added_to_playlist": "добавлен в плейлист", - "add_to_playlist": "Добавить в плейлист", - "load_error": "Ошибка загрузки плейлистов", - "add_error": "Не удалось добавить треки в плейлист", - "no_playlists_yet": "Плейлистов пока нет. Создайте сначала!", - "selected_files": "Выбрано:", - "error": "Ошибка", - "search_audio": "Поиск аудиофайлов…", - "no_audio_files": "Аудиофайлы не найдены", - "selected": "выбрано", - "loading": "Загрузка…", - "search_error": "Не удалось загрузить аудиофайлы", - "adding": "Добавление…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "Поиск файлов...", - "new_folder": "Новая папка", - "upload": "Загрузить", - "upload_files": "Загрузить файлы", - "upload_folder": "Загрузить папку", - "upload.uploading": "Загрузка...", - "upload.complete": "{count} / {total} загружено", - "upload.files": "файлов", - "rename": "Переименовать", - "move": "Переместить в...", - "move_to": "Переместить в", - "delete": "Удалить", - "download": "Скачать", - "view": "Просмотр", - "cancel": "Отмена", - "confirm": "Подтвердить", - "share": "Поделиться", - "favorite": "В избранное", - "unfavorite": "Из избранного", - "copy": "Копировать", - "notify": "Уведомить", - "send": "Отправить", - "clear_recent": "Очистить недавние", - "logout": "Выйти", - "create": "Создать", - "search_btn": "Найти", - "close": "Закрыть", - "delete_permanently": "Удалить навсегда", - "empty_trash": "Очистить корзину", - "open_parent_folder": "Перейти в родительскую папку", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "Оформление", - "about": "О OxiCloud", - "about_description": "Платформа облачного хранения на Rust с чистой архитектурой. Быстрая, безопасная и конфиденциальная.", - "admin_panel": "Панель администратора", - "profile": "Мой профиль", - "role_user": "Пользователь", - "theme": { - "light": "Светлая", - "dark": "Тёмная", - "auto": "Как в системе" - }, - "manage_groups": "Управление группами" - }, - "share": { - "dialogTitle": "Ссылка для обмена", - "linkLabel": "Ссылка:", - "copyLink": "Копировать", - "permissions": "Разрешения:", - "permissionRead": "Чтение", - "permissionWrite": "Запись", - "permissionReshare": "Пересылка", - "password": "Защита паролем:", - "generatePassword": "Сгенерировать", - "expiration": "Срок действия:", - "update": "Обновить общий доступ", - "remove": "Удалить общий доступ", - "notifyTitle": "Отправить уведомление", - "notifyEmailLabel": "Адрес email:", - "notifyMessageLabel": "Сообщение (необязательно):", - "notifySend": "Отправить уведомление", - "shareWithOthers": "Поделиться с другими", - "sharePublicly": "Общий доступ", - "shareSettings": "Настройки общего доступа", - "shareCopied": "Ссылка скопирована в буфер обмена", - "shareCreated": "Ссылка для общего доступа успешно создана", - "shareUpdated": "Настройки общего доступа успешно обновлены", - "shareRemoved": "Общий доступ успешно удалён", - "inviteByEmail": "Пригласить по e-mail — приглашение будет отправлено", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "Ссылка для обмена", - "share_linkLabel": "Ссылка:", - "share_copyLink": "Копировать", - "share_permissions": "Разрешения:", - "share_permissionRead": "Чтение", - "share_permissionWrite": "Запись", - "share_permissionReshare": "Пересылка", - "share_password": "Защита паролем:", - "share_generatePassword": "Сгенерировать", - "share_expiration": "Срок действия:", - "share_update": "Обновить общий доступ", - "share_remove": "Удалить общий доступ", - "share_notifyTitle": "Отправить уведомление", - "share_notifyEmailLabel": "Адрес email:", - "share_notifyMessageLabel": "Сообщение (необязательно):", - "share_notifySend": "Отправить уведомление", - "shared": { - "backToFiles": "Назад к файлам", - "pageTitle": "Общие ресурсы", - "pageDescription": "Управление общими файлами и папками", - "filterType": "Тип:", - "filterAll": "Все", - "filterFiles": "Файлы", - "filterFolders": "Папки", - "sortBy": "Сортировка:", - "sortByName": "Имя", - "sortByDate": "Дата", - "sortByExpiration": "Срок действия", - "search": "Поиск", - "colName": "Имя", - "colType": "Тип", - "colDateShared": "Дата общего доступа", - "colExpiration": "Срок действия", - "colPermissions": "Разрешения", - "colPassword": "Пароль", - "colActions": "Действия", - "emptyStateTitle": "Общих ресурсов пока нет", - "emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", - "goToFiles": "Перейти к файлам", - "typeFile": "Файл", - "typeFolder": "Папка", - "noExpiration": "Без срока", - "hasPassword": "Да", - "noPassword": "Нет", - "editShare": "Изменить общий доступ", - "notifyShare": "Уведомить", - "copyLink": "Копировать ссылку", - "removeShare": "Удалить общий доступ", - "linkCopied": "Ссылка скопирована в буфер обмена!", - "linkCopyFailed": "Не удалось скопировать ссылку", - "itemUpdated": "Настройки общего доступа обновлены", - "itemRemoved": "Общий доступ удалён", - "invalidEmail": "Укажите корректный адрес email", - "notificationSent": "Уведомление успешно отправлено", - "notificationFailed": "Не удалось отправить уведомление", - "shared_backToFiles": "Назад к файлам", - "shared_pageTitle": "Общие ресурсы", - "shared_pageDescription": "Управление общими файлами и папками", - "shared_filterType": "Тип:", - "shared_filterAll": "Все", - "shared_filterFiles": "Файлы", - "shared_filterFolders": "Папки", - "shared_sortBy": "Сортировка:", - "shared_sortByName": "Имя", - "shared_sortByDate": "Дата", - "shared_sortByExpiration": "Срок действия", - "shared_search": "Поиск", - "shared_colName": "Имя", - "shared_colType": "Тип", - "shared_colDateShared": "Дата общего доступа", - "shared_colExpiration": "Срок действия", - "shared_colPermissions": "Разрешения", - "shared_colPassword": "Пароль", - "shared_colActions": "Действия", - "shared_emptyStateTitle": "Общих ресурсов пока нет", - "shared_emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь", - "shared_goToFiles": "Перейти к файлам", - "shared_typeFile": "Файл", - "shared_typeFolder": "Папка", - "shared_noExpiration": "Без срока", - "shared_hasPassword": "Да", - "shared_noPassword": "Нет", - "shared_editShare": "Изменить общий доступ", - "shared_notifyShare": "Уведомить", - "shared_copyLink": "Копировать ссылку", - "shared_removeShare": "Удалить общий доступ", - "shared_linkCopied": "Ссылка скопирована в буфер обмена!", - "shared_linkCopyFailed": "Не удалось скопировать ссылку", - "shared_itemUpdated": "Настройки общего доступа обновлены", - "shared_itemRemoved": "Общий доступ удалён", - "shared_invalidEmail": "Укажите корректный адрес email", - "shared_notificationSent": "Уведомление успешно отправлено", - "shared_notificationFailed": "Не удалось отправить уведомление" - }, - "files": { - "name": "Имя", - "type": "Тип", - "size": "Размер", - "modified": "Изменён", - "no_files": "В этой папке нет файлов", - "empty_hint": "Загрузите файлы или создайте папки, чтобы начать", - "loading": "Загрузка файлов…", - "view_grid": "Сетка", - "view_list": "Список", - "file_types": { - "document": "Документ", - "image": "Изображение", - "video": "Видео", - "audio": "Аудио", - "pdf": "PDF", - "text": "Текст", - "folder": "Папка", - "spreadsheet": "Таблица", - "presentation": "Презентация", - "archive": "Архив", - "installer": "Установщик", - "code": "Код" - }, - "owner": "Владелец" - }, - "dialogs": { - "rename_folder": "Переименовать папку", - "rename_file": "Переименовать файл", - "new_name": "Новое имя", - "new_folder_title": "Новая папка", - "folder_name": "Имя папки", - "folder_placeholder": "Моя папка", - "rename_title": "Переименовать", - "move_file": "Переместить файл", - "move_folder": "Переместить папку", - "select_destination": "Выберите папку назначения:", - "select_this_folder": "Выбрать эту папку", - "go_to_parent": ".. (родительская папка)", - "no_subfolders": "Нет подпапок", - "root": "Корень", - "delete_confirmation": "Вы уверены, что хотите удалить", - "and_contents": "и всё его содержимое", - "no_undo": "Это действие невозможно отменить", - "confirm_title": "Подтверждение действия", - "confirm_delete": "В корзину", - "confirm_delete_file": "Вы уверены, что хотите переместить файл \"{{name}}\" в корзину?", - "confirm_delete_folder": "Вы уверены, что хотите переместить папку \"{{name}}\" и всё её содержимое в корзину?", - "confirm_permanent_delete": "Удалить навсегда", - "confirm_permanent_delete_msg": "Вы уверены, что хотите навсегда удалить этот элемент? Это действие невозможно отменить.", - "confirm_empty_trash": "Очистить корзину", - "confirm_delete_share": "Удалить ссылку общего доступа", - "confirm_delete_share_msg": "Вы уверены, что хотите удалить эту ссылку общего доступа?", - "share_file": "Поделиться файлом", - "share_folder": "Поделиться папкой", - "existing_shares": "Существующие общие доступы", - "share_options": "Параметры общего доступа", - "password": "Пароль", - "expiration": "Срок действия", - "permissions": "Разрешения", - "generated_link": "Сгенерированная ссылка", - "notify": "Отправить уведомление", - "recipient": "Получатель", - "message": "Сообщение", - "move_to_home": "Переместить в домашнюю папку" - }, - "dropzone": { - "drag_files": "Перетащите файлы сюда или нажмите для выбора", - "drop_files": "Отпустите файлы для загрузки" - }, - "permissions": { - "read": "Чтение", - "write": "Запись", - "reshare": "Пересылка" - }, - "errors": { - "file_not_found": "Файл не найден", - "folder_not_found": "Папка не найдена", - "delete_error": "Ошибка удаления", - "upload_error": "Ошибка загрузки файла", - "rename_error": "Ошибка переименования", - "move_error": "Ошибка перемещения", - "empty_name": "Имя не может быть пустым", - "name_exists": "Файл или папка с таким именем уже существует", - "generic_error": "Произошла ошибка", - "group_name_invalid": "Имя группы должно соответствовать формату префикса эл. почты (буквы, цифры, точка, дефис, подчёркивание; 1–64 символов).", - "group_cycle": "Этот участник создаст циклическую ссылку между группами.", - "group_depth_exceeded": "Глубина вложенности превышает допустимый максимум (8).", - "group_virtual_immutable": "Группа «Internal» управляется системой и не может быть изменена.", - "group_not_found": "Группа не найдена.", - "group_name_taken": "Группа с таким именем уже существует." - }, - "breadcrumb": { - "home": "Главная" - }, - "trash": { - "empty_trash": "Очистить корзину", - "empty_state": "Корзина пуста", - "original_location": "Исходное расположение", - "deleted_date": "Дата удаления", - "remaining": "Осталось", - "actions": "Действия", - "restore": "Восстановить", - "delete_permanently": "Удалить навсегда", - "empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда.", - "groupby": { - "remaining_days": "Осталось дней", - "trashed_time": "Время удаления" - } - }, - "daysRemaining": { - "expired": "Истёк", - "today": "Сегодня", - "tomorrow": "Завтра", - "inDays": "{{count}} дн." - }, - "expiryChip": { - "never": "Никогда не истекает", - "expired": "Истёк", - "today": "Истекает сегодня", - "tomorrow": "Истекает завтра", - "inDays": "Истекает через {{count}} дн.", - "onDate": "Истекает {{date}}" - }, - "auth": { - "login_title": "Вход", - "username": "Имя пользователя", - "username_placeholder": "Введите имя пользователя", - "login_identifier": "Имя пользователя или email", - "login_identifier_placeholder": "Введите имя пользователя или email", - "password": "Пароль", - "password_placeholder": "Введите пароль", - "login_button": "Войти", - "no_account": "Нет аккаунта?", - "register": "Зарегистрироваться", - "admin_setup": "Первый запуск?", - "setup": "Настроить администратора", - "register_title": "Создание аккаунта", - "email": "Email", - "email_placeholder": "Введите email", - "confirm_password": "Подтвердите пароль", - "confirm_password_placeholder": "Подтвердите пароль", - "register_button": "Создать аккаунт", - "have_account": "Уже есть аккаунт?", - "login": "Войти", - "setup_title": "Начальная настройка", - "setup_step1": "Админ", - "setup_step2": "Система", - "setup_step3": "Готово", - "admin_username": "Имя администратора", - "admin_email": "Email администратора", - "admin_password": "Пароль администратора", - "create_admin": "Создать администратора", - "back_to_login": "Уже настроено?", - "admin_success": "Аккаунт администратора успешно создан! Теперь вы можете войти.", - "account_success": "Аккаунт успешно создан! Теперь вы можете войти.", - "passwords_mismatch": "Пароли не совпадают", - "admin_create_error": "Ошибка создания аккаунта администратора", - "or": "или", - "sso_login": "Войти через SSO", - "sso_login_provider": "Войти через {{provider}}", - "magicLinkHint": "Нет пароля? Введите ваш email, и мы пришлём вам одноразовую ссылку для входа.", - "magicLinkEmailLabel": "Адрес электронной почты", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "Отправить ссылку для входа", - "magicLinkSent": "Если для этого адреса существует учётная запись, ссылка для входа отправлена. Проверьте входящие.", - "magicLinkUnavailable": "Вход по электронной почте недоступен на этом сервере.", - "magicLinkNetworkError": "Не удалось подключиться к серверу: {{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "Хранилище", - "calculating": "Вычисление...", - "used": "{{percentage}}% использовано ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "Предварительный просмотр этого типа файлов недоступен.", - "download_file": "Скачать файл", - "zoom_in": "Увеличить", - "zoom_out": "Уменьшить", - "zoom_reset": "Сбросить масштаб" - }, - "language_selector": { - "title": "Добро пожаловать!", - "subtitle": "Выберите язык для продолжения", - "continue": "Продолжить", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ru": "Русский", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands" - } - }, - "favorites": { - "empty_state": "Избранного пока нет", - "empty_hint": "Добавьте файлы или папки в избранное, нажав на звёздочку", - "add": "В избранное", - "remove": "Из избранного", - "added_title": "Добавлено в избранное", - "added_msg": "добавлено в избранное", - "removed_title": "Удалено из избранного", - "removed_msg": "удалено из избранного" - }, - "recent": { - "title": "Недавние", - "clear": "Очистить недавние", - "accessed": "Открыт", - "empty_state": "Нет недавних файлов", - "empty_hint": "Открытые вами файлы будут отображаться здесь", - "loadMore": "Загрузить ещё" - }, - "notifications": { - "file_renamed": "Файл переименован", - "file_renamed_to": "Файл переименован в \"{{name}}\"", - "folder_renamed": "Папка переименована", - "folder_renamed_to": "Папка переименована в \"{{name}}\"", - "file_uploaded": "Файл загружен", - "file_deleted": "Файл перемещён в корзину", - "folder_deleted": "Папка перемещена в корзину", - "item_deleted_permanently": "Элемент удалён навсегда", - "trash_emptied": "Корзина успешно очищена", - "title": "Уведомления", - "empty": "Нет уведомлений", - "link_created": "Ссылка создана", - "share_success": "Ссылка для общего доступа успешно создана", - "upload_files_section_title": "Загрузка здесь недоступна", - "upload_files_section_body": "Перейдите в раздел «Файлы», чтобы загрузить файлы" - }, - "batch": { - "one_selected": "Выбран 1 элемент", - "n_selected": "Выбрано {{count}} элементов", - "confirm_delete": "Вы уверены, что хотите переместить {{count}} элементов в корзину?", - "move_title": "Переместить {{count}} элементов", - "add_favorites": "В избранное", - "move_copy": "Переместить или копировать" - }, - "admin": { - "page_title": "Панель администратора", - "back_to_app": "Назад в OxiCloud", - "loading": "Загрузка…", - "access_denied": "Доступ запрещён", - "access_denied_desc": "Необходимы права администратора.", - "sign_in": "Войти", - "tab_dashboard": "Панель", - "tab_users": "Пользователи", - "tab_oidc": "SSO / OIDC", - "total_users": "Всего пользователей", - "active_users": "Активные", - "admins": "Администраторы", - "version": "Версия", - "storage_overview": "Обзор хранилища", - "used": "Использовано", - "total_quota": "Общая квота", - "usage_pct": "Использование %", - "users_over_80": "Пользователи >80%", - "users_over_quota": "Сверх квоты", - "system": "Система", - "auth_label": "Аутентификация", - "oidc_label": "OIDC", - "quotas_label": "Квоты", - "enabled": "Включено", - "disabled": "Отключено", - "active": "Активен", - "off": "Выкл", - "allow_registration": "Разрешить публичную регистрацию", - "registration_warning": "Публичная регистрация отключена. Только админы могут создавать пользователей.", - "user_management": "Управление пользователями", - "create_user": "Создать пользователя", - "col_user": "Пользователь", - "col_role": "Роль", - "col_auth": "Аутентификация", - "col_status": "Статус", - "col_storage": "Хранилище", - "col_last_login": "Последний вход", - "col_actions": "Действия", - "loading_users": "Загрузка пользователей…", - "failed_load_users": "Не удалось загрузить", - "no_users_found": "Пользователи не найдены", - "showing_users": "Показано {{from}}-{{to}} из {{total}}", - "prev": "Назад", - "next": "Далее", - "inactive": "Неактивен", - "you_badge": "(вы)", - "local": "Локальный", - "never": "Никогда", - "just_now": "Только что", - "minutes_ago": "{{n}} мин назад", - "hours_ago": "{{n}} ч назад", - "days_ago": "{{n}} дн назад", - "edit_quota_title": "Изменить квоту", - "reset_password_title": "Сбросить пароль", - "toggle_role_title": "Сменить роль", - "deactivate_title": "Деактивировать", - "activate_title": "Активировать", - "delete_title": "Удалить", - "sso_title": "Единый вход (OIDC / SSO)", - "enable_sso": "Включить SSO", - "provider_name": "Имя провайдера", - "issuer_url": "URL издателя", - "issuer_url_hint": "URL издателя OpenID Connect", - "auto_discover": "Авто-обнаружение", - "discovering": "Обнаружение…", - "client_id": "Client ID", - "client_secret": "Client Secret", - "client_secret_placeholder": "Оставьте пустым для сохранения", - "secret_configured": "Client secret уже настроен", - "callback_url": "URL обратного вызова", - "callback_url_hint": "(зарегистрируйте в IdP)", - "advanced_settings": "Расширенные настройки", - "scopes": "Области", - "auto_provision": "Автоматически создавать пользователей", - "admin_groups": "Группы администраторов", - "admin_groups_hint": "Имена групп OIDC через запятую", - "disable_password": "Отключить вход по паролю (только OIDC)", - "password_warning": "Это заблокирует ВСЕ входы по паролю!", - "test_btn": "Тест", - "save_btn": "Сохранить", - "saving": "Сохранение…", - "settings_saved": "Настройки сохранены — OIDC теперь {{status}}", - "quota_modal_title": "Обновить квоту", - "quota_user_label": "Пользователь:", - "new_quota": "Новая квота", - "quota_unlimited_hint": "0 для безлимитного", - "cancel": "Отмена", - "create_user_title": "Создать пользователя", - "username_label": "Имя пользователя", - "username_placeholder": "ivanov", - "username_hint": "3–32 символа", - "password_label": "Пароль", - "password_placeholder": "Мин. 8 символов", - "email_label": "Эл. почта", - "email_optional": "(необязательно)", - "email_placeholder": "user@example.com (автоматически если пусто)", - "role_label": "Роль", - "role_user": "Пользователь", - "role_admin": "Админ", - "quota_label": "Квота", - "creating": "Создание…", - "reset_pw_title": "Сбросить пароль", - "new_password_label": "Новый пароль", - "resetting": "Сброс…", - "reset_btn": "Сбросить", - "confirm_role_change": "Изменить роль на {{role}}?", - "confirm_deactivate": "Деактивировать этого пользователя?", - "confirm_activate": "Активировать этого пользователя?", - "confirm_delete_user": "УДАЛИТЬ пользователя \"{{name}}\"? Нельзя отменить!", - "confirm_action": "Подтвердить", - "confirm_yes": "Подтвердить", - "confirm_no": "Отмена", - "error_username_short": "Имя минимум 3 символа", - "error_password_short": "Пароль минимум 8 символов", - "error_generic": "Ошибка", - "error_network": "Ошибка сети: {{message}}", - "error_create_user": "Не удалось создать", - "tab_storage": "Хранилище", - "storage_title": "Настройка хранилища", - "storage_current_backend": "Текущий бэкенд", - "storage_total_blobs": "Всего блобов", - "storage_total_size": "Общий размер", - "storage_dedup_ratio": "Коэффициент дедупликации", - "storage_backend": "Бэкенд", - "storage_local": "Локальный", - "storage_s3": "Совместимый с S3", - "storage_provider_preset": "Пресет провайдера", - "storage_preset_custom": "Пользовательский", - "storage_endpoint_url": "URL конечной точки", - "storage_endpoint_hint": "Оставьте пустым для AWS S3", - "storage_bucket": "Бакет", - "storage_region": "Регион", - "storage_access_key": "Ключ доступа", - "storage_secret_key": "Секретный ключ", - "storage_secret_configured": "Ключ настроен", - "storage_key_placeholder": "Введите новый ключ", - "storage_path_style": "Принудительный стиль пути", - "storage_path_style_hint": "Требуется для MinIO и некоторых S3-совместимых сервисов", - "storage_test_connection": "Проверить соединение", - "storage_test_success": "Соединение успешно", - "storage_test_failure": "Ошибка соединения", - "storage_save": "Сохранить конфигурацию", - "storage_saved": "Конфигурация сохранена", - "storage_migration": "Миграция данных", - "storage_migration_coming_soon": "Инструменты миграции скоро появятся", - "migration_status_label": "Статус миграции", - "migration_start": "Начать миграцию", - "migration_pause": "Пауза", - "migration_resume": "Возобновить", - "migration_verify": "Проверить", - "migration_complete": "Завершить", - "migration_started": "Миграция начата", - "migration_paused_msg": "Миграция приостановлена", - "migration_resumed_msg": "Миграция возобновлена", - "migration_completed_msg": "Миграция успешно завершена", - "migration_verifying": "Проверка...", - "migration_verify_passed": "Проверка пройдена", - "migration_verify_failed": "Проверка не пройдена", - "migration_failed_blobs": "Неудачные блобы", - "testing": "Тестирование...", - "smtp_disabled": "Отключено (хост не задан)", - "smtp_enabled": "Включено", - "smtp_enabled_label": "Статус", - "smtp_intro": "SMTP настраивается исключительно через переменные окружения (OXICLOUD_SMTP_*). Значения ниже считываются с работающего сервера — чтобы изменить их, отредактируйте окружение и перезапустите OxiCloud.", - "smtp_not_configured": "SMTP не настроен на этом сервере.", - "smtp_send_failed": "Сбой отправки.", - "smtp_send_test": "Отправить тестовое письмо", - "smtp_sending": "Отправка…", - "smtp_sent": "Тестовое письмо отправлено.", - "smtp_server_code": "Ответ сервера", - "smtp_test_intro": "Отправляет заранее заданное диагностическое сообщение указанному ниже получателю и сообщает ответ SMTP-сервера, чтобы вы могли сопоставить его с журналами вашего relay.", - "smtp_test_missing_to": "Введите адрес получателя.", - "smtp_test_title": "Отправить тестовое письмо", - "smtp_test_to": "Адрес получателя", - "smtp_title": "Исходящая почта (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "Профиль", - "back_to_app": "Назад в OxiCloud", - "loading": "Загрузка…", - "not_authenticated": "Не аутентифицирован", - "not_authenticated_desc": "Войдите, чтобы просмотреть свой профиль.", - "sign_in": "Войти", - "role_admin": "Администратор", - "role_user": "Пользователь", - "account_details": "Данные аккаунта", - "username": "Имя пользователя", - "email": "Эл. почта", - "role": "Роль", - "last_login": "Последний вход", - "storage": "Хранилище", - "used": "Использовано", - "quota": "Квота", - "usage": "Использование", - "unlimited": "Безлимитный", - "app_passwords": "Пароли приложений", - "app_pw_desc": "Создайте пароли для клиентов WebDAV, CalDAV и CardDAV. Каждый пароль показывается только один раз.", - "app_pw_label_placeholder": "Метка (напр. Thunderbird, macOS)", - "generate": "Создать", - "generating": "Создание…", - "new_password_for": "Новый пароль для", - "copy_warning": "Скопируйте пароль сейчас. Вы не сможете увидеть его снова.", - "copy_to_clipboard": "Копировать в буфер", - "col_label": "Метка", - "col_created": "Создан", - "col_last_used": "Последнее использование", - "col_status": "Статус", - "active": "Активен", - "revoked": "Отозван", - "revoke_title": "Отозвать", - "no_app_passwords": "Паролей приложений пока нет.", - "client_sessions": "Сессии клиентов", - "client_sessions_desc": "Автоматически создаются при подключении клиента, совместимого с Nextcloud.", - "col_client": "Клиент", - "never": "Никогда", - "just_now": "Только что", - "minutes_ago": "{{n}} мин назад", - "hours_ago": "{{n}} ч назад", - "days_ago": "{{n}} дн назад", - "edit_profile": "Редактировать профиль", - "edit_oidc_managed": "Чтобы изменить ваши данные (имя, фамилию, фотографию профиля, …), обновите их у вашего провайдера идентификации. Изменения появятся при следующем входе.", - "username_claim_hint": "2–64 символа, буквы / цифры / точка / дефис / подчёркивание. После выбора имя пользователя нельзя изменить (клиенты DAV/NextCloud зависят от него).", - "username_already_claimed": "Имя пользователя установлено и не может быть изменено (клиенты DAV/NextCloud зависят от него).", - "given_name": "Имя", - "family_name": "Фамилия", - "notify_on_share": "Уведомлять меня по электронной почте, когда кто-то делится со мной", - "notify_on_share_hint": "Если флажок снят, общие ресурсы по-прежнему будут отображаться в вашей учётной записи — вы просто не будете получать о них письма.", - "save_profile": "Сохранить изменения", - "profile_saved": "Профиль обновлён", - "profile_no_changes": "Нет изменений для сохранения.", - "profile_save_failed": "Не удалось сохранить", - "username_taken_error": "Это имя пользователя уже занято.", - "username_immutable_error": "Ваше имя пользователя уже установлено и не может быть изменено здесь. Свяжитесь с администратором, если нужно переименовать.", - "change_password": "Изменить пароль", - "current_password": "Текущий пароль", - "new_password": "Новый пароль", - "min_8_chars": "Минимум 8 символов", - "confirm_password": "Подтвердите новый пароль", - "update_password": "Обновить пароль", - "updating": "Обновление…", - "password_updated": "Пароль успешно обновлён", - "passwords_no_match": "Пароли не совпадают", - "password_too_short": "Пароль должен быть не менее 8 символов", - "password_change_failed": "Не удалось изменить пароль", - "error_network": "Ошибка сети: {{message}}", - "error_label_required": "Введите метку", - "error_create_pw": "Не удалось создать пароль", - "confirm_revoke": "Отозвать пароль «{{label}}»? Клиенты перестанут работать.", - "error_revoke": "Не удалось отозвать", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "upload": { - "uploading": "Загрузка...", - "files": "файлов", - "complete": "{{count}} / {{total}} загружено" - }, - "storage_quota_exceeded": "Превышена квота хранилища", - "sharedwithme": { - "pageTitle": "Доступно мне", - "pageDescription": "Файлы и папки, которые другие пользователи предоставили вам", - "emptyStateTitle": "Вам ещё ничего не предоставлено", - "emptyStateDesc": "Элементы, которые другие пользователи предоставят вам, появятся здесь", - "loadMore": "Загрузить ещё", - "sharedBy": "Предоставлено", - "colName": "Имя", - "colType": "Тип", - "colSharedBy": "Предоставлено", - "colDate": "Дата предоставления", - "colPermissions": "Права" - }, - "groupby": { - "none": "Нет", - "title": "Группировать по", - "owner": "Владелец", - "shareDate": "Дата общего доступа", - "type": "Тип", - "type.folders": "Папки", - "accessedAt": "Дата доступа", - "modifiedAt": "Дата изменения", - "createdAt": "Дата создания", - "size": "Размер", - "favoriteDate": "Дата добавления в избранное", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "Новые" - }, - "dateBucket": { - "today": "Сегодня", - "last7days": "Последние 7 дней", - "last30days": "Последние 30 дней" - }, - "groups": { - "title": "Управление группами", - "create_button": "Создать группу", - "create_dialog_title": "Новая группа", - "edit_dialog_title": "Переименовать группу", - "name_label": "Имя", - "name_placeholder": "инженеры", - "description_label": "Описание (необязательно)", - "members_section": "Участники", - "add_member_placeholder": "Добавить пользователя или группу…", - "no_members": "Пока нет участников.", - "remove_member": "Удалить", - "delete_group": "Удалить группу", - "delete_confirm": "Удалить группу «{name}»? Все привязанные к ней разрешения будут отозваны.", - "empty_state": "Пока нет групп.", - "load_more": "Загрузить ещё", - "back_to_list": "Назад", - "loading": "Загрузка…", - "virtual_badge": "Системная", - "member_count_zero": "Нет участников", - "member_count_one": "1 участник", - "member_count_other": "{count} участников", - "delete_confirm_label": "Введите имя группы для подтверждения:", - "delete_confirm_mismatch": "Введите имя группы точно для подтверждения.", - "virtual_internal_name": "Внутренние", - "members_loading": "Загрузка участников…", - "members_empty": "Нет участников", - "virtual_internal_explanation": "Каждый внутренний пользователь на этом сервере" - }, - "myshares": { - "copyLink": "Копировать ссылку", - "deleteLink": "Удалить ссылку", - "notifyByEmail": "Уведомить по e-mail", - "notifyFailed": "Не удалось отправить уведомление.", - "notifyGroupMembers": "Уведомить участников группы", - "notifyRateLimited": "Слишком много уведомлений для этого получателя — попробуйте позже.", - "removeAccess": "Отозвать доступ", - "resendInvitation": "Отправить приглашение повторно" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/zh-TW.json b/static/locales/zh-TW.json deleted file mode 100644 index f0efc548..00000000 --- a/static/locales/zh-TW.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "此登入連結已不再有效", - "expired_body": "連結可能已過期或已被使用。我們可以為您發送一個新的 — 幾秒鐘內將會抵達您的收件匣。", - "resend_to": "發送新連結至 {{email}}", - "generic_unavailable": "此登入連結已不再有效。它可能已被使用或已過期。請在登入頁面請求新連結。", - "service_unavailable": "此伺服器未啟用魔法連結登入。", - "internal_error": "登入時發生錯誤。請重試。", - "resend_failure": "發送連結時發生錯誤。請重試。", - "cross_browser_title": "在此裝置上繼續登入?", - "cross_browser_body": "您在與請求時不同的瀏覽器或裝置上開啟了此登入連結。", - "cross_browser_warning": "如果是您請求了此連結,可以安全繼續。否則,請關閉此頁面 — 點擊「繼續」將使其他人登入您的帳戶。", - "cross_browser_continue": "繼續並登入", - "resend_confirmation_title": "請檢查您的收件匣", - "resend_confirmation_body": "如果登入連結屬於活躍帳戶,新連結剛剛已發送。請檢查您的收件匣。", - "return_link": "返回 OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n點擊下方連結開啟:\n{{link}}\n\n該連結僅可使用一次,並將在 {{ttl_hours}} 小時後過期。\n如果您未預期收到此邀請,可以忽略此訊息。\n\n— OxiCloud" - }, - "login": { - "subject": "登入 OxiCloud", - "body": "您好,\n\n使用下方連結登入 OxiCloud。該連結僅可使用一次,並將在 {{ttl_minutes}} 分鐘後過期。請在請求時使用的同一裝置上開啟。\n\n{{link}}\n\n如果您未請求此登入連結,可以忽略此訊息 — 無需進一步操作。\n\n— OxiCloud" - }, - "kind_file": "檔案", - "kind_folder": "資料夾", - "english_fallback_divider": "--- 以下為英文版本 ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} 在 OxiCloud 上與您分享了一個{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上與您分享了一個{{kind}}。\n\n開啟 OxiCloud 檢視您的新分享:\n{{login_link}}\n\n您可能還有來自 {{inviter}} 的其他新分享 — 登入以檢視所有與您分享的項目。\n\n— OxiCloud\n\n您收到此訊息是因為您擁有 OxiCloud 帳戶且分享通知偏好已開啟。您可以在個人資料中關閉它(當有人與我分享時透過電子郵件通知我)。" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "極簡雲端儲存系統" - }, - "nav": { - "files": "檔案", - "shared": "共享", - "recent": "最近", - "favorites": "收藏", - "photos": "照片", - "music": "音樂", - "trash": "回收站", - "sharedwithme": "與我共享" - }, - "photos": { - "empty_state": "還沒有照片", - "empty_hint": "上傳圖片或影片即可在此檢視", - "items_selected": "已選擇", - "view_daily": "日", - "view_monthly": "月", - "view_yearly": "年" - }, - "music": { - "create_playlist": "建立播放列表", - "playlists": "播放列表", - "no_playlists": "還沒有播放列表", - "select_playlist": "選擇一個播放列表", - "select_hint": "從側邊欄選擇播放列表或建立新播放列表", - "add_tracks": "新增曲目", - "no_tracks": "此播放列表中沒有曲目", - "unknown_artist": "未知藝術家", - "unknown_title": "未知", - "confirm_delete": "刪除此播放列表?", - "playlist_name": "播放列表名稱", - "create": "建立", - "delete": "刪除", - "share": "分享", - "edit": "編輯", - "play_all": "全部播放", - "shuffle": "隨機播放", - "repeat": "重複", - "repeat_one": "單曲迴圈", - "queue": "播放佇列", - "queue_empty": "播放佇列為空", - "not_playing": "未播放", - "play": "播放", - "pause": "暫停", - "previous": "上一首", - "next": "下一首", - "volume": "音量", - "mute": "靜音", - "unmute": "取消靜音", - "title": "標題", - "artist": "藝術家", - "album": "專輯", - "tracks": "首曲目", - "add": "新增", - "added": "已新增!", - "added_to_playlist": "已新增到播放列表", - "add_to_playlist": "新增到播放列表", - "load_error": "載入播放列表出錯", - "add_error": "無法將曲目新增到播放列表", - "no_playlists_yet": "暫無播放列表。請先建立一個!", - "selected_files": "已選擇:", - "error": "錯誤", - "search_audio": "搜尋音訊檔案…", - "no_audio_files": "未找到音訊檔案", - "selected": "已選擇", - "loading": "載入中…", - "search_error": "無法載入音訊檔案", - "adding": "新增中…", - "can_write": "可以編輯", - "cover_updated": "封面已更新", - "empty_hint": "建立你的第一個播放列表來開始整理你的音樂", - "make_private": "設為私人", - "make_public": "設為公開", - "manage_shares": "管理共享", - "no_shares": "尚未共享", - "playback_error": "播放失敗", - "private": "私人", - "public": "公開", - "read_only": "唯讀", - "remove": "移除", - "remove_share": "移除共享", - "set_cover": "設定封面", - "share_with_user": "使用者 ID 或電子郵件", - "toggle_public": "可見性", - "track_removed": "曲目已移除" - }, - "actions": { - "search": "搜尋檔案...", - "new_folder": "新建資料夾", - "upload": "上傳", - "upload_files": "上傳檔案", - "upload_folder": "上傳資料夾", - "upload.uploading": "上傳中...", - "upload.complete": "{count} / {total} 已上傳", - "upload.files": "檔案", - "rename": "重新命名", - "move": "移動到...", - "move_to": "移動到", - "delete": "刪除", - "download": "下載", - "view": "檢視", - "cancel": "取消", - "confirm": "確認", - "share": "共享", - "favorite": "新增到收藏", - "unfavorite": "取消收藏", - "copy": "複製", - "notify": "通知", - "send": "傳送", - "clear_recent": "清除最近", - "logout": "退出登入", - "create": "建立", - "search_btn": "搜尋", - "close": "關閉", - "delete_permanently": "永久刪除", - "empty_trash": "清空回收站", - "open_parent_folder": "轉到父資料夾", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "外觀", - "about": "關於 OxiCloud", - "about_description": "基於 Rust 和整潔架構構建的雲端儲存平臺。快速、安全、私密。", - "admin_panel": "管理面板", - "profile": "我的資料", - "role_user": "使用者", - "theme": { - "light": "淺色", - "dark": "深色", - "auto": "跟隨系統" - }, - "manage_groups": "管理群組" - }, - "share": { - "dialogTitle": "共享連結", - "linkLabel": "共享連結:", - "copyLink": "複製", - "permissions": "許可權:", - "permissionRead": "讀取", - "permissionWrite": "寫入", - "permissionReshare": "再共享", - "password": "密碼保護:", - "generatePassword": "生成", - "expiration": "過期日期:", - "update": "更新共享", - "remove": "移除共享", - "notifyTitle": "傳送通知", - "notifyEmailLabel": "電子郵件地址:", - "notifyMessageLabel": "訊息(可選):", - "notifySend": "傳送通知", - "shareWithOthers": "與他人共享", - "sharePublicly": "公開共享", - "shareSettings": "共享設定", - "shareCopied": "連結已複製到剪貼簿", - "shareCreated": "共享連結建立成功", - "shareUpdated": "共享設定更新成功", - "shareRemoved": "共享已移除", - "inviteByEmail": "透過郵件邀請 — 將傳送邀請", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "共享連結", - "share_linkLabel": "共享連結:", - "share_copyLink": "複製", - "share_permissions": "許可權:", - "share_permissionRead": "讀取", - "share_permissionWrite": "寫入", - "share_permissionReshare": "再共享", - "share_password": "密碼保護:", - "share_generatePassword": "生成", - "share_expiration": "過期日期:", - "share_update": "更新共享", - "share_remove": "移除共享", - "share_notifyTitle": "傳送通知", - "share_notifyEmailLabel": "電子郵件地址:", - "share_notifyMessageLabel": "訊息(可選):", - "share_notifySend": "傳送通知", - "shared": { - "backToFiles": "返回檔案", - "pageTitle": "共享資源", - "pageDescription": "管理你的共享檔案和資料夾", - "filterType": "型別:", - "filterAll": "全部", - "filterFiles": "檔案", - "filterFolders": "資料夾", - "sortBy": "排序依據:", - "sortByName": "名稱", - "sortByDate": "共享日期", - "sortByExpiration": "過期日期", - "search": "搜尋", - "colName": "名稱", - "colType": "型別", - "colDateShared": "共享日期", - "colExpiration": "過期日期", - "colPermissions": "許可權", - "colPassword": "密碼", - "colActions": "操作", - "emptyStateTitle": "尚未有共享資源", - "emptyStateDesc": "當你共享檔案或資料夾時,它們會出現在這裡", - "goToFiles": "前往檔案", - "typeFile": "檔案", - "typeFolder": "資料夾", - "noExpiration": "無過期", - "hasPassword": "有", - "noPassword": "無", - "editShare": "編輯共享", - "notifyShare": "通知某人", - "copyLink": "複製連結", - "removeShare": "移除共享", - "linkCopied": "連結已複製到剪貼簿!", - "linkCopyFailed": "複製連結失敗", - "itemUpdated": "共享設定更新成功", - "itemRemoved": "共享已移除成功", - "invalidEmail": "請輸入有效的電子郵件地址", - "notificationSent": "通知已成功傳送", - "notificationFailed": "傳送通知失敗", - "shared_backToFiles": "返回檔案", - "shared_colActions": "操作", - "shared_colDateShared": "共享日期", - "shared_colExpiration": "過期日期", - "shared_colName": "名稱", - "shared_colPassword": "密碼", - "shared_colPermissions": "許可權", - "shared_colType": "類型", - "shared_copyLink": "複製連結", - "shared_editShare": "編輯共享", - "shared_emptyStateDesc": "當你共享檔案或資料夾時,它們會顯示在此", - "shared_emptyStateTitle": "尚無共享資源", - "shared_filterAll": "全部", - "shared_filterFiles": "檔案", - "shared_filterFolders": "資料夾", - "shared_filterType": "類型:", - "shared_goToFiles": "前往檔案", - "shared_hasPassword": "是", - "shared_invalidEmail": "請輸入有效的電子郵件地址", - "shared_itemRemoved": "共享已成功移除", - "shared_itemUpdated": "共享設定已成功更新", - "shared_linkCopied": "連結已複製到剪貼簿!", - "shared_linkCopyFailed": "複製連結失敗", - "shared_noExpiration": "永不過期", - "shared_noPassword": "否", - "shared_notificationFailed": "傳送通知失敗", - "shared_notificationSent": "通知已成功傳送", - "shared_notifyShare": "通知對方", - "shared_pageDescription": "管理你的共享檔案與資料夾", - "shared_pageTitle": "共享資源", - "shared_removeShare": "移除共享", - "shared_search": "搜尋", - "shared_sortBy": "排序方式:", - "shared_sortByDate": "共享日期", - "shared_sortByExpiration": "過期日期", - "shared_sortByName": "名稱", - "shared_typeFile": "檔案", - "shared_typeFolder": "資料夾" - }, - "files": { - "name": "名稱", - "type": "型別", - "size": "大小", - "modified": "修改日期", - "no_files": "此資料夾中沒有檔案", - "empty_hint": "上傳檔案或建立資料夾以開始使用", - "loading": "正在載入檔案…", - "view_grid": "網格檢視", - "view_list": "列表檢視", - "file_types": { - "document": "文件", - "image": "圖片", - "video": "影片", - "audio": "音訊", - "pdf": "PDF", - "text": "文字", - "folder": "資料夾", - "spreadsheet": "電子表格", - "presentation": "簡報", - "archive": "壓縮檔案", - "installer": "安裝程式", - "code": "程式碼" - }, - "owner": "擁有者" - }, - "dialogs": { - "rename_folder": "重新命名資料夾", - "new_name": "新名稱", - "new_folder_title": "新建資料夾", - "folder_name": "資料夾名稱", - "folder_placeholder": "我的資料夾", - "rename_title": "重新命名", - "move_file": "移動檔案", - "select_destination": "選擇目標資料夾", - "root": "根目錄", - "delete_confirmation": "你確定要刪除", - "and_contents": "及其所有內容", - "no_undo": "此操作無法撤銷", - "share_file": "共享檔案", - "share_folder": "共享資料夾", - "existing_shares": "現有共享", - "share_options": "共享選項", - "password": "密碼", - "expiration": "過期日期", - "permissions": "許可權", - "generated_link": "生成的連結", - "notify": "傳送通知", - "recipient": "收件人", - "message": "訊息", - "confirm_delete": "移至回收站", - "confirm_delete_file": "確定要將檔案「{{name}}」移至回收站嗎?", - "confirm_delete_folder": "確定要將資料夾「{{name}}」及其所有內容移至回收站嗎?", - "confirm_delete_share": "刪除共享連結", - "confirm_delete_share_msg": "確定要刪除此共享連結嗎?", - "confirm_empty_trash": "清空回收站", - "confirm_permanent_delete": "永久刪除", - "confirm_permanent_delete_msg": "確定要永久刪除此項目嗎?此操作無法復原。", - "confirm_title": "確認操作", - "go_to_parent": ".. (上層資料夾)", - "move_folder": "移動資料夾", - "no_subfolders": "沒有子資料夾", - "rename_file": "重新命名檔案", - "select_this_folder": "選擇此資料夾", - "move_to_home": "移動到主資料夾" - }, - "dropzone": { - "drag_files": "將檔案拖到這裡,或點選選擇", - "drop_files": "釋放檔案以上傳" - }, - "permissions": { - "read": "讀取", - "write": "寫入", - "reshare": "再共享" - }, - "errors": { - "file_not_found": "檔案未找到", - "folder_not_found": "資料夾未找到", - "delete_error": "刪除時出錯", - "upload_error": "上傳檔案時出錯", - "rename_error": "重新命名時出錯", - "move_error": "移動時出錯", - "empty_name": "名稱不能為空", - "name_exists": "已存在同名檔案或資料夾", - "generic_error": "發生錯誤", - "group_name_invalid": "群組名稱必須符合電子郵件前綴格式(字母、數字、點、連字符、下劃線;1–64 個字元)。", - "group_cycle": "此成員會在群組之間形成循環參照。", - "group_depth_exceeded": "嵌套深度超過允許的最大值(8)。", - "group_virtual_immutable": "「Internal」群組由系統管理,無法修改。", - "group_not_found": "找不到群組。", - "group_name_taken": "已存在同名群組。" - }, - "breadcrumb": { - "home": "主頁" - }, - "trash": { - "empty_trash": "清空回收站", - "empty_state": "回收站為空", - "original_location": "原始位置", - "deleted_date": "刪除日期", - "remaining": "剩餘", - "actions": "操作", - "restore": "恢復", - "delete_permanently": "永久刪除", - "empty_confirm": "你確定要清空回收站嗎?這將永久刪除所有專案。", - "groupby": { - "remaining_days": "剩餘天數", - "trashed_time": "刪除時間" - } - }, - "daysRemaining": { - "expired": "已過期", - "today": "今天", - "tomorrow": "明天", - "inDays": "{{count}} 天" - }, - "expiryChip": { - "never": "永不過期", - "expired": "已過期", - "today": "今天到期", - "tomorrow": "明天到期", - "inDays": "{{count}} 天後到期", - "onDate": "於 {{date}} 到期" - }, - "auth": { - "login_title": "登入", - "username": "使用者名稱", - "username_placeholder": "輸入你的使用者名稱", - "login_identifier": "使用者名稱或電子郵件", - "login_identifier_placeholder": "請輸入使用者名稱或電子郵件", - "password": "密碼", - "password_placeholder": "輸入你的密碼", - "login_button": "登入", - "no_account": "沒有賬號?", - "register": "註冊", - "admin_setup": "首次使用?", - "setup": "設定管理員", - "register_title": "建立賬號", - "email": "電子郵件", - "email_placeholder": "輸入你的電子郵件", - "confirm_password": "確認密碼", - "confirm_password_placeholder": "確認你的密碼", - "register_button": "建立賬號", - "have_account": "已有賬號?", - "login": "登入", - "setup_title": "初始設定", - "setup_step1": "管理員", - "setup_step2": "系統", - "setup_step3": "完成", - "admin_username": "管理員使用者名稱", - "admin_email": "管理員電子郵件", - "admin_password": "管理員密碼", - "create_admin": "建立管理員", - "back_to_login": "已設定完成?", - "admin_success": "管理員賬號建立成功!您現在可以登入。", - "account_success": "賬號建立成功!您現在可以登入。", - "passwords_mismatch": "密碼不匹配", - "admin_create_error": "建立管理員賬號時出錯", - "or": "或", - "sso_login": "使用 SSO 登入", - "sso_login_provider": "使用 {{provider}} 登入", - "magicLinkHint": "沒有密碼?輸入您的電子郵件,我們將向您發送一次性登入連結。", - "magicLinkEmailLabel": "電子郵件地址", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "傳送登入連結", - "magicLinkSent": "若該電子郵件存在帳號,登入連結已發送。請查收您的收件匣。", - "magicLinkUnavailable": "此伺服器不支援電子郵件登入。", - "magicLinkNetworkError": "無法連線到伺服器:{{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "儲存空間", - "calculating": "計算中...", - "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "無法預覽此檔案型別。", - "download_file": "下載檔案", - "zoom_in": "放大", - "zoom_out": "縮小", - "zoom_reset": "重置縮放" - }, - "language_selector": { - "title": "歡迎!", - "subtitle": "選擇您的語言以繼續", - "continue": "繼續", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "還沒有收藏", - "empty_hint": "為檔案或資料夾新增星標以將其新增到收藏夾", - "add": "新增到收藏夾", - "remove": "從收藏夾移除", - "added_title": "已新增到收藏", - "added_msg": "已新增到收藏", - "removed_title": "已從收藏移除", - "removed_msg": "已從收藏移除" - }, - "recent": { - "title": "最近", - "clear": "清除最近", - "accessed": "訪問於", - "empty_state": "沒有最近檔案", - "empty_hint": "您開啟的檔案將顯示在這裡", - "loadMore": "載入更多" - }, - "batch": { - "one_selected": "已選擇 1 個專案", - "n_selected": "已選擇 {{count}} 個專案", - "confirm_delete": "確定要將 {{count}} 個專案移至回收站嗎?", - "move_title": "移動 {{count}} 個專案", - "add_favorites": "新增到收藏夾", - "move_copy": "移動或複製" - }, - "admin": { - "page_title": "管理面板", - "back_to_app": "返回 OxiCloud", - "loading": "載入中…", - "access_denied": "拒絕訪問", - "access_denied_desc": "需要管理員許可權。", - "sign_in": "登入", - "tab_dashboard": "儀表盤", - "tab_users": "使用者", - "tab_oidc": "SSO / OIDC", - "total_users": "使用者總數", - "active_users": "活躍使用者", - "admins": "管理員", - "version": "版本", - "storage_overview": "儲存概覽", - "used": "已使用", - "total_quota": "總配額", - "usage_pct": "使用率", - "users_over_80": "超過80%配額", - "users_over_quota": "超過配額", - "system": "系統", - "auth_label": "認證", - "oidc_label": "OIDC", - "quotas_label": "配額", - "enabled": "已啟用", - "disabled": "已禁用", - "active": "活躍", - "off": "關閉", - "allow_registration": "允許公開自助註冊", - "registration_warning": "公開註冊已禁用。只有管理員可以建立新使用者。", - "user_management": "使用者管理", - "create_user": "建立使用者", - "col_user": "使用者", - "col_role": "角色", - "col_auth": "認證", - "col_status": "狀態", - "col_storage": "儲存", - "col_last_login": "最後登入", - "col_actions": "操作", - "loading_users": "正在載入使用者…", - "failed_load_users": "載入失敗", - "no_users_found": "未找到使用者", - "showing_users": "顯示 {{from}}-{{to}} / {{total}}", - "prev": "上一頁", - "next": "下一頁", - "inactive": "未啟用", - "you_badge": "(你)", - "local": "本地", - "never": "從未", - "just_now": "剛剛", - "minutes_ago": "{{n}}分鐘前", - "hours_ago": "{{n}}小時前", - "days_ago": "{{n}}天前", - "edit_quota_title": "編輯配額", - "reset_password_title": "重置密碼", - "toggle_role_title": "切換角色", - "deactivate_title": "停用", - "activate_title": "啟用", - "delete_title": "刪除", - "sso_title": "單點登入 (OIDC / SSO)", - "enable_sso": "啟用 SSO 認證", - "provider_name": "提供商名稱", - "issuer_url": "發行者 URL", - "issuer_url_hint": "您的身份提供商的 OpenID Connect 發行者 URL", - "auto_discover": "自動發現", - "discovering": "發現中…", - "client_id": "客戶端 ID", - "client_secret": "客戶端金鑰", - "client_secret_placeholder": "留空以保留當前值", - "secret_configured": "已配置客戶端金鑰", - "callback_url": "回撥 URL", - "callback_url_hint": "(在您的 IdP 中註冊)", - "advanced_settings": "高階設定", - "scopes": "範圍", - "auto_provision": "首次登入時自動配置使用者", - "admin_groups": "管理組", - "admin_groups_hint": "對映到管理員角色的逗號分隔 OIDC 組名", - "disable_password": "禁用密碼登入 (僅 OIDC)", - "password_warning": "這將阻止所有基於密碼的登入!", - "test_btn": "測試", - "save_btn": "儲存", - "saving": "儲存中…", - "settings_saved": "設定已儲存 — OIDC 現在 {{status}}", - "quota_modal_title": "更新儲存配額", - "quota_user_label": "使用者:", - "new_quota": "新配額", - "quota_unlimited_hint": "0表示無限制", - "cancel": "取消", - "create_user_title": "建立新使用者", - "username_label": "使用者名稱", - "username_placeholder": "zhangsan", - "username_hint": "3–32個字元", - "password_label": "密碼", - "password_placeholder": "至少8個字元", - "email_label": "郵箱", - "email_optional": "(可選)", - "email_placeholder": "user@example.com (留空自動生成)", - "role_label": "角色", - "role_user": "使用者", - "role_admin": "管理員", - "quota_label": "配額", - "creating": "建立中…", - "reset_pw_title": "重置密碼", - "new_password_label": "新密碼", - "resetting": "重置中…", - "reset_btn": "重置", - "confirm_role_change": "將角色更改為 {{role}}?", - "confirm_deactivate": "確定要停用此使用者嗎?", - "confirm_activate": "確定要啟用此使用者嗎?", - "confirm_delete_user": "刪除使用者 \"{{name}}\"?此操作無法撤消!", - "confirm_action": "確認操作", - "confirm_yes": "確認", - "confirm_no": "取消", - "error_username_short": "使用者名稱至少需要3個字元", - "error_password_short": "密碼至少需要8個字元", - "error_generic": "失敗", - "error_network": "網路錯誤:{{message}}", - "error_create_user": "建立使用者失敗", - "tab_storage": "儲存", - "storage_title": "儲存配置", - "storage_current_backend": "當前後端", - "storage_total_blobs": "總塊數", - "storage_total_size": "總大小", - "storage_dedup_ratio": "去重比率", - "storage_backend": "後端", - "storage_local": "本地", - "storage_s3": "S3 相容", - "storage_provider_preset": "提供商預設", - "storage_preset_custom": "自定義", - "storage_endpoint_url": "端點 URL", - "storage_endpoint_hint": "AWS S3 請留空", - "storage_bucket": "儲存桶", - "storage_region": "地區", - "storage_access_key": "訪問金鑰", - "storage_secret_key": "金鑰", - "storage_secret_configured": "金鑰已配置", - "storage_key_placeholder": "輸入新金鑰", - "storage_path_style": "強制路徑風格", - "storage_path_style_hint": "MinIO 及某些 S3 相容服務需要此選項", - "storage_test_connection": "測試連線", - "storage_test_success": "連線成功", - "storage_test_failure": "連線失敗", - "storage_save": "儲存配置", - "storage_saved": "配置已儲存", - "storage_migration": "資料遷移", - "storage_migration_coming_soon": "遷移工具即將推出", - "migration_status_label": "遷移狀態", - "migration_start": "開始遷移", - "migration_pause": "暫停", - "migration_resume": "繼續", - "migration_verify": "驗證", - "migration_complete": "完成", - "migration_started": "遷移已開始", - "migration_paused_msg": "遷移已暫停", - "migration_resumed_msg": "遷移已繼續", - "migration_completed_msg": "遷移成功完成", - "migration_verifying": "正在驗證...", - "migration_verify_passed": "驗證透過", - "migration_verify_failed": "驗證失敗", - "migration_failed_blobs": "失敗的塊", - "testing": "正在測試...", - "smtp_disabled": "已停用(未設定主機)", - "smtp_enabled": "已啟用", - "smtp_enabled_label": "狀態", - "smtp_intro": "SMTP 僅透過環境變數(OXICLOUD_SMTP_*)設定。下方數值是從運行中的伺服器讀取的 — 如需修改,請編輯環境變數並重新啟動 OxiCloud。", - "smtp_not_configured": "此伺服器未設定 SMTP。", - "smtp_send_failed": "傳送失敗。", - "smtp_send_test": "傳送測試郵件", - "smtp_sending": "傳送中…", - "smtp_sent": "測試郵件已傳送。", - "smtp_server_code": "伺服器回應", - "smtp_test_intro": "向下方收件者傳送預設的診斷訊息,並回報 SMTP 伺服器的回應,以便您與轉發日誌進行對照。", - "smtp_test_missing_to": "請輸入收件者地址。", - "smtp_test_title": "傳送測試郵件", - "smtp_test_to": "收件者地址", - "smtp_title": "外寄郵件 (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "個人資料", - "back_to_app": "返回 OxiCloud", - "loading": "載入中…", - "not_authenticated": "未認證", - "not_authenticated_desc": "請登入以檢視您的個人資料。", - "sign_in": "登入", - "role_admin": "管理員", - "role_user": "使用者", - "account_details": "賬戶詳情", - "username": "使用者名稱", - "email": "郵箱", - "role": "角色", - "last_login": "最後登入", - "storage": "儲存", - "used": "已使用", - "quota": "配額", - "usage": "使用率", - "unlimited": "無限制", - "app_passwords": "應用密碼", - "app_pw_desc": "為 WebDAV、CalDAV 和 CardDAV 客戶端生成密碼。每個密碼只顯示一次。", - "app_pw_label_placeholder": "標籤(如 Thunderbird、macOS)", - "generate": "生成", - "generating": "生成中…", - "new_password_for": "新密碼用於", - "copy_warning": "請立即複製此密碼,之後將無法再次檢視。", - "copy_to_clipboard": "複製到剪貼簿", - "col_label": "標籤", - "col_created": "建立時間", - "col_last_used": "最後使用", - "col_status": "狀態", - "active": "活躍", - "revoked": "已撤銷", - "revoke_title": "撤銷", - "no_app_passwords": "暫無應用密碼。", - "client_sessions": "客戶端會話", - "client_sessions_desc": "連線 Nextcloud 相容客戶端時自動生成。", - "col_client": "客戶端", - "never": "從未", - "just_now": "剛剛", - "minutes_ago": "{{n}}分鐘前", - "hours_ago": "{{n}}小時前", - "days_ago": "{{n}}天前", - "edit_profile": "編輯個人資料", - "edit_oidc_managed": "要更改您的資訊(姓名、名字、頭像等),請前往您的身分提供者更新。變更將在您下次登入時顯示。", - "username_claim_hint": "2-64 個字元,字母 / 數字 / 點 / 短橫線 / 底線。一旦選定,使用者名稱將無法更改(DAV/NextCloud 用戶端依賴它)。", - "username_already_claimed": "使用者名稱已設定,不可更改(DAV/NextCloud 用戶端依賴它)。", - "given_name": "名", - "family_name": "姓", - "notify_on_share": "當有人與我分享時透過電子郵件通知我", - "notify_on_share_hint": "取消勾選後,分享項目仍會顯示在您的帳戶中 — 只是不會收到相關郵件通知。", - "save_profile": "儲存變更", - "profile_saved": "個人資料已更新", - "profile_no_changes": "沒有變更可儲存。", - "profile_save_failed": "儲存失敗", - "username_taken_error": "該使用者名稱已被使用。", - "username_immutable_error": "您的使用者名稱已設定,無法在此更改。如需重新命名,請聯絡管理員。", - "change_password": "修改密碼", - "current_password": "當前密碼", - "new_password": "新密碼", - "min_8_chars": "至少8個字元", - "confirm_password": "確認新密碼", - "update_password": "更新密碼", - "updating": "更新中…", - "password_updated": "密碼更新成功", - "passwords_no_match": "密碼不匹配", - "password_too_short": "密碼至少需要8個字元", - "password_change_failed": "修改密碼失敗", - "error_network": "網路錯誤:{{message}}", - "error_label_required": "請輸入標籤", - "error_create_pw": "建立應用密碼失敗", - "confirm_revoke": "撤銷應用密碼\"{{label}}\"?使用此密碼的客戶端將停止工作。", - "error_revoke": "撤銷失敗", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "notifications": { - "file_renamed": "檔案已重新命名", - "file_renamed_to": "檔案已重新命名為\"{{name}}\"", - "folder_renamed": "資料夾已重新命名", - "folder_renamed_to": "資料夾已重新命名為\"{{name}}\"", - "file_uploaded": "檔案已上傳", - "file_deleted": "檔案已移至回收站", - "folder_deleted": "資料夾已移至回收站", - "item_deleted_permanently": "專案已永久刪除", - "trash_emptied": "回收站已清空", - "title": "通知", - "empty": "暫無通知", - "link_created": "連結已建立", - "share_success": "分享連結建立成功", - "upload_files_section_title": "此處不支援上傳", - "upload_files_section_body": "請前往檔案部分上傳檔案" - }, - "upload": { - "uploading": "正在上傳...", - "files": "個檔案", - "complete": "已上傳 {{count}} / {{total}}" - }, - "storage_quota_exceeded": "儲存配額已超限", - "sharedwithme": { - "pageTitle": "與我共享", - "pageDescription": "其他使用者與您共享的檔案和資料夾", - "emptyStateTitle": "目前沒有內容與您共享", - "emptyStateDesc": "其他使用者與您共享的項目將顯示在這裡", - "loadMore": "載入更多", - "sharedBy": "共享者", - "colName": "名稱", - "colType": "類型", - "colSharedBy": "共享者", - "colDate": "共享日期", - "colPermissions": "權限" - }, - "groupby": { - "none": "無", - "title": "分組方式", - "owner": "擁有者", - "shareDate": "分享日期", - "type": "類型", - "type.folders": "資料夾", - "accessedAt": "存取日期", - "modifiedAt": "修改日期", - "createdAt": "建立日期", - "size": "大小", - "favoriteDate": "收藏日期", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "新增" - }, - "dateBucket": { - "today": "今天", - "last7days": "近7天", - "last30days": "近30天" - }, - "groups": { - "title": "管理群組", - "create_button": "建立群組", - "create_dialog_title": "新群組", - "edit_dialog_title": "重新命名群組", - "name_label": "名稱", - "name_placeholder": "engineering", - "description_label": "描述(選填)", - "members_section": "成員", - "add_member_placeholder": "新增使用者或群組…", - "no_members": "尚無成員。", - "remove_member": "移除", - "delete_group": "刪除群組", - "delete_confirm": "刪除群組「{name}」?引用此群組的所有授權將被撤銷。", - "empty_state": "尚無群組。", - "load_more": "載入更多", - "back_to_list": "返回", - "loading": "載入中…", - "virtual_badge": "系統", - "member_count_zero": "無成員", - "member_count_one": "1 個成員", - "member_count_other": "{count} 個成員", - "delete_confirm_label": "請輸入群組名稱以確認:", - "delete_confirm_mismatch": "請準確輸入群組名稱以確認。", - "virtual_internal_name": "內部", - "members_loading": "正在載入成員…", - "members_empty": "無成員", - "virtual_internal_explanation": "本伺服器上的所有內部使用者" - }, - "myshares": { - "copyLink": "複製連結", - "deleteLink": "刪除連結", - "notifyByEmail": "透過郵件通知", - "notifyFailed": "無法傳送通知。", - "notifyGroupMembers": "通知群組成員", - "notifyRateLimited": "對此收件者的通知過多 — 請稍後重試。", - "removeAccess": "移除存取權限", - "resendInvitation": "重新傳送邀請郵件" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} diff --git a/static/locales/zh.json b/static/locales/zh.json deleted file mode 100644 index a4af3ee3..00000000 --- a/static/locales/zh.json +++ /dev/null @@ -1,980 +0,0 @@ -{ - "server": { - "magic_link": { - "page": { - "expired_title": "此登录链接已不再有效", - "expired_body": "链接可能已过期或已被使用。我们可以为您发送一个新的 — 几秒钟内它将到达您的收件箱。", - "resend_to": "发送新链接至 {{email}}", - "generic_unavailable": "此登录链接已不再有效。它可能已被使用或已过期。请在登录页面请求新链接。", - "service_unavailable": "此服务器未启用魔法链接登录。", - "internal_error": "登录时出错。请重试。", - "resend_failure": "发送链接时出错。请重试。", - "cross_browser_title": "在此设备上继续登录?", - "cross_browser_body": "您在与请求时不同的浏览器或设备上打开了此登录链接。", - "cross_browser_warning": "如果是您请求了此链接,可以安全继续。否则,请关闭此页面 — 点击「继续」将使其他人登录您的账户。", - "cross_browser_continue": "继续并登录", - "resend_confirmation_title": "请检查您的收件箱", - "resend_confirmation_body": "如果登录链接属于活跃账户,新链接刚刚已发送。请检查您的收件箱。", - "return_link": "返回 OxiCloud" - }, - "email": { - "invitation": { - "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n点击下方链接打开:\n{{link}}\n\n该链接仅可使用一次,并将在 {{ttl_hours}} 小时后过期。\n如果您未预期收到此邀请,可以忽略此消息。\n\n— OxiCloud" - }, - "login": { - "subject": "登录 OxiCloud", - "body": "您好,\n\n使用下方链接登录 OxiCloud。该链接仅可使用一次,并将在 {{ttl_minutes}} 分钟后过期。请在请求时使用的同一设备上打开。\n\n{{link}}\n\n如果您未请求此登录链接,可以忽略此消息 — 无需进一步操作。\n\n— OxiCloud" - }, - "kind_file": "文件", - "kind_folder": "文件夹", - "english_fallback_divider": "--- 以下为英文版本 ---" - } - }, - "notification": { - "share": { - "subject": "{{inviter}} 在 OxiCloud 上与您共享了一个{{kind}}", - "body": "{{inviter_full}} 在 OxiCloud 上与您共享了一个{{kind}}。\n\n打开 OxiCloud 查看您的新共享:\n{{login_link}}\n\n您可能还有来自 {{inviter}} 的其他新共享 — 登录以查看所有共享给您的项目。\n\n— OxiCloud\n\n您收到此消息是因为您拥有 OxiCloud 账户且共享通知偏好已开启。您可以在个人资料中关闭它(当有人与我共享时通过电子邮件通知我)。" - } - } - }, - "app": { - "title": "OxiCloud", - "description": "极简云存储系统" - }, - "nav": { - "files": "文件", - "shared": "共享", - "recent": "最近", - "favorites": "收藏", - "photos": "照片", - "music": "音乐", - "trash": "回收站", - "sharedwithme": "与我共享" - }, - "photos": { - "empty_state": "还没有照片", - "empty_hint": "上传图片或视频即可在此查看", - "items_selected": "已选择", - "view_daily": "日", - "view_monthly": "月", - "view_yearly": "年" - }, - "music": { - "create_playlist": "创建播放列表", - "playlists": "播放列表", - "no_playlists": "还没有播放列表", - "select_playlist": "选择一个播放列表", - "select_hint": "从侧边栏选择播放列表或创建新播放列表", - "add_tracks": "添加曲目", - "no_tracks": "此播放列表中没有曲目", - "unknown_artist": "未知艺术家", - "unknown_title": "未知", - "confirm_delete": "删除此播放列表?", - "playlist_name": "播放列表名称", - "create": "创建", - "delete": "删除", - "share": "分享", - "edit": "编辑", - "play_all": "全部播放", - "shuffle": "随机播放", - "repeat": "重复", - "repeat_one": "单曲循环", - "queue": "播放队列", - "queue_empty": "播放队列为空", - "not_playing": "未播放", - "play": "播放", - "pause": "暂停", - "previous": "上一首", - "next": "下一首", - "volume": "音量", - "mute": "静音", - "unmute": "取消静音", - "title": "标题", - "artist": "艺术家", - "album": "专辑", - "tracks": "首曲目", - "add": "添加", - "added": "已添加!", - "added_to_playlist": "已添加到播放列表", - "add_to_playlist": "添加到播放列表", - "load_error": "加载播放列表出错", - "add_error": "无法将曲目添加到播放列表", - "no_playlists_yet": "暂无播放列表。请先创建一个!", - "selected_files": "已选择:", - "error": "错误", - "search_audio": "搜索音频文件…", - "no_audio_files": "未找到音频文件", - "selected": "已选择", - "loading": "加载中…", - "search_error": "无法加载音频文件", - "adding": "添加中…", - "can_write": "Can edit", - "cover_updated": "Cover updated", - "empty_hint": "Create your first playlist to start organizing your music", - "make_private": "Make private", - "make_public": "Make public", - "manage_shares": "Manage Shares", - "no_shares": "No shares yet", - "playback_error": "Playback failed", - "private": "Private", - "public": "Public", - "read_only": "Read only", - "remove": "Remove", - "remove_share": "Remove share", - "set_cover": "Set cover", - "share_with_user": "User ID or email", - "toggle_public": "Visibility", - "track_removed": "Track removed" - }, - "actions": { - "search": "搜索文件...", - "new_folder": "新建文件夹", - "upload": "上传", - "upload_files": "上传文件", - "upload_folder": "上传文件夹", - "upload.uploading": "上传中...", - "upload.complete": "{count} / {total} 已上传", - "upload.files": "文件", - "rename": "重命名", - "move": "移动到...", - "move_to": "移动到", - "delete": "删除", - "download": "下载", - "view": "查看", - "cancel": "取消", - "confirm": "确认", - "share": "共享", - "favorite": "添加到收藏", - "unfavorite": "取消收藏", - "copy": "复制", - "notify": "通知", - "send": "发送", - "clear_recent": "清除最近", - "logout": "退出登录", - "create": "创建", - "search_btn": "搜索", - "close": "关闭", - "delete_permanently": "Delete permanently", - "empty_trash": "Empty trash", - "open_parent_folder": "转到父文件夹", - "add": "Add", - "apply": "Apply", - "clear": "Clear", - "remove": "Remove" - }, - "user_menu": { - "appearance": "外观", - "about": "关于 OxiCloud", - "about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。", - "admin_panel": "管理面板", - "profile": "我的资料", - "role_user": "用户", - "theme": { - "light": "浅色", - "dark": "深色", - "auto": "跟随系统" - }, - "manage_groups": "管理群组" - }, - "share": { - "dialogTitle": "共享链接", - "linkLabel": "共享链接:", - "copyLink": "复制", - "permissions": "权限:", - "permissionRead": "读取", - "permissionWrite": "写入", - "permissionReshare": "再共享", - "password": "密码保护:", - "generatePassword": "生成", - "expiration": "过期日期:", - "update": "更新共享", - "remove": "移除共享", - "notifyTitle": "发送通知", - "notifyEmailLabel": "电子邮件地址:", - "notifyMessageLabel": "消息(可选):", - "notifySend": "发送通知", - "shareWithOthers": "与他人共享", - "sharePublicly": "公开共享", - "shareSettings": "共享设置", - "shareCopied": "链接已复制到剪贴板", - "shareCreated": "共享链接创建成功", - "shareUpdated": "共享设置更新成功", - "shareRemoved": "共享已移除", - "inviteByEmail": "通过邮件邀请 — 将发送邀请", - "directoryUnavailable": "User directory unavailable", - "linkNamePlaceholder": "Link name (optional)", - "newLink": "New link", - "noExpiry": "No expiry", - "pending": "Pending", - "people": "People", - "publicLinks": "Public links", - "role": { - "canEdit": "Can edit", - "canManage": "Can manage", - "canView": "Can view" - }, - "searchPlaceholder": "Search people…", - "shareOf": "Share of:", - "sharedLink": "Shared link" - }, - "share_dialogTitle": "共享链接", - "share_linkLabel": "共享链接:", - "share_copyLink": "复制", - "share_permissions": "权限:", - "share_permissionRead": "读取", - "share_permissionWrite": "写入", - "share_permissionReshare": "再共享", - "share_password": "密码保护:", - "share_generatePassword": "生成", - "share_expiration": "过期日期:", - "share_update": "更新共享", - "share_remove": "移除共享", - "share_notifyTitle": "发送通知", - "share_notifyEmailLabel": "电子邮件地址:", - "share_notifyMessageLabel": "消息(可选):", - "share_notifySend": "发送通知", - "shared": { - "backToFiles": "返回文件", - "pageTitle": "共享资源", - "pageDescription": "管理你的共享文件和文件夹", - "filterType": "类型:", - "filterAll": "全部", - "filterFiles": "文件", - "filterFolders": "文件夹", - "sortBy": "排序依据:", - "sortByName": "名称", - "sortByDate": "共享日期", - "sortByExpiration": "过期日期", - "search": "搜索", - "colName": "名称", - "colType": "类型", - "colDateShared": "共享日期", - "colExpiration": "过期日期", - "colPermissions": "权限", - "colPassword": "密码", - "colActions": "操作", - "emptyStateTitle": "尚未有共享资源", - "emptyStateDesc": "当你共享文件或文件夹时,它们会出现在这里", - "goToFiles": "前往文件", - "typeFile": "文件", - "typeFolder": "文件夹", - "noExpiration": "无过期", - "hasPassword": "有", - "noPassword": "无", - "editShare": "编辑共享", - "notifyShare": "通知某人", - "copyLink": "复制链接", - "removeShare": "移除共享", - "linkCopied": "链接已复制到剪贴板!", - "linkCopyFailed": "复制链接失败", - "itemUpdated": "共享设置更新成功", - "itemRemoved": "共享已移除成功", - "invalidEmail": "请输入有效的电子邮件地址", - "notificationSent": "通知已成功发送", - "notificationFailed": "发送通知失败", - "shared_backToFiles": "Back to Files", - "shared_colActions": "Actions", - "shared_colDateShared": "Date Shared", - "shared_colExpiration": "Expiration", - "shared_colName": "Name", - "shared_colPassword": "Password", - "shared_colPermissions": "Permissions", - "shared_colType": "Type", - "shared_copyLink": "Copy Link", - "shared_editShare": "Edit Share", - "shared_emptyStateDesc": "When you share files or folders, they will appear here", - "shared_emptyStateTitle": "No shared resources yet", - "shared_filterAll": "All", - "shared_filterFiles": "Files", - "shared_filterFolders": "Folders", - "shared_filterType": "Type:", - "shared_goToFiles": "Go to Files", - "shared_hasPassword": "Yes", - "shared_invalidEmail": "Please enter a valid email address", - "shared_itemRemoved": "Share removed successfully", - "shared_itemUpdated": "Share settings updated successfully", - "shared_linkCopied": "Link copied to clipboard!", - "shared_linkCopyFailed": "Failed to copy link", - "shared_noExpiration": "No expiration", - "shared_noPassword": "No", - "shared_notificationFailed": "Failed to send notification", - "shared_notificationSent": "Notification sent successfully", - "shared_notifyShare": "Notify Someone", - "shared_pageDescription": "Manage your shared files and folders", - "shared_pageTitle": "Shared Resources", - "shared_removeShare": "Remove Share", - "shared_search": "Search", - "shared_sortBy": "Sort by:", - "shared_sortByDate": "Date shared", - "shared_sortByExpiration": "Expiration", - "shared_sortByName": "Name", - "shared_typeFile": "File", - "shared_typeFolder": "Folder" - }, - "files": { - "name": "名称", - "type": "类型", - "size": "大小", - "modified": "修改日期", - "no_files": "此文件夹中没有文件", - "empty_hint": "上传文件或创建文件夹以开始使用", - "loading": "正在加载文件…", - "view_grid": "网格视图", - "view_list": "列表视图", - "file_types": { - "document": "文档", - "image": "图片", - "video": "视频", - "audio": "音频", - "pdf": "PDF", - "text": "文本", - "folder": "文件夹", - "spreadsheet": "电子表格", - "presentation": "演示文稿", - "archive": "压缩文件", - "installer": "安装程序", - "code": "代码" - }, - "owner": "所有者" - }, - "dialogs": { - "rename_folder": "重命名文件夹", - "new_name": "新名称", - "new_folder_title": "新建文件夹", - "folder_name": "文件夹名称", - "folder_placeholder": "我的文件夹", - "rename_title": "重命名", - "move_file": "移动文件", - "select_destination": "选择目标文件夹", - "root": "根目录", - "delete_confirmation": "你确定要删除", - "and_contents": "及其所有内容", - "no_undo": "此操作无法撤销", - "share_file": "共享文件", - "share_folder": "共享文件夹", - "existing_shares": "现有共享", - "share_options": "共享选项", - "password": "密码", - "expiration": "过期日期", - "permissions": "权限", - "generated_link": "生成的链接", - "notify": "发送通知", - "recipient": "收件人", - "message": "消息", - "confirm_delete": "Move to trash", - "confirm_delete_file": "Are you sure you want to move the file \"{{name}}\" to trash?", - "confirm_delete_folder": "Are you sure you want to move the folder \"{{name}}\" and all its contents to trash?", - "confirm_delete_share": "Delete share link", - "confirm_delete_share_msg": "Are you sure you want to delete this shared link?", - "confirm_empty_trash": "Empty trash", - "confirm_permanent_delete": "Delete permanently", - "confirm_permanent_delete_msg": "Are you sure you want to permanently delete this item? This action cannot be undone.", - "confirm_title": "Confirm action", - "go_to_parent": ".. (parent folder)", - "move_folder": "Move folder", - "no_subfolders": "No subfolders", - "rename_file": "Rename file", - "select_this_folder": "Select this folder", - "move_to_home": "移动到主文件夹" - }, - "dropzone": { - "drag_files": "将文件拖到这里,或点击选择", - "drop_files": "释放文件以上传" - }, - "permissions": { - "read": "读取", - "write": "写入", - "reshare": "再共享" - }, - "errors": { - "file_not_found": "文件未找到", - "folder_not_found": "文件夹未找到", - "delete_error": "删除时出错", - "upload_error": "上传文件时出错", - "rename_error": "重命名时出错", - "move_error": "移动时出错", - "empty_name": "名称不能为空", - "name_exists": "已存在同名文件或文件夹", - "generic_error": "发生错误", - "group_name_invalid": "组名必须符合邮件前缀格式(字母、数字、点、连字符、下划线;1–64个字符)。", - "group_cycle": "此成员会在组之间形成循环引用。", - "group_depth_exceeded": "嵌套深度超出允许的最大值(8)。", - "group_virtual_immutable": "“Internal”组由系统管理,无法修改。", - "group_not_found": "未找到组。", - "group_name_taken": "同名组已存在。" - }, - "breadcrumb": { - "home": "主页" - }, - "trash": { - "empty_trash": "清空回收站", - "empty_state": "回收站为空", - "original_location": "原始位置", - "deleted_date": "删除日期", - "remaining": "剩余", - "actions": "操作", - "restore": "恢复", - "delete_permanently": "永久删除", - "empty_confirm": "你确定要清空回收站吗?这将永久删除所有项目。", - "groupby": { - "remaining_days": "剩余天数", - "trashed_time": "删除时间" - } - }, - "daysRemaining": { - "expired": "已过期", - "today": "今天", - "tomorrow": "明天", - "inDays": "{{count}} 天" - }, - "expiryChip": { - "never": "永不过期", - "expired": "已过期", - "today": "今天到期", - "tomorrow": "明天到期", - "inDays": "{{count}} 天后到期", - "onDate": "于 {{date}} 到期" - }, - "auth": { - "login_title": "登录", - "username": "用户名", - "username_placeholder": "输入你的用户名", - "login_identifier": "用户名或邮箱", - "login_identifier_placeholder": "请输入用户名或邮箱", - "password": "密码", - "password_placeholder": "输入你的密码", - "login_button": "登录", - "no_account": "没有账号?", - "register": "注册", - "admin_setup": "首次使用?", - "setup": "设置管理员", - "register_title": "创建账号", - "email": "电子邮件", - "email_placeholder": "输入你的电子邮件", - "confirm_password": "确认密码", - "confirm_password_placeholder": "确认你的密码", - "register_button": "创建账号", - "have_account": "已有账号?", - "login": "登录", - "setup_title": "初始设置", - "setup_step1": "管理员", - "setup_step2": "系统", - "setup_step3": "完成", - "admin_username": "管理员用户名", - "admin_email": "管理员电子邮件", - "admin_password": "管理员密码", - "create_admin": "创建管理员", - "back_to_login": "已设置完成?", - "admin_success": "管理员账号创建成功!您现在可以登录。", - "account_success": "账号创建成功!您现在可以登录。", - "passwords_mismatch": "密码不匹配", - "admin_create_error": "创建管理员账号时出错", - "or": "或", - "sso_login": "使用 SSO 登录", - "sso_login_provider": "使用 {{provider}} 登录", - "magicLinkHint": "没有密码?输入您的邮箱,我们将向您发送一次性登录链接。", - "magicLinkEmailLabel": "邮箱地址", - "magicLinkEmailPlaceholder": "you@example.com", - "magicLinkSubmit": "发送登录链接", - "magicLinkSent": "如果该邮箱存在账户,登录链接已发送。请查收您的收件箱。", - "magicLinkUnavailable": "此服务器不支持邮箱登录。", - "magicLinkNetworkError": "无法连接到服务器:{{message}}", - "magicLinkToggle": "No password? Email me a sign-in link", - "passwordsMatch": "Passwords match", - "capsLock": "Caps Lock is on" - }, - "storage": { - "title": "存储空间", - "calculating": "计算中...", - "used": "{{percentage}}% 已使用 ({{used}} / {{total}})" - }, - "viewer": { - "unsupported_file": "无法预览此文件类型。", - "download_file": "下载文件", - "zoom_in": "放大", - "zoom_out": "缩小", - "zoom_reset": "重置缩放" - }, - "language_selector": { - "title": "欢迎!", - "subtitle": "选择您的语言以继续", - "continue": "继续", - "languages": { - "en": "English", - "es": "Español", - "zh": "中文", - "fa": "فارسی", - "fr": "Français", - "de": "Deutsch", - "pt": "Português", - "ar": "العربية", - "hi": "हिन्दी", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "nl": "Nederlands", - "ru": "Русский" - } - }, - "favorites": { - "empty_state": "还没有收藏", - "empty_hint": "为文件或文件夹添加星标以将其添加到收藏夹", - "add": "添加到收藏夹", - "remove": "从收藏夹移除", - "added_title": "已添加到收藏", - "added_msg": "已添加到收藏", - "removed_title": "已从收藏移除", - "removed_msg": "已从收藏移除" - }, - "recent": { - "title": "最近", - "clear": "清除最近", - "accessed": "访问于", - "empty_state": "没有最近文件", - "empty_hint": "您打开的文件将显示在这里", - "loadMore": "加载更多" - }, - "batch": { - "one_selected": "已选择 1 个项目", - "n_selected": "已选择 {{count}} 个项目", - "confirm_delete": "确定要将 {{count}} 个项目移至回收站吗?", - "move_title": "移动 {{count}} 个项目", - "add_favorites": "添加到收藏夹", - "move_copy": "移动或复制" - }, - "admin": { - "page_title": "管理面板", - "back_to_app": "返回 OxiCloud", - "loading": "加载中…", - "access_denied": "拒绝访问", - "access_denied_desc": "需要管理员权限。", - "sign_in": "登录", - "tab_dashboard": "仪表盘", - "tab_users": "用户", - "tab_oidc": "SSO / OIDC", - "total_users": "用户总数", - "active_users": "活跃用户", - "admins": "管理员", - "version": "版本", - "storage_overview": "存储概览", - "used": "已使用", - "total_quota": "总配额", - "usage_pct": "使用率", - "users_over_80": "超过80%配额", - "users_over_quota": "超过配额", - "system": "系统", - "auth_label": "认证", - "oidc_label": "OIDC", - "quotas_label": "配额", - "enabled": "已启用", - "disabled": "已禁用", - "active": "活跃", - "off": "关闭", - "allow_registration": "允许公开自助注册", - "registration_warning": "公开注册已禁用。只有管理员可以创建新用户。", - "user_management": "用户管理", - "create_user": "创建用户", - "col_user": "用户", - "col_role": "角色", - "col_auth": "认证", - "col_status": "状态", - "col_storage": "存储", - "col_last_login": "最后登录", - "col_actions": "操作", - "loading_users": "正在加载用户…", - "failed_load_users": "加载失败", - "no_users_found": "未找到用户", - "showing_users": "显示 {{from}}-{{to}} / {{total}}", - "prev": "上一页", - "next": "下一页", - "inactive": "未激活", - "you_badge": "(你)", - "local": "本地", - "never": "从未", - "just_now": "刚刚", - "minutes_ago": "{{n}}分钟前", - "hours_ago": "{{n}}小时前", - "days_ago": "{{n}}天前", - "edit_quota_title": "编辑配额", - "reset_password_title": "重置密码", - "toggle_role_title": "切换角色", - "deactivate_title": "停用", - "activate_title": "启用", - "delete_title": "删除", - "sso_title": "单点登录 (OIDC / SSO)", - "enable_sso": "启用 SSO 认证", - "provider_name": "提供商名称", - "issuer_url": "发行者 URL", - "issuer_url_hint": "您的身份提供商的 OpenID Connect 发行者 URL", - "auto_discover": "自动发现", - "discovering": "发现中…", - "client_id": "客户端 ID", - "client_secret": "客户端密钥", - "client_secret_placeholder": "留空以保留当前值", - "secret_configured": "已配置客户端密钥", - "callback_url": "回调 URL", - "callback_url_hint": "(在您的 IdP 中注册)", - "advanced_settings": "高级设置", - "scopes": "范围", - "auto_provision": "首次登录时自动配置用户", - "admin_groups": "管理组", - "admin_groups_hint": "映射到管理员角色的逗号分隔 OIDC 组名", - "disable_password": "禁用密码登录 (仅 OIDC)", - "password_warning": "这将阻止所有基于密码的登录!", - "test_btn": "测试", - "save_btn": "保存", - "saving": "保存中…", - "settings_saved": "设置已保存 — OIDC 现在 {{status}}", - "quota_modal_title": "更新存储配额", - "quota_user_label": "用户:", - "new_quota": "新配额", - "quota_unlimited_hint": "0表示无限制", - "cancel": "取消", - "create_user_title": "创建新用户", - "username_label": "用户名", - "username_placeholder": "zhangsan", - "username_hint": "3–32个字符", - "password_label": "密码", - "password_placeholder": "至少8个字符", - "email_label": "邮箱", - "email_optional": "(可选)", - "email_placeholder": "user@example.com (留空自动生成)", - "role_label": "角色", - "role_user": "用户", - "role_admin": "管理员", - "quota_label": "配额", - "creating": "创建中…", - "reset_pw_title": "重置密码", - "new_password_label": "新密码", - "resetting": "重置中…", - "reset_btn": "重置", - "confirm_role_change": "将角色更改为 {{role}}?", - "confirm_deactivate": "确定要停用此用户吗?", - "confirm_activate": "确定要启用此用户吗?", - "confirm_delete_user": "删除用户 \"{{name}}\"?此操作无法撤消!", - "confirm_action": "确认操作", - "confirm_yes": "确认", - "confirm_no": "取消", - "error_username_short": "用户名至少需要3个字符", - "error_password_short": "密码至少需要8个字符", - "error_generic": "失败", - "error_network": "网络错误:{{message}}", - "error_create_user": "创建用户失败", - "tab_storage": "存储", - "storage_title": "存储配置", - "storage_current_backend": "当前后端", - "storage_total_blobs": "总块数", - "storage_total_size": "总大小", - "storage_dedup_ratio": "去重比率", - "storage_backend": "后端", - "storage_local": "本地", - "storage_s3": "S3 兼容", - "storage_provider_preset": "提供商预设", - "storage_preset_custom": "自定义", - "storage_endpoint_url": "端点 URL", - "storage_endpoint_hint": "AWS S3 请留空", - "storage_bucket": "存储桶", - "storage_region": "地区", - "storage_access_key": "访问密钥", - "storage_secret_key": "密钥", - "storage_secret_configured": "密钥已配置", - "storage_key_placeholder": "输入新密钥", - "storage_path_style": "强制路径风格", - "storage_path_style_hint": "MinIO 及某些 S3 兼容服务需要此选项", - "storage_test_connection": "测试连接", - "storage_test_success": "连接成功", - "storage_test_failure": "连接失败", - "storage_save": "保存配置", - "storage_saved": "配置已保存", - "storage_migration": "数据迁移", - "storage_migration_coming_soon": "迁移工具即将推出", - "migration_status_label": "迁移状态", - "migration_start": "开始迁移", - "migration_pause": "暂停", - "migration_resume": "继续", - "migration_verify": "验证", - "migration_complete": "完成", - "migration_started": "迁移已开始", - "migration_paused_msg": "迁移已暂停", - "migration_resumed_msg": "迁移已继续", - "migration_completed_msg": "迁移成功完成", - "migration_verifying": "正在验证...", - "migration_verify_passed": "验证通过", - "migration_verify_failed": "验证失败", - "migration_failed_blobs": "失败的块", - "testing": "正在测试...", - "smtp_disabled": "已禁用(未设置主机)", - "smtp_enabled": "已启用", - "smtp_enabled_label": "状态", - "smtp_intro": "SMTP 仅通过环境变量(OXICLOUD_SMTP_*)配置。以下值是从运行中的服务器读取的 — 如需修改,请编辑环境变量并重启 OxiCloud。", - "smtp_not_configured": "此服务器未配置 SMTP。", - "smtp_send_failed": "发送失败。", - "smtp_send_test": "发送测试邮件", - "smtp_sending": "发送中…", - "smtp_sent": "测试邮件已发送。", - "smtp_server_code": "服务器回复", - "smtp_test_intro": "向下方收件人发送预设的诊断消息,并报告 SMTP 服务器的响应,以便您与中继日志进行核对。", - "smtp_test_missing_to": "请输入收件人地址。", - "smtp_test_title": "发送测试邮件", - "smtp_test_to": "收件人地址", - "smtp_title": "出站邮件 (SMTP)", - "tab_smtp": "SMTP" - }, - "profile": { - "page_title": "个人资料", - "back_to_app": "返回 OxiCloud", - "loading": "加载中…", - "not_authenticated": "未认证", - "not_authenticated_desc": "请登录以查看您的个人资料。", - "sign_in": "登录", - "role_admin": "管理员", - "role_user": "用户", - "account_details": "账户详情", - "username": "用户名", - "email": "邮箱", - "role": "角色", - "last_login": "最后登录", - "storage": "存储", - "used": "已使用", - "quota": "配额", - "usage": "使用率", - "unlimited": "无限制", - "app_passwords": "应用密码", - "app_pw_desc": "为 WebDAV、CalDAV 和 CardDAV 客户端生成密码。每个密码只显示一次。", - "app_pw_label_placeholder": "标签(如 Thunderbird、macOS)", - "generate": "生成", - "generating": "生成中…", - "new_password_for": "新密码用于", - "copy_warning": "请立即复制此密码,之后将无法再次查看。", - "copy_to_clipboard": "复制到剪贴板", - "col_label": "标签", - "col_created": "创建时间", - "col_last_used": "最后使用", - "col_status": "状态", - "active": "活跃", - "revoked": "已撤销", - "revoke_title": "撤销", - "no_app_passwords": "暂无应用密码。", - "client_sessions": "客户端会话", - "client_sessions_desc": "连接 Nextcloud 兼容客户端时自动生成。", - "col_client": "客户端", - "never": "从未", - "just_now": "刚刚", - "minutes_ago": "{{n}}分钟前", - "hours_ago": "{{n}}小时前", - "days_ago": "{{n}}天前", - "edit_profile": "编辑个人资料", - "edit_oidc_managed": "要更改您的信息(姓名、名字、头像等),请前往您的身份提供商更新。变更将在您下次登录时显示。", - "username_claim_hint": "2-64 个字符,字母 / 数字 / 点 / 短横线 / 下划线。一旦选定,用户名将无法更改(DAV/NextCloud 客户端依赖它)。", - "username_already_claimed": "用户名已设置,不可更改(DAV/NextCloud 客户端依赖它)。", - "given_name": "名", - "family_name": "姓", - "notify_on_share": "当有人与我共享时通过电子邮件通知我", - "notify_on_share_hint": "取消勾选后,共享项目仍会显示在您的账户中 — 只是不会收到相关邮件通知。", - "save_profile": "保存更改", - "profile_saved": "个人资料已更新", - "profile_no_changes": "无更改可保存。", - "profile_save_failed": "保存失败", - "username_taken_error": "该用户名已被占用。", - "username_immutable_error": "您的用户名已设置,无法在此更改。如需重命名,请联系管理员。", - "change_password": "修改密码", - "current_password": "当前密码", - "new_password": "新密码", - "min_8_chars": "至少8个字符", - "confirm_password": "确认新密码", - "update_password": "更新密码", - "updating": "更新中…", - "password_updated": "密码更新成功", - "passwords_no_match": "密码不匹配", - "password_too_short": "密码至少需要8个字符", - "password_change_failed": "修改密码失败", - "error_network": "网络错误:{{message}}", - "error_label_required": "请输入标签", - "error_create_pw": "创建应用密码失败", - "confirm_revoke": "撤销应用密码\"{{label}}\"?使用此密码的客户端将停止工作。", - "error_revoke": "撤销失败", - "edit_photo": "Edit photo", - "photo_tab_url": "URL", - "photo_tab_upload": "Upload", - "photo_url_placeholder": "https://example.com/photo.jpg", - "photo_url_hint": "https://, http://, or data:image/…;base64,… accepted", - "photo_choose_file": "Choose a photo (PNG, JPEG, WebP)", - "photo_resize_note": "Images larger than 512 × 512 px are automatically resized.", - "photo_save": "Save photo", - "photo_remove": "Remove photo", - "photo_cancel": "Cancel", - "photo_save_failed": "Failed to save photo", - "photo_no_file": "Please select a file first", - "photo_managed_by_oidc": "Photo managed by your identity provider." - }, - "notifications": { - "file_renamed": "文件已重命名", - "file_renamed_to": "文件已重命名为\"{{name}}\"", - "folder_renamed": "文件夹已重命名", - "folder_renamed_to": "文件夹已重命名为\"{{name}}\"", - "file_uploaded": "文件已上传", - "file_deleted": "文件已移至回收站", - "folder_deleted": "文件夹已移至回收站", - "item_deleted_permanently": "项目已永久删除", - "trash_emptied": "回收站已清空", - "title": "通知", - "empty": "暂无通知", - "link_created": "链接已创建", - "share_success": "分享链接创建成功", - "upload_files_section_title": "此处不支持上传", - "upload_files_section_body": "请前往文件部分上传文件" - }, - "upload": { - "uploading": "正在上传...", - "files": "个文件", - "complete": "已上传 {{count}} / {{total}}" - }, - "storage_quota_exceeded": "存储配额已超限", - "sharedwithme": { - "pageTitle": "与我共享", - "pageDescription": "其他用户与您共享的文件和文件夹", - "emptyStateTitle": "暂无内容与您共享", - "emptyStateDesc": "其他用户与您共享的项目将显示在此处", - "loadMore": "加载更多", - "sharedBy": "共享者", - "colName": "名称", - "colType": "类型", - "colSharedBy": "共享者", - "colDate": "共享日期", - "colPermissions": "权限" - }, - "groupby": { - "none": "无", - "title": "分组方式", - "owner": "所有者", - "shareDate": "分享日期", - "type": "类型", - "type.folders": "文件夹", - "accessedAt": "访问日期", - "modifiedAt": "修改日期", - "createdAt": "创建日期", - "size": "大小", - "favoriteDate": "收藏日期", - "byFiles": "By files", - "sharedWith": "Shared with", - "justAdded": "新建" - }, - "dateBucket": { - "today": "今天", - "last7days": "近7天", - "last30days": "近30天" - }, - "groups": { - "title": "管理群组", - "create_button": "创建群组", - "create_dialog_title": "新群组", - "edit_dialog_title": "重命名群组", - "name_label": "名称", - "name_placeholder": "engineering", - "description_label": "描述(可选)", - "members_section": "成员", - "add_member_placeholder": "添加用户或群组…", - "no_members": "暂无成员。", - "remove_member": "移除", - "delete_group": "删除群组", - "delete_confirm": "删除群组 \"{name}\"?引用此群组的所有权限将被撤销。", - "empty_state": "暂无群组。", - "load_more": "加载更多", - "back_to_list": "返回", - "loading": "加载中…", - "virtual_badge": "系统", - "member_count_zero": "无成员", - "member_count_one": "1 个成员", - "member_count_other": "{count} 个成员", - "delete_confirm_label": "请输入群组名称以确认:", - "delete_confirm_mismatch": "请准确输入群组名称以确认。", - "virtual_internal_name": "内部", - "members_loading": "正在加载成员…", - "members_empty": "无成员", - "virtual_internal_explanation": "本服务器上的所有内部用户" - }, - "myshares": { - "copyLink": "复制链接", - "deleteLink": "删除链接", - "notifyByEmail": "通过邮件通知", - "notifyFailed": "无法发送通知。", - "notifyGroupMembers": "通知群组成员", - "notifyRateLimited": "对此收件人的通知过多 — 请稍后重试。", - "removeAccess": "移除访问权限", - "resendInvitation": "重新发送邀请邮件" - }, - "sort": { - "asc": "ascending", - "desc": "descending" - }, - "notif": { - "errorTitle": "Error", - "searchError": "Error performing search", - "cleanupCompleted": "Cleanup completed", - "cleanupCompletedBody": "Recent files history has been cleared", - "batchCopy": "Batch copy", - "batchCopyBody": "{{success}} copied, {{errors}} failed", - "itemsCopied": "Items copied", - "itemsCopiedBody": "{{count}} items copied successfully", - "batchMove": "Batch move", - "batchMoveBody": "{{success}} moved, {{errors}} failed", - "itemsMoved": "Items moved", - "itemsMovedBody": "{{count}} items moved successfully", - "batchDelete": "Batch delete", - "batchDeleteBody": "{{success}} moved to trash, {{errors}} failed", - "movedToTrash": "Moved to trash", - "movedToTrashBody": "{{count}} items moved to trash", - "trashItemsError": "Could not move items to trash", - "preparingDownload": "Preparing download", - "preparingDownloadBody": "Preparing your download…", - "downloadItemsError": "Could not download selected items", - "favoritesAddError": "Could not add items to favorites", - "invalidEmail": "Please enter a valid email address", - "notificationSendError": "Could not send notification", - "folderCreated": "Folder created", - "folderCreatedBody": "\"{{name}}\" created successfully", - "fileMoved": "File moved", - "fileMovedBody": "File moved successfully", - "fileMoveError": "Error moving the file: {{error}}", - "fileMoveErrorGeneric": "Error moving the file", - "folderMoved": "Folder moved", - "folderMovedBody": "Folder moved successfully", - "folderMoveError": "Error moving the folder: {{error}}", - "folderMoveErrorGeneric": "Error moving the folder", - "fileCopied": "File copied", - "fileCopiedBody": "File copied successfully", - "fileCopyError": "Error copying the file: {{error}}", - "fileCopyErrorGeneric": "Error copying the file", - "folderRenamed": "Folder renamed", - "folderRenamedBody": "Folder renamed to \"{{name}}\"", - "fileTrashed": "File moved to trash", - "fileTrashedBody": "\"{{name}}\" moved to trash", - "fileDeleted": "File deleted", - "fileDeletedBody": "\"{{name}}\" deleted successfully", - "fileDeleteError": "Error deleting the file", - "folderTrashed": "Folder moved to trash", - "folderTrashedBody": "\"{{name}}\" moved to trash", - "folderDeleted": "Folder deleted", - "folderDeletedBody": "\"{{name}}\" deleted successfully", - "folderDeleteError": "Error deleting the folder", - "itemRestored": "Item restored", - "itemRestoredBody": "Item restored successfully", - "itemRestoreError": "Error restoring the item", - "itemDeleted": "Item deleted", - "itemDeletedBody": "Item permanently deleted", - "itemDeleteError": "Error deleting the item", - "trashEmptied": "Trash emptied", - "trashEmptiedBody": "The trash has been emptied successfully", - "trashEmptyError": "Error emptying the trash", - "cacheCleared": "Cache cleared", - "cacheClearedBody": "Search cache cleared successfully", - "cacheClearError": "Error clearing search cache", - "wopiOpenError": "Could not open the document editor.", - "linkCopied": "Link copied", - "linkCopiedBody": "Link copied to clipboard", - "linkCopyError": "Could not copy link", - "notificationSent": "Notification sent", - "notificationSentBody": "Notification sent to {{email}}" - } -} From 5494efea35f835ca20a2e6c56aee910c8f4339ec Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 12:59:16 +0000 Subject: [PATCH 5/6] feat(frontend): port Places, People & photo tabs to the Svelte app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring the Photos/People/Places UI that main added (in the legacy vanilla frontend) into the SvelteKit rewrite, wired to the now-merged backend (/api/photos/geo, /api/people/*). Photos page (routes/photos/+page.svelte): - Moments | Places | People sub-tabs (the People tab appears only when the faces feature is enabled, via a /api/people capability probe), mirroring the vanilla photos sub-nav. - Square ↔ justified layout toggle. Justified uses a Flickr-style row-packer over the width/height the photos list endpoint returns (PhotoItem), falling back to 1:1 when dimensions are missing. New components: - PhotoLightbox.svelte — the lightbox extracted from the photos page into a reusable component (items + bindable index, onDelete callback) so the grid, People and Places all share one implementation (no duplication). - PlacesMap.svelte — MapLibre GL map with server-clustered markers; the vector basemap is optional (probed at /basemaps/basemap.pmtiles, themed fallback otherwise). Cluster click zooms in or opens the lightbox. - PeopleView.svelte — identity-cluster grid → per-person photo grid, with rename via the in-app prompt dialog. Supporting: - api/endpoints/people.ts (+ peopleEnabled probe); photos.ts gains fetchPhotosGeo + GeoCluster + PhotoItem; fileThumbnailUrl takes a size. - lib/vendor/maplibre.ts — minimal typings + lazy loader for the vendored MapLibre GL + pmtiles globals (kept any-free for ESLint). - utils/media.ts — shared isVideo / photoTimestamp / minimalPhotoItem. - Vendored maplibre-gl 5.24.0 + pmtiles 4.4.1 under static/vendors and an optional static/basemaps dir, matching the PR's vendored-asset pattern. - New photos.tab_*/layout_*/map_* + people.* keys in en.json. Verified: npm run check (svelte-check + eslint + stylelint + prettier), npm run test:unit (36 pass), and npm run build all green. --- frontend/.prettierignore | 1 + frontend/src/lib/api/endpoints/files.ts | 8 +- frontend/src/lib/api/endpoints/people.ts | 55 ++ frontend/src/lib/api/endpoints/photos.ts | 36 +- frontend/src/lib/components/PeopleView.svelte | 297 +++++++ .../src/lib/components/PhotoLightbox.svelte | 377 +++++++++ frontend/src/lib/components/PlacesMap.svelte | 342 ++++++++ frontend/src/lib/utils/media.ts | 42 + frontend/src/lib/vendor/maplibre.ts | 95 +++ frontend/src/routes/photos/+page.svelte | 749 +++++++----------- frontend/static/basemaps/.gitignore | 5 + frontend/static/basemaps/README.md | 33 + frontend/static/locales/en.json | 17 +- frontend/static/vendors/maplibre-gl.css | 1 + frontend/static/vendors/maplibre-gl.js | 59 ++ frontend/static/vendors/pmtiles.js | 2 + 16 files changed, 1663 insertions(+), 456 deletions(-) create mode 100644 frontend/src/lib/api/endpoints/people.ts create mode 100644 frontend/src/lib/components/PeopleView.svelte create mode 100644 frontend/src/lib/components/PhotoLightbox.svelte create mode 100644 frontend/src/lib/components/PlacesMap.svelte create mode 100644 frontend/src/lib/utils/media.ts create mode 100644 frontend/src/lib/vendor/maplibre.ts create mode 100644 frontend/static/basemaps/.gitignore create mode 100644 frontend/static/basemaps/README.md create mode 100644 frontend/static/vendors/maplibre-gl.css create mode 100644 frontend/static/vendors/maplibre-gl.js create mode 100644 frontend/static/vendors/pmtiles.js diff --git a/frontend/.prettierignore b/frontend/.prettierignore index 45415cfe..dd38ec68 100644 --- a/frontend/.prettierignore +++ b/frontend/.prettierignore @@ -13,3 +13,4 @@ src/lib/icons/registry.ts static/locales/ static/vendors/ static/workers/ +static/basemaps/ diff --git a/frontend/src/lib/api/endpoints/files.ts b/frontend/src/lib/api/endpoints/files.ts index 3d641968..54444ac3 100644 --- a/frontend/src/lib/api/endpoints/files.ts +++ b/frontend/src/lib/api/endpoints/files.ts @@ -85,6 +85,10 @@ export function fileInlineUrl(fileId: string): string { return `/api/files/${fileId}?inline=true`; } -export function fileThumbnailUrl(fileId: string): string { - return `/api/files/${fileId}/thumbnail/preview`; +/** Thumbnail URL for a file at the given size (server-rendered, content-typed). */ +export function fileThumbnailUrl( + fileId: string, + size: 'icon' | 'preview' | 'large' = 'preview' +): string { + return `/api/files/${fileId}/thumbnail/${size}`; } diff --git a/frontend/src/lib/api/endpoints/people.ts b/frontend/src/lib/api/endpoints/people.ts new file mode 100644 index 00000000..93d16598 --- /dev/null +++ b/frontend/src/lib/api/endpoints/people.ts @@ -0,0 +1,55 @@ +/** People (faces) endpoints — ported from features/library/people.js. */ +import { apiFetch } from '$lib/api/client'; +import { getCsrfHeaders } from '$lib/api/csrf'; + +/** An identity cluster from `GET /api/people`. */ +export interface Person { + id: string; + /** Absent until the user names the person. */ + name?: string; + /** File id of the cover face's photo, for the tile thumbnail. */ + cover_file_id?: string; + face_count: number; + is_hidden: boolean; +} + +/** + * List identity clusters. The feature is gated on `OXICLOUD_ENABLE_FACES` — + * when it is off the route 404s; callers treat that as "faces disabled". + */ +export async function fetchPeople(): Promise { + const res = await apiFetch('/api/people', { credentials: 'same-origin' }); + if (!res.ok) throw new Error(`people failed: ${res.status}`); + return (await res.json()) as Person[]; +} + +/** + * Probe whether the People feature is available (faces enabled). Used to reveal + * the People tab only when the backend can serve it. + */ +export async function peopleEnabled(): Promise { + try { + const res = await apiFetch('/api/people', { credentials: 'same-origin' }); + return res.ok; + } catch { + return false; + } +} + +/** File ids of the photos a person appears in. */ +export async function fetchPersonPhotos(personId: string): Promise { + const res = await apiFetch(`/api/people/${personId}/photos`, { credentials: 'same-origin' }); + if (!res.ok) throw new Error(`person photos failed: ${res.status}`); + return (await res.json()) as string[]; +} + +/** Rename a person, or pass `null` to clear the name. */ +export async function renamePerson(personId: string, name: string | null): Promise { + const res = await apiFetch(`/api/people/${personId}`, { + method: 'PATCH', + credentials: 'same-origin', + headers: { 'Content-Type': 'application/json', ...getCsrfHeaders() }, + body: JSON.stringify({ name }) + }); + if (!res.ok) throw new Error(`rename failed: ${res.status}`); +} diff --git a/frontend/src/lib/api/endpoints/photos.ts b/frontend/src/lib/api/endpoints/photos.ts index 64df2b7a..72ac8ba5 100644 --- a/frontend/src/lib/api/endpoints/photos.ts +++ b/frontend/src/lib/api/endpoints/photos.ts @@ -3,8 +3,17 @@ import { apiFetch } from '$lib/api/client'; import { getCsrfHeaders } from '$lib/api/csrf'; import type { FileItem } from '$lib/api/types'; +/** + * A timeline photo/video. Extends {@link FileItem} with the pixel dimensions the + * list endpoint returns, used by the justified (aspect-preserving) grid layout. + */ +export interface PhotoItem extends FileItem { + width?: number; + height?: number; +} + export interface PhotoPage { - items: FileItem[]; + items: PhotoItem[]; nextCursor: string | null; } @@ -27,6 +36,29 @@ export interface BatchTrashResult { failed: string[]; } +/** One server-side photo cluster for the Places map (`GET /api/photos/geo`). */ +export interface GeoCluster { + lng: number; + lat: number; + count: number; + sample_file_id: string; +} + +/** + * Fetch geotagged-photo clusters for a viewport. The backend aggregates + * server-side on a grid keyed by zoom, so the client draws one lightweight + * marker per cluster — no client-side clustering needed. `bbox` is + * `"west,south,east,north"` in decimal degrees. Available only when the + * Places feature is enabled (otherwise the route 404s). + */ +export async function fetchPhotosGeo(bbox: string, zoom: number): Promise { + const res = await apiFetch(`/api/photos/geo?bbox=${encodeURIComponent(bbox)}&zoom=${zoom}`, { + credentials: 'same-origin' + }); + if (!res.ok) throw new Error(`photos geo failed: ${res.status}`); + return (await res.json()) as GeoCluster[]; +} + /** Backend `MAX_BATCH_SIZE` — chunk larger selections into separate requests. */ const BATCH_CHUNK_SIZE = 1000; @@ -40,7 +72,7 @@ export async function fetchPhotos(limit = 60, before?: string | null): Promise

    + /** + * People (faces): a grid of identity clusters from `GET /api/people`; clicking + * a person shows their photos in the shared lightbox. Faces are detected and + * clustered server-side, so this view is read-mostly (list, drill-in, rename). + * Gated on `OXICLOUD_ENABLE_FACES` — when off the API 404s and we show a hint. + */ + import EmptyState from '$lib/components/EmptyState.svelte'; + import PhotoLightbox from '$lib/components/PhotoLightbox.svelte'; + import Icon from '$lib/icons/Icon.svelte'; + import { + fetchPeople, + fetchPersonPhotos, + renamePerson, + type Person + } from '$lib/api/endpoints/people'; + import { fileThumbnailUrl } from '$lib/api/endpoints/files'; + import type { FileItem } from '$lib/api/types'; + import { promptDialog } from '$lib/stores/dialogs.svelte'; + import { t } from '$lib/i18n/index.svelte'; + import { errorMessage } from '$lib/utils/errors'; + import { minimalPhotoItem } from '$lib/utils/media'; + import { onMount } from 'svelte'; + + type View = 'list' | 'person'; + + let view = $state('list'); + let people = $state([]); + let loading = $state(true); + /** Set when the feature is unavailable (faces disabled) or the list errors. */ + let disabled = $state(false); + + // Drill-in state. + let current = $state<{ id: string; name: string } | null>(null); + let photos = $state([]); + let lightbox = $state(-1); + + function personName(p: Person): string { + return p.name || t('people.unnamed', 'Unnamed'); + } + + async function loadList() { + loading = true; + disabled = false; + try { + people = await fetchPeople(); + } catch { + people = []; + disabled = true; + } finally { + loading = false; + } + } + + async function openPerson(p: Person) { + current = { id: p.id, name: personName(p) }; + view = 'person'; + photos = []; + lightbox = -1; + try { + const ids = await fetchPersonPhotos(p.id); + photos = ids.map(minimalPhotoItem); + } catch { + photos = []; + } + } + + function backToList() { + view = 'list'; + current = null; + lightbox = -1; + } + + async function rename() { + if (!current) return; + const placeholder = t('people.unnamed', 'Unnamed'); + const value = current.name === placeholder ? '' : current.name; + const next = await promptDialog({ + title: t('people.rename_title', 'Name this person'), + message: t('people.name_label', 'Name'), + defaultValue: value + }); + if (next === null) return; + const trimmed = next.trim(); + try { + await renamePerson(current.id, trimmed || null); + current = { id: current.id, name: trimmed || placeholder }; + // Keep the list in sync so a return trip shows the new name. + people = people.map((p) => (p.id === current?.id ? { ...p, name: trimmed || undefined } : p)); + } catch (e) { + // Surface the failure inline via the dialog's own error channel is not + // available here; fall back to logging — rename is non-destructive. + console.error('rename failed:', errorMessage(e)); + } + } + + function onDeletePhoto(id: string) { + photos = photos.filter((p) => p.id !== id); + } + + onMount(loadList); + + +{#if loading} +

    {t('common.loading', 'Loading…')}

    +{:else if disabled} + +{:else if view === 'list'} + {#if people.length === 0} + + {:else} +
      + {#each people as person (person.id)} +
    • + +
    • + {/each} +
    + {/if} +{:else if current} +
    + +

    {current.name}

    + +
    + +
      + {#each photos as photo, i (photo.id)} +
    • + +
    • + {/each} +
    + + +{/if} + + diff --git a/frontend/src/lib/components/PhotoLightbox.svelte b/frontend/src/lib/components/PhotoLightbox.svelte new file mode 100644 index 00000000..97da2276 --- /dev/null +++ b/frontend/src/lib/components/PhotoLightbox.svelte @@ -0,0 +1,377 @@ + + + + +{#if item} + + +{/if} + + diff --git a/frontend/src/lib/components/PlacesMap.svelte b/frontend/src/lib/components/PlacesMap.svelte new file mode 100644 index 00000000..aa358c38 --- /dev/null +++ b/frontend/src/lib/components/PlacesMap.svelte @@ -0,0 +1,342 @@ + + +
    +
    + {#if loading && !error} +
    + {/if} + {#if error} +
    {t('photos.map_error', 'Could not load the map')}
    + {/if} +
    + + + + diff --git a/frontend/src/lib/utils/media.ts b/frontend/src/lib/utils/media.ts new file mode 100644 index 00000000..1b7dd066 --- /dev/null +++ b/frontend/src/lib/utils/media.ts @@ -0,0 +1,42 @@ +/** Shared helpers for the photo/video timeline (used by the grid, lightbox, + * People and Places views). */ +import type { FileItem } from '$lib/api/types'; + +/** True for video tiles (they get a play badge and client-side frame thumbs). */ +export function isVideo(p: FileItem): boolean { + return (p.mime_type ?? '').startsWith('video/'); +} + +/** + * EXIF-aware capture timestamp in milliseconds. `sort_date`/`created_at` are + * stored in seconds; values below ~1e12 are treated as seconds and scaled up. + */ +export function photoTimestamp(p: FileItem): number { + const v = p.sort_date || p.created_at || 0; + return v < 1e12 ? v * 1000 : v; +} + +/** + * Build a minimal {@link FileItem} from just an id — used by People and Places + * to open the lightbox by id and let it lazily fetch the rest (name, EXIF). + */ +export function minimalPhotoItem(id: string): FileItem { + return { + category: 'image', + created_at: 0, + icon_class: '', + icon_special_class: '', + id, + mime_type: 'image/jpeg', + modified_at: 0, + name: '', + owner_id: '', + folder_id: '', + path: '', + size: 0, + size_formatted: '', + sort_date: 0, + etag: '', + content_hash: '' + }; +} diff --git a/frontend/src/lib/vendor/maplibre.ts b/frontend/src/lib/vendor/maplibre.ts new file mode 100644 index 00000000..7512f99e --- /dev/null +++ b/frontend/src/lib/vendor/maplibre.ts @@ -0,0 +1,95 @@ +/** + * Minimal typings + lazy loader for the vendored MapLibre GL + pmtiles globals. + * + * The libraries are heavy (~1 MB) and only the Places map needs them, so they + * are vendored under `/vendors` (not bundled) and injected on first use — the + * same pattern the legacy frontend used. We declare only the small slice of the + * MapLibre API the Places view touches, so the rest of the app stays `any`-free. + */ + +export interface MapBounds { + getWest(): number; + getSouth(): number; + getEast(): number; + getNorth(): number; +} + +export interface LngLatBounds { + extend(lngLat: [number, number]): LngLatBounds; + isEmpty(): boolean; +} + +export interface MapMarker { + setLngLat(lngLat: [number, number]): MapMarker; + addTo(map: MapLibreMap): MapMarker; + remove(): void; +} + +export interface MapLibreMap { + addControl(control: unknown, position?: string): MapLibreMap; + on(type: string, listener: () => void): void; + getBounds(): MapBounds; + getZoom(): number; + resize(): void; + easeTo(opts: { center: [number, number]; zoom: number }): void; + fitBounds( + bounds: LngLatBounds, + opts?: { padding?: number; maxZoom?: number; duration?: number } + ): void; + remove(): void; +} + +export interface MapLibreModule { + Map: new (opts: Record) => MapLibreMap; + Marker: new (opts: { element: HTMLElement }) => MapMarker; + NavigationControl: new (opts?: { showCompass?: boolean }) => unknown; + AttributionControl: new (opts?: { customAttribution?: string }) => unknown; + LngLatBounds: new () => LngLatBounds; + addProtocol(name: string, fn: unknown): void; +} + +export interface PMTilesModule { + Protocol: new () => { tile: unknown }; +} + +export interface MapLibs { + maplibregl: MapLibreModule; + pmtiles: PMTilesModule; +} + +let cached: MapLibs | null = null; + +/** Inject a vendored script once, resolving when it has loaded. */ +function loadScript(src: string): Promise { + return new Promise((resolve, reject) => { + if (document.querySelector(`script[data-vendor="${src}"]`)) { + resolve(); + return; + } + const s = document.createElement('script'); + s.src = src; + s.async = true; + s.dataset.vendor = src; + s.addEventListener('load', () => resolve()); + s.addEventListener('error', () => reject(new Error(`Failed to load ${src}`))); + document.head.appendChild(s); + }); +} + +/** Lazy-load MapLibre GL + pmtiles.js (+ MapLibre CSS) and read their globals. */ +export async function loadMapLibs(): Promise { + if (cached) return cached; + if (!document.querySelector('link[data-vendor="maplibre-css"]')) { + const l = document.createElement('link'); + l.rel = 'stylesheet'; + l.href = '/vendors/maplibre-gl.css'; + l.dataset.vendor = 'maplibre-css'; + document.head.appendChild(l); + } + await loadScript('/vendors/maplibre-gl.js'); + await loadScript('/vendors/pmtiles.js'); + const w = window as unknown as { maplibregl?: MapLibreModule; pmtiles?: PMTilesModule }; + if (!w.maplibregl || !w.pmtiles) throw new Error('map libraries failed to initialise'); + cached = { maplibregl: w.maplibregl, pmtiles: w.pmtiles }; + return cached; +} diff --git a/frontend/src/routes/photos/+page.svelte b/frontend/src/routes/photos/+page.svelte index d492ad48..3dab174c 100644 --- a/frontend/src/routes/photos/+page.svelte +++ b/frontend/src/routes/photos/+page.svelte @@ -1,50 +1,52 @@ {t('nav.photos', 'Photos')} · OxiCloud -

    {t('nav.photos', 'Photos')}

    -
    - {#each MODES as m (m)} - + + {#if peopleAvailable} + - {/each} + {/if}
    -{#if selected.size > 0} -
    - {t('files.selected_count', { n: selected.size }, '{{n}} selected')} -
    - - - -
    -
    -{/if} - -{#if error} - -{:else if items.length === 0 && exhausted} - -{:else} - {#each groups as group (group.key)} -

    - {group.label} {group.photos.length} -

    -
      - {#each group.photos as photo (photo.id)} -
    • - - -
    • +{#if tab === 'moments'} +
      +
      + {#each MODES as m (m)} + {/each} -
    - {/each} -{/if} - - -{#if loading}

    {t('common.loading', 'Loading…')}

    {/if} - -{#if lbItem} - - + + {#if selected.size > 0} +
    + {t('files.selected_count', { n: selected.size }, '{{n}} selected')} +
    + + + +
    +
    + {/if} + + {#if error} + + {:else if items.length === 0 && exhausted} + + {:else} +
    +
    + {#each groups as group (group.key)} +

    + {group.label} {group.photos.length} +

    + {#if layoutMode === 'justified' && gridWidth > 0} + {#each justifiedRows(group.photos, gridWidth) as row, ri (group.key + '-' + ri)} +
    + {#each row.tiles as cell (cell.file.id)} + {@render tile(cell.file, `width:${cell.w}px;height:${cell.h}px`)} + {/each} +
    + {/each} + {:else} +
      + {#each group.photos as photo (photo.id)} +
    • + {@render tile(photo)} +
    • + {/each} +
    + {/if} + {/each} +
    +
    + {/if} + + + {#if loading}

    {t('common.loading', 'Loading…')}

    {/if} + + +{:else if tab === 'places'} + +{:else if tab === 'people'} + {/if} +{#snippet tile(photo: PhotoItem, sizeStyle?: string)} +
    + + +
    +{/snippet} + diff --git a/frontend/static/basemaps/.gitignore b/frontend/static/basemaps/.gitignore new file mode 100644 index 00000000..4b9a289c --- /dev/null +++ b/frontend/static/basemaps/.gitignore @@ -0,0 +1,5 @@ +# The vector basemap is large (tens of MB) and operator-provided — never +# commit it to the repo. Drop a Protomaps `.pmtiles` here as `basemap.pmtiles` +# and the existing static file server (tower-http ServeDir, Range-capable) +# will serve it to the Places map. See README.md. +*.pmtiles diff --git a/frontend/static/basemaps/README.md b/frontend/static/basemaps/README.md new file mode 100644 index 00000000..4ad70e71 --- /dev/null +++ b/frontend/static/basemaps/README.md @@ -0,0 +1,33 @@ +# Places basemap (optional) + +The **Places** photo map renders your geotagged photos as clusters. It works +out of the box **without** a basemap (clusters on a plain background). To get a +real street/terrain backdrop, drop a self-hosted vector basemap here — no +third-party tile API, fully offline. + +## How it works (Approach "A") + +OxiCloud already serves `static/` through `tower-http`'s `ServeDir`, which +honours **HTTP Range** requests. A [PMTiles](https://docs.protomaps.com/pmtiles/) +basemap is a *single file* read directly by the browser via Range — so the +basemap is just a static file the app already knows how to serve. No extra +backend, no tile server, no API keys. + +## Enabling it + +1. Get a Protomaps `.pmtiles` basemap (vector, ODbL OpenStreetMap data): + - Whole planet z0–15 (~120 GB) or a smaller global `z0-6` (~60 MB), or + - A **regional extract** (recommended — only the area you need, a few MB): + ```sh + # one-time, downloads only your bounding box from the remote planet + pmtiles extract https://build.protomaps.com/.pmtiles basemap.pmtiles \ + --bbox=,,, + ``` + See https://docs.protomaps.com/basemaps/downloads +2. Place it here as **`static/basemaps/basemap.pmtiles`** (this path is + git-ignored on purpose — see `.gitignore`). +3. Reload the Places view. The map will pick it up automatically. + +The bundled style is **label-light** (water / land / roads / buildings, no +text) so it needs no glyph/sprite assets. Attribution “© OpenStreetMap” +(ODbL) is shown automatically when a basemap is present. diff --git a/frontend/static/locales/en.json b/frontend/static/locales/en.json index b653245e..7c9f53be 100644 --- a/frontend/static/locales/en.json +++ b/frontend/static/locales/en.json @@ -88,7 +88,22 @@ "full_resolution": "Full resolution", "group_by": "Group by", "trash_partial": "{{ok}} of {{total}} moved to trash.", - "trashed": "{{n}} moved to trash." + "trashed": "{{n}} moved to trash.", + "layout_square": "Grid", + "layout_justified": "Justified", + "tab_moments": "Moments", + "tab_places": "Places", + "tab_people": "People", + "map_loading": "Loading map…", + "map_error": "Could not load the map" + }, + "people": { + "unnamed": "Unnamed", + "empty": "No people yet", + "disabled": "Face recognition is disabled", + "rename_title": "Name this person", + "name_label": "Name", + "back": "Back" }, "music": { "create_playlist": "Create Playlist", diff --git a/frontend/static/vendors/maplibre-gl.css b/frontend/static/vendors/maplibre-gl.css new file mode 100644 index 00000000..6b4bfda7 --- /dev/null +++ b/frontend/static/vendors/maplibre-gl.css @@ -0,0 +1 @@ +.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0 0 0/0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px rgba(0,0,0,.1)}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:rgba(0,0,0,.05)}}.maplibregl-ctrl button:not(:disabled):active{background-color:rgba(0,0,0,.05)}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}@keyframes maplibregl-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:hsla(0,0%,100%,.5);margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:hsla(0,0%,100%,.5);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:rgba(0,0,0,.05)}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-attrib a{color:rgba(0,0,0,.75);text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{background-color:hsla(0,0%,100%,.75);border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px;white-space:nowrap}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}[dir=rtl] .maplibregl-popup-anchor-left{flex-direction:row-reverse}[dir=rtl] .maplibregl-popup-anchor-right{flex-direction:row}[dir=rtl] .maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-end}[dir=rtl] .maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-start}[dir=rtl] .maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-end}[dir=rtl] .maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-start}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-close-button:hover{background-color:rgba(0,0,0,.05)}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.1);padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:"";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px rgba(0,0,0,.35);box-sizing:border-box;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px}@media (prefers-reduced-motion:reduce){.maplibregl-user-location-dot:before{animation:none}}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:rgba(0,0,0,.4);color:#fff;display:flex;font-size:1.4em;inset:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999} \ No newline at end of file diff --git a/frontend/static/vendors/maplibre-gl.js b/frontend/static/vendors/maplibre-gl.js new file mode 100644 index 00000000..c5acb688 --- /dev/null +++ b/frontend/static/vendors/maplibre-gl.js @@ -0,0 +1,59 @@ +/** + * MapLibre GL JS + * @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v5.24.0/LICENSE.txt + */ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : +typeof define === 'function' && define.amd ? define(factory) : +(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.maplibregl = factory()); +})(this, (function () { 'use strict'; + +/* eslint-disable */ + +var maplibregl = {}; +var modules = {}; +function define(moduleName, _dependencies, moduleFactory) { + modules[moduleName] = moduleFactory; + + // to get the list of modules see generated dist/maplibre-gl-dev.js file (look for `define(` calls) + if (moduleName !== 'index') { + return; + } + + // we assume that when an index module is initializing then other modules are loaded already + var workerBundleString = 'var sharedModule = {}; (' + modules.shared + ')(sharedModule); (' + modules.worker + ')(sharedModule);' + + var sharedModule = {}; + // the order of arguments of a module factory depends on rollup (it decides who is whose dependency) + // to check the correct order, see dist/maplibre-gl-dev.js file (look for `define(` calls) + // we assume that for our 3 chunks it will generate 3 modules and their order is predefined like the following + modules.shared(sharedModule); + modules.index(maplibregl, sharedModule); + + if (typeof window !== 'undefined') { + maplibregl.setWorkerUrl(window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' }))); + } + + return maplibregl; +}; + + + +define("shared",["exports"],(function(t){"use strict";function e(t,e,n,r){return new(n||(n=Promise))((function(i,s){function o(t){try{l(r.next(t));}catch(t){s(t);}}function a(t){try{l(r.throw(t));}catch(t){s(t);}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e);}))).then(o,a);}l((r=r.apply(t,e||[])).next());}))}function n(t,e){this.x=t,this.y=e;}function r(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var i,s;"function"==typeof SuppressedError&&SuppressedError,n.prototype={clone(){return new n(this.x,this.y)},add(t){return this.clone()._add(t)},sub(t){return this.clone()._sub(t)},multByPoint(t){return this.clone()._multByPoint(t)},divByPoint(t){return this.clone()._divByPoint(t)},mult(t){return this.clone()._mult(t)},div(t){return this.clone()._div(t)},rotate(t){return this.clone()._rotate(t)},rotateAround(t,e){return this.clone()._rotateAround(t,e)},matMult(t){return this.clone()._matMult(t)},unit(){return this.clone()._unit()},perp(){return this.clone()._perp()},round(){return this.clone()._round()},mag(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals(t){return this.x===t.x&&this.y===t.y},dist(t){return Math.sqrt(this.distSqr(t))},distSqr(t){const e=t.x-this.x,n=t.y-this.y;return e*e+n*n},angle(){return Math.atan2(this.y,this.x)},angleTo(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith(t){return this.angleWithSep(t.x,t.y)},angleWithSep(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult(t){const e=t[2]*this.x+t[3]*this.y;return this.x=t[0]*this.x+t[1]*this.y,this.y=e,this},_add(t){return this.x+=t.x,this.y+=t.y,this},_sub(t){return this.x-=t.x,this.y-=t.y,this},_mult(t){return this.x*=t,this.y*=t,this},_div(t){return this.x/=t,this.y/=t,this},_multByPoint(t){return this.x*=t.x,this.y*=t.y,this},_divByPoint(t){return this.x/=t.x,this.y/=t.y,this},_unit(){return this._div(this.mag()),this},_perp(){const t=this.y;return this.y=this.x,this.x=-t,this},_rotate(t){const e=Math.cos(t),n=Math.sin(t),r=n*this.x+e*this.y;return this.x=e*this.x-n*this.y,this.y=r,this},_rotateAround(t,e){const n=Math.cos(t),r=Math.sin(t),i=e.y+r*(this.x-e.x)+n*(this.y-e.y);return this.x=e.x+n*(this.x-e.x)-r*(this.y-e.y),this.y=i,this},_round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},constructor:n},n.convert=function(t){if(t instanceof n)return t;if(Array.isArray(t))return new n(+t[0],+t[1]);if(void 0!==t.x&&void 0!==t.y)return new n(+t.x,+t.y);throw new Error("Expected [x, y] or {x, y} point format")};var o=function(){if(s)return i;function t(t,e,n,r){this.cx=3*t,this.bx=3*(n-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(r-e)-this.cy,this.ay=1-this.cy-this.by,this.p1x=t,this.p1y=e,this.p2x=n,this.p2y=r;}return s=1,i=t,t.prototype={sampleCurveX:function(t){return ((this.ax*t+this.bx)*t+this.cx)*t},sampleCurveY:function(t){return ((this.ay*t+this.by)*t+this.cy)*t},sampleCurveDerivativeX:function(t){return (3*this.ax*t+2*this.bx)*t+this.cx},solveCurveX:function(t,e){if(void 0===e&&(e=1e-6),t<0)return 0;if(t>1)return 1;for(var n=t,r=0;r<8;r++){var i=this.sampleCurveX(n)-t;if(Math.abs(i)i?o=n:a=n,n=.5*(a-o)+o;return n},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}},i}(),a=r(o);let l,u;function c(){return null!=l||(l="undefined"!=typeof OffscreenCanvas&&new OffscreenCanvas(1,1).getContext("2d")&&"function"==typeof createImageBitmap),l}function h(){if(null==u&&(u=!1,c())){const t=5,e=new OffscreenCanvas(t,t).getContext("2d",{willReadFrequently:!0});if(e){for(let n=0;n4&&void 0!==arguments[4]?arguments[4]:"zyx",s=Math.PI/360;e*=s,r*=s,n*=s;var o=Math.sin(e),a=Math.cos(e),l=Math.sin(n),u=Math.cos(n),c=Math.sin(r),h=Math.cos(r);switch(i){case "xyz":t[0]=o*u*h+a*l*c,t[1]=a*l*h-o*u*c,t[2]=a*u*c+o*l*h,t[3]=a*u*h-o*l*c;break;case "xzy":t[0]=o*u*h-a*l*c,t[1]=a*l*h-o*u*c,t[2]=a*u*c+o*l*h,t[3]=a*u*h+o*l*c;break;case "yxz":t[0]=o*u*h+a*l*c,t[1]=a*l*h-o*u*c,t[2]=a*u*c-o*l*h,t[3]=a*u*h+o*l*c;break;case "yzx":t[0]=o*u*h+a*l*c,t[1]=a*l*h+o*u*c,t[2]=a*u*c-o*l*h,t[3]=a*u*h-o*l*c;break;case "zxy":t[0]=o*u*h-a*l*c,t[1]=a*l*h+o*u*c,t[2]=a*u*c+o*l*h,t[3]=a*u*h-o*l*c;break;case "zyx":t[0]=o*u*h-a*l*c,t[1]=a*l*h+o*u*c,t[2]=a*u*c-o*l*h,t[3]=a*u*h+o*l*c;break;default:throw new Error("Unknown angle order "+i)}return t}function I(){var t=new f(2);return f!=Float32Array&&(t[0]=0,t[1]=0),t}function E(t,e){var n=new f(2);return n[0]=t,n[1]=e,n}m(),_=new f(4),f!=Float32Array&&(_[0]=0,_[1]=0,_[2]=0,_[3]=0),m(),x(1,0,0),x(0,1,0),M(),M(),d(),I();const T=8192;function F(t,e,n){return e*(T/(t.tileSize*Math.pow(2,n-t.tileID.overscaledZ)))}function P(t){return t instanceof Error?t:new Error("string"==typeof t?t:String(t))}function D(t,e){return (t%e+e)%e}function z(t,e,n){return t*(1-n)+e*n}function B(t){if(t<=0)return 0;if(t>=1)return 1;const e=t*t,n=e*t;return 4*(t<.5?n:3*(t-e)+n-.75)}function C(t,e,n,r){const i=new a(t,e,n,r);return t=>i.solve(t)}const V=C(.25,.1,.25,1);function L(t,e,n){return Math.min(n,Math.max(e,t))}function O(t,e,n){const r=n-e,i=((t-e)%r+r)%r+e;return i===e?n:i}function $(t,...e){for(const n of e)for(const e in n)t[e]=n[e];return t}let R=1;function N(t,e,n){const r={};for(const n in t)r[n]=e.call(this,t[n],n,t);return r}function U(t,e,n){const r={};for(const n in t)e.call(this,t[n],n,t)&&(r[n]=t[n]);return r}function j(t){return Array.isArray(t)?t.map(j):"object"==typeof t&&t?N(t,j):t}const q={};function G(t){q[t]||("undefined"!=typeof console&&console.warn(t),q[t]=!0);}function X(t,e,n){return (n.y-t.y)*(e.x-t.x)>(e.y-t.y)*(n.x-t.x)}function Y(t){return "undefined"!=typeof WorkerGlobalScope&&void 0!==t&&t instanceof WorkerGlobalScope}let Z=null;function H(t){return "undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap}const W="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=";function K(t,n,r,i,s){return e(this,void 0,void 0,(function*(){if("undefined"==typeof VideoFrame)throw new Error("VideoFrame not supported");const e=new VideoFrame(t,{timestamp:0});try{const o=null==e?void 0:e.format;if(!o||!o.startsWith("BGR")&&!o.startsWith("RGB"))throw new Error(`Unrecognized format ${o}`);const a=o.startsWith("BGR"),l=new Uint8ClampedArray(i*s*4);if(yield e.copyTo(l,function(t,e,n,r,i){const s=4*Math.max(-e,0),o=(Math.max(0,n)-n)*r*4+s,a=4*r,l=Math.max(0,e),u=Math.max(0,n);return {rect:{x:l,y:u,width:Math.min(t.width,e+r)-l,height:Math.min(t.height,n+i)-u},layout:[{offset:o,stride:a}]}}(t,n,r,i,s)),a)for(let t=0;t{t.removeEventListener(e,n,r);}}}function et(t){return t*Math.PI/180}function nt(t){return t/Math.PI*180}const rt={touchstart:!0,touchmove:!0,touchmoveWindow:!0,touchend:!0,touchcancel:!0},it={dblclick:!0,click:!0,mouseover:!0,mouseout:!0,mousedown:!0,mousemove:!0,mousemoveWindow:!0,mouseup:!0,mouseupWindow:!0,contextmenu:!0,wheel:!0},st="AbortError";class ot extends Error{constructor(t=st){super(t instanceof Error?t.message:t),this.name=st,t instanceof Error&&t.stack&&(this.stack=t.stack);}}function at(t){return t instanceof Error&&t.name===st}function lt(t){if(t.aborted)throw new ot(t.reason)}const ut={MAX_PARALLEL_IMAGE_REQUESTS:16,MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME:8,MAX_TILE_CACHE_ZOOM_LEVELS:5,REGISTERED_PROTOCOLS:{},WORKER_URL:""};function ct(t){return ut.REGISTERED_PROTOCOLS[t.substring(0,t.indexOf("://"))]}const ht="global-dispatcher";class pt extends Error{constructor(t,e,n,r){super(`AJAXError: ${e} (${t}): ${n}`),this.status=t,this.statusText=e,this.url=n,this.body=r;}}const ft=()=>{var t;return Y(self)?null===(t=self.worker)||void 0===t?void 0:t.referrer:("blob:"===window.location.protocol?window.parent:window).location.href},dt=function(t,n){return e(this,void 0,void 0,(function*(){var r,i;if(t.url.includes("://")&&!/^https?:|^file:/.test(t.url)){const e=ct(t.url);if(e){const r=yield e(t,n);return r.data||"arrayBuffer"!==t.type?r:$(r,{data:new ArrayBuffer(0)})}if(Y(self)&&(null===(r=self.worker)||void 0===r?void 0:r.actor))return self.worker.actor.sendAsync({type:"GR",data:t,targetMapId:ht},n)}if(!(t=>{var e;return t.startsWith("file:")||(null===(e=ft())||void 0===e?void 0:e.startsWith("file:"))&&!/^\w+:/.test(t)})(t.url)){if(fetch&&Request&&AbortController&&Object.hasOwn(Request.prototype,"signal"))return function(t,n){return e(this,void 0,void 0,(function*(){const e=new Request(t.url,{method:t.method||"GET",body:t.body,credentials:t.credentials,headers:t.headers,cache:t.cache,referrer:ft(),referrerPolicy:t.referrerPolicy,signal:n.signal});let r,i;"json"!==t.type||e.headers.has("Accept")||e.headers.set("Accept","application/json");try{r=yield fetch(e);}catch(e){if(at(e))throw e;throw new pt(0,P(e).message,t.url,new Blob)}if(!r.ok){const e=yield r.blob();throw new pt(r.status,r.statusText,t.url,e)}i="arrayBuffer"===t.type||"image"===t.type?r.arrayBuffer():"json"===t.type?r.json():r.text();const s=yield i;return lt(n.signal),{data:s,cacheControl:r.headers.get("Cache-Control"),expires:r.headers.get("Expires"),etag:r.headers.get("ETag")}}))}(t,n);if(Y(self)&&(null===(i=self.worker)||void 0===i?void 0:i.actor))return self.worker.actor.sendAsync({type:"GR",data:t,mustQueue:!0,targetMapId:ht},n)}return function(t,e){return new Promise(((n,r)=>{var i;const s=new XMLHttpRequest;s.open(t.method||"GET",t.url,!0),"arrayBuffer"!==t.type&&"image"!==t.type||(s.responseType="arraybuffer");for(const e in t.headers)s.setRequestHeader(e,t.headers[e]);"json"===t.type&&(s.responseType="text",(null===(i=t.headers)||void 0===i?void 0:i.Accept)||s.setRequestHeader("Accept","application/json")),s.withCredentials="include"===t.credentials,s.onerror=()=>{r(new Error(s.statusText));},s.onload=()=>{if(!e.signal.aborted)if((s.status>=200&&s.status<300||0===s.status)&&null!==s.response){let e=s.response;if("json"===t.type)try{e=JSON.parse(s.response);}catch(t){return void r(t)}n({data:e,cacheControl:s.getResponseHeader("Cache-Control"),expires:s.getResponseHeader("Expires"),etag:s.getResponseHeader("ETag")});}else {const e=new Blob([s.response],{type:s.getResponseHeader("Content-Type")});r(new pt(s.status,s.statusText,t.url,e));}},e.signal.addEventListener("abort",(()=>{s.abort(),r(new ot(e.signal.reason));})),s.send(t.body);}))}(t,n)}))};function yt(t){if(!t||t.indexOf("://")<=0||t.startsWith("data:image/")||t.startsWith("blob:"))return !0;const e=new URL(t),n=window.location;return e.protocol===n.protocol&&e.host===n.host}function mt(t,e,n){var r;(null===(r=n[t])||void 0===r?void 0:r.includes(e))||(n[t]||(n[t]=[]),n[t].push(e));}function gt(t,e,n){if(null==n?void 0:n[t]){const r=n[t].indexOf(e);-1!==r&&n[t].splice(r,1);}}class xt{constructor(t,e={}){$(this,e),this.type=t;}}class vt extends xt{constructor(t,e={}){super("error",$({error:t},e));}}class bt{on(t,e){return this._listeners||(this._listeners={}),mt(t,e,this._listeners),{unsubscribe:()=>{this.off(t,e);}}}off(t,e){return gt(t,e,this._listeners),gt(t,e,this._oneTimeListeners),this}once(t,e){return e?(this._oneTimeListeners||(this._oneTimeListeners={}),mt(t,e,this._oneTimeListeners),this):new Promise((e=>this.once(t,e)))}fire(t,e){var n,r;"string"==typeof t&&(t=new xt(t,e||{}));const i=t.type;if(this.listens(i)){t.target=this;const e=(null===(n=this._listeners)||void 0===n?void 0:n[i])?this._listeners[i].slice():[];for(const n of e)n.call(this,t);const s=(null===(r=this._oneTimeListeners)||void 0===r?void 0:r[i])?this._oneTimeListeners[i].slice():[];for(const e of s)gt(i,e,this._oneTimeListeners),e.call(this,t);const o=this._eventedParent;o&&($(t,"function"==typeof this._eventedParentData?this._eventedParentData():this._eventedParentData),o.fire(t));}else t instanceof vt&&console.error(t.error);return this}listens(t){var e,n,r,i,s;return (null===(n=null===(e=this._listeners)||void 0===e?void 0:e[t])||void 0===n?void 0:n.length)>0||(null===(i=null===(r=this._oneTimeListeners)||void 0===r?void 0:r[t])||void 0===i?void 0:i.length)>0||(null===(s=this._eventedParent)||void 0===s?void 0:s.listens(t))}setEventedParent(t,e){return this._eventedParent=t,this._eventedParentData=e,this}}var wt={$version:8,$root:{version:{required:!0,type:"enum",values:[8]},name:{type:"string"},metadata:{type:"*"},center:{type:"array",value:"number",length:2},centerAltitude:{type:"number"},zoom:{type:"number"},bearing:{type:"number",default:0,period:360,units:"degrees"},pitch:{type:"number",default:0,units:"degrees"},roll:{type:"number",default:0,units:"degrees"},state:{type:"state",default:{}},light:{type:"light"},sky:{type:"sky"},projection:{type:"projection"},terrain:{type:"terrain"},sources:{required:!0,type:"sources"},sprite:{type:"sprite"},glyphs:{type:"string"},"font-faces":{type:"fontFaces"},transition:{type:"transition"},layers:{required:!0,type:"array",value:"layer"}},sources:{"*":{type:"source"}},source:["source_vector","source_raster","source_raster_dem","source_geojson","source_video","source_image"],source_vector:{type:{required:!0,type:"enum",values:{vector:{}}},url:{type:"string"},tiles:{type:"array",value:"string"},bounds:{type:"array",value:"number",length:4,default:[-180,-85.051129,180,85.051129]},scheme:{type:"enum",values:{xyz:{},tms:{}},default:"xyz"},minzoom:{type:"number",default:0},maxzoom:{type:"number",default:22},attribution:{type:"string"},promoteId:{type:"promoteId"},volatile:{type:"boolean",default:!1},encoding:{type:"enum",values:{mvt:{},mlt:{}},default:"mvt"},"*":{type:"*"}},source_raster:{type:{required:!0,type:"enum",values:{raster:{}}},url:{type:"string"},tiles:{type:"array",value:"string"},bounds:{type:"array",value:"number",length:4,default:[-180,-85.051129,180,85.051129]},minzoom:{type:"number",default:0},maxzoom:{type:"number",default:22},tileSize:{type:"number",default:512,units:"pixels"},scheme:{type:"enum",values:{xyz:{},tms:{}},default:"xyz"},attribution:{type:"string"},volatile:{type:"boolean",default:!1},"*":{type:"*"}},source_raster_dem:{type:{required:!0,type:"enum",values:{"raster-dem":{}}},url:{type:"string"},tiles:{type:"array",value:"string"},bounds:{type:"array",value:"number",length:4,default:[-180,-85.051129,180,85.051129]},minzoom:{type:"number",default:0},maxzoom:{type:"number",default:22},tileSize:{type:"number",default:512,units:"pixels"},attribution:{type:"string"},encoding:{type:"enum",values:{terrarium:{},mapbox:{},custom:{}},default:"mapbox"},redFactor:{type:"number",default:1},blueFactor:{type:"number",default:1},greenFactor:{type:"number",default:1},baseShift:{type:"number",default:0},volatile:{type:"boolean",default:!1},"*":{type:"*"}},source_geojson:{type:{required:!0,type:"enum",values:{geojson:{}}},data:{required:!0,type:"*"},maxzoom:{type:"number",default:18},attribution:{type:"string"},buffer:{type:"number",default:128,maximum:512,minimum:0},filter:{type:"filter"},tolerance:{type:"number",default:.375},cluster:{type:"boolean",default:!1},clusterRadius:{type:"number",default:50,minimum:0},clusterMaxZoom:{type:"number"},clusterMinPoints:{type:"number"},clusterProperties:{type:"*"},lineMetrics:{type:"boolean",default:!1},generateId:{type:"boolean",default:!1},promoteId:{type:"promoteId"}},source_video:{type:{required:!0,type:"enum",values:{video:{}}},urls:{required:!0,type:"array",value:"string"},coordinates:{required:!0,type:"array",length:4,value:{type:"array",length:2,value:"number"}}},source_image:{type:{required:!0,type:"enum",values:{image:{}}},url:{required:!0,type:"string"},coordinates:{required:!0,type:"array",length:4,value:{type:"array",length:2,value:"number"}}},layer:{id:{type:"string",required:!0},type:{type:"enum",values:{fill:{},line:{},symbol:{},circle:{},heatmap:{},"fill-extrusion":{},raster:{},hillshade:{},"color-relief":{},background:{}},required:!0},metadata:{type:"*"},source:{type:"string"},"source-layer":{type:"string"},minzoom:{type:"number",minimum:0,maximum:24},maxzoom:{type:"number",minimum:0,maximum:24},filter:{type:"filter"},layout:{type:"layout"},paint:{type:"paint"}},layout:["layout_fill","layout_line","layout_circle","layout_heatmap","layout_fill-extrusion","layout_symbol","layout_raster","layout_hillshade","layout_color-relief","layout_background"],layout_background:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_fill:{"fill-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_circle:{"circle-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_heatmap:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},"layout_fill-extrusion":{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_line:{"line-cap":{type:"enum",values:{butt:{},round:{},square:{}},default:"butt",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"line-join":{type:"enum",values:{bevel:{},round:{},miter:{}},default:"miter",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"line-miter-limit":{type:"number",default:2,requires:[{"line-join":"miter"}],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"line-round-limit":{type:"number",default:1.05,requires:[{"line-join":"round"}],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"line-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_symbol:{"symbol-placement":{type:"enum",values:{point:{},line:{},"line-center":{}},default:"point",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"symbol-spacing":{type:"number",default:250,minimum:1,units:"pixels",requires:[{"symbol-placement":"line"}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"symbol-avoid-edges":{type:"boolean",default:!1,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"symbol-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"symbol-z-order":{type:"enum",values:{auto:{},"viewport-y":{},source:{}},default:"auto",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-allow-overlap":{type:"boolean",default:!1,requires:["icon-image",{"!":"icon-overlap"}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-overlap":{type:"enum",values:{never:{},always:{},cooperative:{}},requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-ignore-placement":{type:"boolean",default:!1,requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-optional":{type:"boolean",default:!1,requires:["icon-image","text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-rotation-alignment":{type:"enum",values:{map:{},viewport:{},auto:{}},default:"auto",requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-size":{type:"number",default:1,minimum:0,units:"factor of the original icon size",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-text-fit":{type:"enum",values:{none:{},width:{},height:{},both:{}},default:"none",requires:["icon-image","text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-text-fit-padding":{type:"array",value:"number",length:4,default:[0,0,0,0],units:"pixels",requires:["icon-image","text-field",{"icon-text-fit":["both","width","height"]}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"icon-image":{type:"resolvedImage",tokens:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-rotate":{type:"number",default:0,period:360,units:"degrees",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-padding":{type:"padding",default:[2],units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-keep-upright":{type:"boolean",default:!1,requires:["icon-image",{"icon-rotation-alignment":"map"},{"symbol-placement":["line","line-center"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-offset":{type:"array",value:"number",length:2,default:[0,0],requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-anchor":{type:"enum",values:{center:{},left:{},right:{},top:{},bottom:{},"top-left":{},"top-right":{},"bottom-left":{},"bottom-right":{}},default:"center",requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-pitch-alignment":{type:"enum",values:{map:{},viewport:{},auto:{}},default:"auto",requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-pitch-alignment":{type:"enum",values:{map:{},viewport:{},auto:{}},default:"auto",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-rotation-alignment":{type:"enum",values:{map:{},viewport:{},"viewport-glyph":{},auto:{}},default:"auto",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-field":{type:"formatted",default:"",tokens:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-font":{type:"array",value:"string",default:["Open Sans Regular","Arial Unicode MS Regular"],requires:["text-field"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-size":{type:"number",default:16,minimum:0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-max-width":{type:"number",default:10,minimum:0,units:"ems",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-line-height":{type:"number",default:1.2,units:"ems",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-letter-spacing":{type:"number",default:0,units:"ems",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-justify":{type:"enum",values:{auto:{},left:{},center:{},right:{}},default:"center",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-radial-offset":{type:"number",units:"ems",default:0,requires:["text-field"],"property-type":"data-driven",expression:{interpolated:!0,parameters:["zoom","feature"]}},"text-variable-anchor":{type:"array",value:"enum",values:{center:{},left:{},right:{},top:{},bottom:{},"top-left":{},"top-right":{},"bottom-left":{},"bottom-right":{}},requires:["text-field",{"symbol-placement":["point"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-variable-anchor-offset":{type:"variableAnchorOffsetCollection",requires:["text-field",{"symbol-placement":["point"]}],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-anchor":{type:"enum",values:{center:{},left:{},right:{},top:{},bottom:{},"top-left":{},"top-right":{},"bottom-left":{},"bottom-right":{}},default:"center",requires:["text-field",{"!":"text-variable-anchor"}],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-max-angle":{type:"number",default:45,units:"degrees",requires:["text-field",{"symbol-placement":["line","line-center"]}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-writing-mode":{type:"array",value:"enum",values:{horizontal:{},vertical:{}},requires:["text-field",{"symbol-placement":["point"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-rotate":{type:"number",default:0,period:360,units:"degrees",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-padding":{type:"number",default:2,minimum:0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-keep-upright":{type:"boolean",default:!0,requires:["text-field",{"text-rotation-alignment":"map"},{"symbol-placement":["line","line-center"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-transform":{type:"enum",values:{none:{},uppercase:{},lowercase:{}},default:"none",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-offset":{type:"array",value:"number",units:"ems",length:2,default:[0,0],requires:["text-field",{"!":"text-radial-offset"}],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-allow-overlap":{type:"boolean",default:!1,requires:["text-field",{"!":"text-overlap"}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-overlap":{type:"enum",values:{never:{},always:{},cooperative:{}},requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-ignore-placement":{type:"boolean",default:!1,requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-optional":{type:"boolean",default:!1,requires:["text-field","icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_raster:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},layout_hillshade:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},"layout_color-relief":{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible",expression:{interpolated:!1,parameters:["global-state"]},"property-type":"data-constant"}},filter:{type:"boolean",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},filter_operator:{type:"enum",values:{"==":{},"!=":{},">":{},">=":{},"<":{},"<=":{},in:{},"!in":{},all:{},any:{},none:{},has:{},"!has":{}}},geometry_type:{type:"enum",values:{Point:{},LineString:{},Polygon:{}}},function:{expression:{type:"expression"},stops:{type:"array",value:"function_stop"},base:{type:"number",default:1,minimum:0},property:{type:"string",default:"$zoom"},type:{type:"enum",values:{identity:{},exponential:{},interval:{},categorical:{}},default:"exponential"},colorSpace:{type:"enum",values:{rgb:{},lab:{},hcl:{}},default:"rgb"},default:{type:"*",required:!1}},function_stop:{type:"array",minimum:0,maximum:24,value:["number","color"],length:2},expression:{type:"array",value:"expression_name",minimum:1},light:{anchor:{type:"enum",default:"viewport",values:{map:{},viewport:{}},"property-type":"data-constant",transition:!1,expression:{interpolated:!1,parameters:["zoom"]}},position:{type:"array",default:[1.15,210,30],length:3,value:"number","property-type":"data-constant",transition:!0,expression:{interpolated:!0,parameters:["zoom"]}},color:{type:"color","property-type":"data-constant",default:"#ffffff",expression:{interpolated:!0,parameters:["zoom"]},transition:!0},intensity:{type:"number","property-type":"data-constant",default:.5,minimum:0,maximum:1,expression:{interpolated:!0,parameters:["zoom"]},transition:!0}},sky:{"sky-color":{type:"color","property-type":"data-constant",default:"#88C6FC",expression:{interpolated:!0,parameters:["zoom"]},transition:!0},"horizon-color":{type:"color","property-type":"data-constant",default:"#ffffff",expression:{interpolated:!0,parameters:["zoom"]},transition:!0},"fog-color":{type:"color","property-type":"data-constant",default:"#ffffff",expression:{interpolated:!0,parameters:["zoom"]},transition:!0},"fog-ground-blend":{type:"number","property-type":"data-constant",default:.5,minimum:0,maximum:1,expression:{interpolated:!0,parameters:["zoom"]},transition:!0},"horizon-fog-blend":{type:"number","property-type":"data-constant",default:.8,minimum:0,maximum:1,expression:{interpolated:!0,parameters:["zoom"]},transition:!0},"sky-horizon-blend":{type:"number","property-type":"data-constant",default:.8,minimum:0,maximum:1,expression:{interpolated:!0,parameters:["zoom"]},transition:!0},"atmosphere-blend":{type:"number","property-type":"data-constant",default:.8,minimum:0,maximum:1,expression:{interpolated:!0,parameters:["zoom"]},transition:!0}},terrain:{source:{type:"string",required:!0},exaggeration:{type:"number",minimum:0,default:1}},projection:{type:{type:"projectionDefinition",default:"mercator","property-type":"data-constant",transition:!1,expression:{interpolated:!0,parameters:["zoom"]}}},paint:["paint_fill","paint_line","paint_circle","paint_heatmap","paint_fill-extrusion","paint_symbol","paint_raster","paint_hillshade","paint_color-relief","paint_background"],paint_fill:{"fill-antialias":{type:"boolean",default:!0,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"fill-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"fill-pattern"}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-outline-color":{type:"color",transition:!0,requires:[{"!":"fill-pattern"},{"fill-antialias":!0}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"fill-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["fill-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"fill-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"}},"paint_fill-extrusion":{"fill-extrusion-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"fill-extrusion-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"fill-extrusion-pattern"}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-extrusion-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"fill-extrusion-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["fill-extrusion-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"fill-extrusion-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"},"fill-extrusion-height":{type:"number",default:0,minimum:0,units:"meters",transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-extrusion-base":{type:"number",default:0,minimum:0,units:"meters",transition:!0,requires:["fill-extrusion-height"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-extrusion-vertical-gradient":{type:"boolean",default:!0,transition:!1,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"}},paint_line:{"line-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"line-pattern"}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"line-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["line-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"line-width":{type:"number",default:1,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-gap-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-offset":{type:"number",default:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-blur":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-dasharray":{type:"array",value:"number",minimum:0,transition:!0,units:"line widths",requires:[{"!":"line-pattern"}],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"},"line-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"},"line-gradient":{type:"color",transition:!1,requires:[{"!":"line-dasharray"},{"!":"line-pattern"},{source:"geojson",has:{lineMetrics:!0}}],expression:{interpolated:!0,parameters:["line-progress"]},"property-type":"color-ramp"}},paint_circle:{"circle-radius":{type:"number",default:5,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-blur":{type:"number",default:0,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"circle-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["circle-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"circle-pitch-scale":{type:"enum",values:{map:{},viewport:{}},default:"map",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"circle-pitch-alignment":{type:"enum",values:{map:{},viewport:{}},default:"viewport",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"circle-stroke-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-stroke-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-stroke-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"}},paint_heatmap:{"heatmap-radius":{type:"number",default:30,minimum:1,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"heatmap-weight":{type:"number",default:1,minimum:0,transition:!1,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"heatmap-intensity":{type:"number",default:1,minimum:0,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"heatmap-color":{type:"color",default:["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",.1,"royalblue",.3,"cyan",.5,"lime",.7,"yellow",1,"red"],transition:!1,expression:{interpolated:!0,parameters:["heatmap-density"]},"property-type":"color-ramp"},"heatmap-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},paint_symbol:{"icon-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-color":{type:"color",default:"#000000",transition:!0,requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-halo-color":{type:"color",default:"rgba(0, 0, 0, 0)",transition:!0,requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-halo-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-halo-blur":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"icon-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["icon-image","icon-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-color":{type:"color",default:"#000000",transition:!0,overridable:!0,requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-halo-color":{type:"color",default:"rgba(0, 0, 0, 0)",transition:!0,requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-halo-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-halo-blur":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["text-field","text-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"}},paint_raster:{"raster-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-hue-rotate":{type:"number",default:0,period:360,transition:!0,units:"degrees",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-brightness-min":{type:"number",default:0,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-brightness-max":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-saturation":{type:"number",default:0,minimum:-1,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-contrast":{type:"number",default:0,minimum:-1,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},resampling:{type:"enum",values:{linear:{},nearest:{}},default:"linear",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"raster-resampling":{type:"enum",values:{linear:{},nearest:{}},default:"linear",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"raster-fade-duration":{type:"number",default:300,minimum:0,transition:!1,units:"milliseconds",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},paint_hillshade:{"hillshade-illumination-direction":{type:"numberArray",default:335,minimum:0,maximum:359,transition:!1,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-illumination-altitude":{type:"numberArray",default:45,minimum:0,maximum:90,transition:!1,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-illumination-anchor":{type:"enum",values:{map:{},viewport:{}},default:"viewport",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-exaggeration":{type:"number",default:.5,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-shadow-color":{type:"colorArray",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-highlight-color":{type:"colorArray",default:"#FFFFFF",transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-accent-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-method":{type:"enum",values:{standard:{},basic:{},combined:{},igor:{},multidirectional:{}},default:"standard",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},resampling:{type:"enum",values:{linear:{},nearest:{}},default:"linear",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"}},"paint_color-relief":{"color-relief-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"color-relief-color":{type:"color",transition:!1,expression:{interpolated:!0,parameters:["elevation"]},"property-type":"color-ramp"},resampling:{type:"enum",values:{linear:{},nearest:{}},default:"linear",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"}},paint_background:{"background-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"background-pattern"}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"background-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"cross-faded"},"background-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},transition:{duration:{type:"number",default:300,minimum:0,units:"milliseconds"},delay:{type:"number",default:0,minimum:0,units:"milliseconds"}},"property-type":{"data-driven":{type:"property-type"},"cross-faded":{type:"property-type"},"cross-faded-data-driven":{type:"property-type"},"color-ramp":{type:"property-type"},"data-constant":{type:"property-type"},constant:{type:"property-type"}},promoteId:{"*":{type:"string"}},interpolation:{type:"array",value:"interpolation_name",minimum:1},interpolation_name:{type:"enum",values:{linear:{syntax:{overloads:[{parameters:[],"output-type":"interpolation"}],parameters:[]}},exponential:{syntax:{overloads:[{parameters:["base"],"output-type":"interpolation"}],parameters:[{name:"base",type:"number literal"}]}},"cubic-bezier":{syntax:{overloads:[{parameters:["x1","y1","x2","y2"],"output-type":"interpolation"}],parameters:[{name:"x1",type:"number literal"},{name:"y1",type:"number literal"},{name:"x2",type:"number literal"},{name:"y2",type:"number literal"}]}}}}};const _t=["type","source","source-layer","minzoom","maxzoom","filter","layout"];function St(t,e){const n={};for(const e in t)"ref"!==e&&(n[e]=t[e]);return _t.forEach((t=>{t in e&&(n[t]=e[t]);})),n}function At(t,e){if(Array.isArray(t)){if(!Array.isArray(e)||t.length!==e.length)return !1;for(let n=0;n`:"value"===t.itemType.kind?"array":`array<${e}>`}return t.kind}const te=[Lt,Ot,$t,Rt,Nt,Ut,Xt,jt,Jt(qt),Yt,Ht,Zt,Wt,Kt];function ee(t,e){if("error"===e.kind)return null;if("array"===t.kind){if("array"===e.kind&&(0===e.N&&"value"===e.itemType.kind||!ee(t.itemType,e.itemType))&&("number"!=typeof t.N||t.N===e.N))return null}else {if(t.kind===e.kind)return null;if("value"===t.kind)for(const t of te)if(!ee(t,e))return null}return `Expected ${Qt(t)} but found ${Qt(e)} instead.`}function ne(t,e){return e.some((e=>e.kind===t.kind))}function re(t,e){return e.some((e=>"null"===e?null===t:"array"===e?Array.isArray(t):"object"===e?t&&!Array.isArray(t)&&"object"==typeof t:e===typeof t))}function ie(t,e){return "array"===t.kind&&"array"===e.kind?t.itemType.kind===e.itemType.kind&&"number"==typeof t.N:t.kind===e.kind}const se=.96422,oe=.82521,ae=4/29,le=6/29,ue=3*le*le,ce=le*le*le,he=Math.PI/180,pe=180/Math.PI;function fe(t){return (t%=360)<0&&(t+=360),t}function de([t,e,n,r]){let i,s;const o=me((.2225045*(t=ye(t))+.7168786*(e=ye(e))+.0606169*(n=ye(n)))/1);t===e&&e===n?i=s=o:(i=me((.4360747*t+.3850649*e+.1430804*n)/se),s=me((.0139322*t+.0971045*e+.7141733*n)/oe));const a=116*o-16;return [a<0?0:a,500*(i-o),200*(o-s),r]}function ye(t){return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function me(t){return t>ce?Math.pow(t,1/3):t/ue+ae}function ge([t,e,n,r]){let i=(t+16)/116,s=isNaN(e)?i:i+e/500,o=isNaN(n)?i:i-n/200;return i=1*ve(i),s=se*ve(s),o=oe*ve(o),[xe(3.1338561*s-1.6168667*i-.4906146*o),xe(-.9787684*s+1.9161415*i+.033454*o),xe(.0719453*s-.2289914*i+1.4052427*o),r]}function xe(t){return (t=t<=.00304?12.92*t:1.055*Math.pow(t,1/2.4)-.055)<0?0:t>1?1:t}function ve(t){return t>le?t*t*t:ue*(t-ae)}const be=Object.hasOwn||function(t,e){return Object.prototype.hasOwnProperty.call(t,e)};function we(t,e){return be(t,e)?t[e]:void 0}function _e(t){return parseInt(t.padEnd(2,t),16)/255}function Se(t,e){return Ae(e?t/100:t,0,1)}function Ae(t,e,n){return Math.min(Math.max(e,t),n)}function Me(t){return !t.some(Number.isNaN)}const ke={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]};function Ie(t,e,n){return t+n*(e-t)}function Ee(t,e,n){return t.map(((t,r)=>Ie(t,e[r],n)))}class Te{constructor(t,e,n,r=1,i=!0){this.r=t,this.g=e,this.b=n,this.a=r,i||(this.r*=r,this.g*=r,this.b*=r,r||this.overwriteGetter("rgb",[t,e,n,r]));}static parse(t){if(t instanceof Te)return t;if("string"!=typeof t)return;const e=function(t){if("transparent"===(t=t.toLowerCase().trim()))return [0,0,0,0];const e=we(ke,t);if(e){const[t,n,r]=e;return [t/255,n/255,r/255,1]}if(t.startsWith("#")&&/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(t)){const e=t.length<6?1:2;let n=1;return [_e(t.slice(n,n+=e)),_e(t.slice(n,n+=e)),_e(t.slice(n,n+=e)),_e(t.slice(n,n+e)||"ff")]}if(t.startsWith("rgb")){const e=t.match(/^rgba?\(\s*([\de.+-]+)(%)?(?:\s+|\s*(,)\s*)([\de.+-]+)(%)?(?:\s+|\s*(,)\s*)([\de.+-]+)(%)?(?:\s*([,\/])\s*([\de.+-]+)(%)?)?\s*\)$/);if(e){const[t,n,r,i,s,o,a,l,u,c,h,p]=e,f=[i||" ",a||" ",c].join("");if(" "===f||" /"===f||",,"===f||",,,"===f){const t=[r,o,u].join(""),e="%%%"===t?100:""===t?255:0;if(e){const t=[Ae(+n/e,0,1),Ae(+s/e,0,1),Ae(+l/e,0,1),h?Se(+h,p):1];if(Me(t))return t}}return}}const n=t.match(/^hsla?\(\s*([\de.+-]+)(?:deg)?(?:\s+|\s*(,)\s*)([\de.+-]+)%(?:\s+|\s*(,)\s*)([\de.+-]+)%(?:\s*([,\/])\s*([\de.+-]+)(%)?)?\s*\)$/);if(n){const[t,e,r,i,s,o,a,l,u]=n,c=[r||" ",s||" ",a].join("");if(" "===c||" /"===c||",,"===c||",,,"===c){const t=[+e,Ae(+i,0,100),Ae(+o,0,100),l?Se(+l,u):1];if(Me(t))return function([t,e,n,r]){function i(r){const i=(r+t/30)%12,s=e*Math.min(n,1-n);return n-s*Math.max(-1,Math.min(i-3,9-i,1))}return t=fe(t),e/=100,n/=100,[i(0),i(8),i(4),r]}(t)}}}(t);return e?new Te(...e,!1):void 0}get rgb(){const{r:t,g:e,b:n,a:r}=this,i=r||1/0;return this.overwriteGetter("rgb",[t/i,e/i,n/i,r])}get hcl(){return this.overwriteGetter("hcl",function(t){const[e,n,r,i]=de(t),s=Math.sqrt(n*n+r*r);return [Math.round(1e4*s)?fe(Math.atan2(r,n)*pe):NaN,s,e,i]}(this.rgb))}get lab(){return this.overwriteGetter("lab",de(this.rgb))}overwriteGetter(t,e){return Object.defineProperty(this,t,{value:e}),e}toString(){const[t,e,n,r]=this.rgb;return `rgba(${[t,e,n].map((t=>Math.round(255*t))).join(",")},${r})`}static interpolate(t,e,n,r="rgb"){switch(r){case "rgb":{const[r,i,s,o]=Ee(t.rgb,e.rgb,n);return new Te(r,i,s,o,!1)}case "hcl":{const[r,i,s,o]=t.hcl,[a,l,u,c]=e.hcl;let h,p;if(isNaN(r)||isNaN(a))isNaN(r)?isNaN(a)?h=NaN:(h=a,1!==s&&0!==s||(p=l)):(h=r,1!==u&&0!==u||(p=i));else {let t=a-r;a>r&&t>180?t-=360:a180&&(t+=360),h=r+n*t;}const[f,d,y,m]=function([t,e,n,r]){return t=isNaN(t)?0:t*he,ge([n,Math.cos(t)*e,Math.sin(t)*e,r])}([h,null!=p?p:Ie(i,l,n),Ie(s,u,n),Ie(o,c,n)]);return new Te(f,d,y,m,!1)}case "lab":{const[r,i,s,o]=ge(Ee(t.lab,e.lab,n));return new Te(r,i,s,o,!1)}}}}Te.black=new Te(0,0,0,1),Te.white=new Te(1,1,1,1),Te.transparent=new Te(0,0,0,0),Te.red=new Te(1,0,0,1);class Fe{constructor(t,e,n){this.sensitivity=t?e?"variant":"case":e?"accent":"base",this.locale=n,this.collator=new Intl.Collator(this.locale?this.locale:[],{sensitivity:this.sensitivity,usage:"search"});}compare(t,e){return this.collator.compare(t,e)}resolvedLocale(){return new Intl.Collator(this.locale?this.locale:[]).resolvedOptions().locale}}const Pe=["bottom","center","top"];class De{constructor(t,e,n,r,i,s){this.text=t,this.image=e,this.scale=n,this.fontStack=r,this.textColor=i,this.verticalAlign=s;}}class ze{constructor(t){this.sections=t;}static fromString(t){return new ze([new De(t,null,null,null,null,null)])}isEmpty(){return 0===this.sections.length||!this.sections.some((t=>0!==t.text.length||t.image&&0!==t.image.name.length))}static factory(t){return t instanceof ze?t:ze.fromString(t)}toString(){return 0===this.sections.length?"":this.sections.map((t=>t.text)).join("")}}class Be{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Be)return t;if("number"==typeof t)return new Be([t,t,t,t]);if(Array.isArray(t)&&!(t.length<1||t.length>4)){for(const e of t)if("number"!=typeof e)return;switch(t.length){case 1:t=[t[0],t[0],t[0],t[0]];break;case 2:t=[t[0],t[1],t[0],t[1]];break;case 3:t=[t[0],t[1],t[2],t[1]];}return new Be(t)}}toString(){return JSON.stringify(this.values)}static interpolate(t,e,n){return new Be(Ee(t.values,e.values,n))}}class Ce{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Ce)return t;if("number"==typeof t)return new Ce([t]);if(Array.isArray(t)){for(const e of t)if("number"!=typeof e)return;return new Ce(t)}}toString(){return JSON.stringify(this.values)}static interpolate(t,e,n){return new Ce(Ee(t.values,e.values,n))}}class Ve{constructor(t){this.values=t.slice();}static parse(t){if(t instanceof Ve)return t;if("string"==typeof t){const e=Te.parse(t);if(!e)return;return new Ve([e])}if(!Array.isArray(t))return;const e=[];for(const n of t){if("string"!=typeof n)return;const t=Te.parse(n);if(!t)return;e.push(t);}return new Ve(e)}toString(){return JSON.stringify(this.values)}static interpolate(t,e,n,r="rgb"){const i=[];if(t.values.length!=e.values.length)throw new Error(`colorArray: Arrays have mismatched length (${t.values.length} vs. ${e.values.length}), cannot interpolate.`);for(let s=0;s=0&&t<=255&&"number"==typeof e&&e>=0&&e<=255&&"number"==typeof n&&n>=0&&n<=255?void 0===r||"number"==typeof r&&r>=0&&r<=1?null:`Invalid rgba value [${[t,e,n,r].join(", ")}]: 'a' must be between 0 and 1.`:`Invalid rgba value [${("number"==typeof r?[t,e,n,r]:[t,e,n]).join(", ")}]: 'r', 'g', and 'b' must be between 0 and 255.`}function je(t){if(null===t||"string"==typeof t||"boolean"==typeof t||"number"==typeof t||t instanceof Ne||t instanceof Te||t instanceof Fe||t instanceof ze||t instanceof Be||t instanceof Ce||t instanceof Ve||t instanceof $e||t instanceof Re)return !0;if(Array.isArray(t)){for(const e of t)if(!je(e))return !1;return !0}if("object"==typeof t){for(const e in t)if(!je(t[e]))return !1;return !0}return !1}function qe(t){if(null===t)return Lt;if("string"==typeof t)return $t;if("boolean"==typeof t)return Rt;if("number"==typeof t)return Ot;if(t instanceof Te)return Nt;if(t instanceof Ne)return Ut;if(t instanceof Fe)return Gt;if(t instanceof ze)return Xt;if(t instanceof Be)return Yt;if(t instanceof Ce)return Ht;if(t instanceof Ve)return Zt;if(t instanceof $e)return Kt;if(t instanceof Re)return Wt;if(Array.isArray(t)){const e=t.length;let n;for(const e of t){const t=qe(e);if(n){if(n===t)continue;n=qt;break}n=t;}return Jt(n||qt,e)}return jt}function Ge(t){const e=typeof t;return null===t?"":"string"===e||"number"===e||"boolean"===e?String(t):t instanceof Te||t instanceof Ne||t instanceof ze||t instanceof Be||t instanceof Ce||t instanceof Ve||t instanceof $e||t instanceof Re?t.toString():JSON.stringify(t)}class Xe{constructor(t,e){this.type=t,this.value=e;}static parse(t,e){if(2!==t.length)return e.error(`'literal' expression requires exactly one argument, but found ${t.length-1} instead.`);if(!je(t[1]))return e.error("invalid value");const n=t[1];let r=qe(n);const i=e.expectedType;return "array"!==r.kind||0!==r.N||!i||"array"!==i.kind||"number"==typeof i.N&&0!==i.N||(r=i),new Xe(r,n)}evaluate(){return this.value}eachChild(){}outputDefined(){return !0}}const Ye={string:$t,number:Ot,boolean:Rt,object:jt};class Ze{constructor(t,e){this.type=t,this.args=e;}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");let n,r=1;const i=t[0];if("array"===i){let i,s;if(t.length>2){const n=t[1];if("string"!=typeof n||!(n in Ye)||"object"===n)return e.error('The item type argument of "array" must be one of string, number, boolean',1);i=Ye[n],r++;}else i=qt;if(t.length>3){if(null!==t[2]&&("number"!=typeof t[2]||t[2]<0||t[2]!==Math.floor(t[2])))return e.error('The length argument to "array" must be a positive integer literal',2);s=t[2],r++;}n=Jt(i,s);}else {if(!Ye[i])throw new Error(`Types doesn't contain name = ${i}`);n=Ye[i];}const s=[];for(;rt.outputDefined()))}}const He={"to-boolean":Rt,"to-color":Nt,"to-number":Ot,"to-string":$t};class We{constructor(t,e){this.type=t,this.args=e;}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");const n=t[0];if(!He[n])throw new Error(`Can't parse ${n} as it is not part of the known types`);if(("to-boolean"===n||"to-string"===n)&&2!==t.length)return e.error("Expected one argument.");const r=He[n],i=[];for(let n=1;n4?`Invalid rgba value ${JSON.stringify(e)}: expected an array containing either three or four numeric values.`:Ue(e[0],e[1],e[2],e[3]),!n))return new Te(e[0]/255,e[1]/255,e[2]/255,e[3])}throw new Le(n||`Could not parse color from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}case "padding":{let e;for(const n of this.args){e=n.evaluate(t);const r=Be.parse(e);if(r)return r}throw new Le(`Could not parse padding from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}case "numberArray":{let e;for(const n of this.args){e=n.evaluate(t);const r=Ce.parse(e);if(r)return r}throw new Le(`Could not parse numberArray from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}case "colorArray":{let e;for(const n of this.args){e=n.evaluate(t);const r=Ve.parse(e);if(r)return r}throw new Le(`Could not parse colorArray from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}case "variableAnchorOffsetCollection":{let e;for(const n of this.args){e=n.evaluate(t);const r=$e.parse(e);if(r)return r}throw new Le(`Could not parse variableAnchorOffsetCollection from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}case "number":{let e=null;for(const n of this.args){if(e=n.evaluate(t),null===e)return 0;const r=Number(e);if(!isNaN(r))return r}throw new Le(`Could not convert ${JSON.stringify(e)} to number.`)}case "formatted":return ze.fromString(Ge(this.args[0].evaluate(t)));case "resolvedImage":return Re.fromString(Ge(this.args[0].evaluate(t)));case "projectionDefinition":return this.args[0].evaluate(t);default:return Ge(this.args[0].evaluate(t))}}eachChild(t){this.args.forEach(t);}outputDefined(){return this.args.every((t=>t.outputDefined()))}}const Ke=["Unknown","Point","LineString","Polygon"];class Je{constructor(){this.globals=null,this.feature=null,this.featureState=null,this.formattedSection=null,this._parseColorCache=new Map,this.availableImages=null,this.canonical=null;}id(){return this.feature&&"id"in this.feature?this.feature.id:null}geometryType(){return this.feature?"number"==typeof this.feature.type?Ke[this.feature.type]:this.feature.type:null}geometry(){return this.feature&&"geometry"in this.feature?this.feature.geometry:null}canonicalID(){return this.canonical}properties(){return this.feature&&this.feature.properties||{}}parseColor(t){let e=this._parseColorCache.get(t);return e||(e=Te.parse(t),this._parseColorCache.set(t,e)),e}}class Qe{constructor(t,e,n=[],r,i=new Vt,s=[]){this.registry=t,this.path=n,this.key=n.map((t=>`[${t}]`)).join(""),this.scope=i,this.errors=s,this.expectedType=r,this._isConstant=e;}parse(t,e,n,r,i={}){return e?this.concat(e,n,r)._parse(t,i):this._parse(t,i)}_parse(t,e){function n(t,e,n){return "assert"===n?new Ze(e,[t]):"coerce"===n?new We(e,[t]):t}if(null!==t&&"string"!=typeof t&&"boolean"!=typeof t&&"number"!=typeof t||(t=["literal",t]),Array.isArray(t)){if(0===t.length)return this.error('Expected an array with at least one element. If you wanted a literal array, use ["literal", []].');const r=t[0];if("string"!=typeof r)return this.error(`Expression name must be a string, but found ${typeof r} instead. If you wanted a literal array, use ["literal", [...]].`,0),null;const i=this.registry[r];if(i){let r=i.parse(t,this);if(!r)return null;if(this.expectedType){const t=this.expectedType,i=r.type;if("string"!==t.kind&&"number"!==t.kind&&"boolean"!==t.kind&&"object"!==t.kind&&"array"!==t.kind||"value"!==i.kind){if("projectionDefinition"===t.kind&&["string","array"].includes(i.kind)||["color","formatted","resolvedImage"].includes(t.kind)&&["value","string"].includes(i.kind)||["padding","numberArray"].includes(t.kind)&&["value","number","array"].includes(i.kind)||"colorArray"===t.kind&&["value","string","array"].includes(i.kind)||"variableAnchorOffsetCollection"===t.kind&&["value","array"].includes(i.kind))r=n(r,t,e.typeAnnotation||"coerce");else if(this.checkSubtype(t,i))return null}else r=n(r,t,e.typeAnnotation||"assert");}if(!(r instanceof Xe)&&"resolvedImage"!==r.type.kind&&this._isConstant(r)){const t=new Je;try{r=new Xe(r.type,r.evaluate(t));}catch(t){return this.error(t.message),null}}return r}return this.error(`Unknown expression "${r}". If you wanted a literal array, use ["literal", [...]].`,0)}return this.error(void 0===t?"'undefined' value invalid. Use null instead.":"object"==typeof t?'Bare objects invalid. Use ["literal", {...}] instead.':`Expected an array, but found ${typeof t} instead.`)}concat(t,e,n){const r="number"==typeof t?this.path.concat(t):this.path,i=n?this.scope.concat(n):this.scope;return new Qe(this.registry,this._isConstant,r,e||null,i,this.errors)}error(t,...e){const n=`${this.key}${e.map((t=>`[${t}]`)).join("")}`;this.errors.push(new Ct(n,t));}checkSubtype(t,e){const n=ee(t,e);return n&&this.error(n),n}}class tn{constructor(t,e){this.type=e.type,this.bindings=[].concat(t),this.result=e;}evaluate(t){return this.result.evaluate(t)}eachChild(t){for(const e of this.bindings)t(e[1]);t(this.result);}static parse(t,e){if(t.length<4)return e.error(`Expected at least 3 arguments, but found ${t.length-1} instead.`);const n=[];for(let r=1;r=n.length)throw new Le(`Array index out of bounds: ${e} > ${n.length-1}.`);if(e!==Math.floor(e))throw new Le(`Array index must be an integer, but found ${e} instead.`);return n[e]}eachChild(t){t(this.index),t(this.input);}outputDefined(){return !1}}class rn{constructor(t,e){this.type=Rt,this.needle=t,this.haystack=e;}static parse(t,e){if(3!==t.length)return e.error(`Expected 2 arguments, but found ${t.length-1} instead.`);const n=e.parse(t[1],1,qt),r=e.parse(t[2],2,qt);return n&&r?ne(n.type,[Rt,$t,Ot,Lt,qt])?new rn(n,r):e.error(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(n.type)} instead`):null}evaluate(t){const e=this.needle.evaluate(t),n=this.haystack.evaluate(t);if(!n)return !1;if(!re(e,["boolean","string","number","null"]))throw new Le(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(qe(e))} instead.`);if(!re(n,["string","array"]))throw new Le(`Expected second argument to be of type array or string, but found ${Qt(qe(n))} instead.`);return n.indexOf(e)>=0}eachChild(t){t(this.needle),t(this.haystack);}outputDefined(){return !0}}class sn{constructor(t,e,n){this.type=Ot,this.needle=t,this.haystack=e,this.fromIndex=n;}static parse(t,e){if(t.length<=2||t.length>=5)return e.error(`Expected 2 or 3 arguments, but found ${t.length-1} instead.`);const n=e.parse(t[1],1,qt),r=e.parse(t[2],2,qt);if(!n||!r)return null;if(!ne(n.type,[Rt,$t,Ot,Lt,qt]))return e.error(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(n.type)} instead`);if(4===t.length){const i=e.parse(t[3],3,Ot);return i?new sn(n,r,i):null}return new sn(n,r)}evaluate(t){const e=this.needle.evaluate(t),n=this.haystack.evaluate(t);if(!re(e,["boolean","string","number","null"]))throw new Le(`Expected first argument to be of type boolean, string, number or null, but found ${Qt(qe(e))} instead.`);let r;if(this.fromIndex&&(r=this.fromIndex.evaluate(t)),re(n,["string"])){const t=n.indexOf(e,r);return -1===t?-1:[...n.slice(0,t)].length}if(re(n,["array"]))return n.indexOf(e,r);throw new Le(`Expected second argument to be of type array or string, but found ${Qt(qe(n))} instead.`)}eachChild(t){t(this.needle),t(this.haystack),this.fromIndex&&t(this.fromIndex);}outputDefined(){return !1}}class on{constructor(t,e,n,r,i,s){this.inputType=t,this.type=e,this.input=n,this.cases=r,this.outputs=i,this.otherwise=s;}static parse(t,e){if(t.length<5)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if(t.length%2!=1)return e.error("Expected an even number of arguments.");let n,r;e.expectedType&&"value"!==e.expectedType.kind&&(r=e.expectedType);const i={},s=[];for(let o=2;oNumber.MAX_SAFE_INTEGER)return u.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`);if("number"==typeof t&&Math.floor(t)!==t)return u.error("Numeric branch labels must be integer values.");if(n){if(u.checkSubtype(n,qe(t)))return null}else n=qe(t);if(void 0!==i[String(t)])return u.error("Branch labels must be unique.");i[String(t)]=s.length;}const c=e.parse(l,o,r);if(!c)return null;r=r||c.type,s.push(c);}const o=e.parse(t[1],1,qt);if(!o)return null;const a=e.parse(t[t.length-1],t.length-1,r);return a?"value"!==o.type.kind&&e.concat(1).checkSubtype(n,o.type)?null:new on(n,r,o,i,s,a):null}evaluate(t){const e=this.input.evaluate(t);return (qe(e)===this.inputType&&this.outputs[this.cases[e]]||this.otherwise).evaluate(t)}eachChild(t){t(this.input),this.outputs.forEach(t),t(this.otherwise);}outputDefined(){return this.outputs.every((t=>t.outputDefined()))&&this.otherwise.outputDefined()}}class an{constructor(t,e,n){this.type=t,this.branches=e,this.otherwise=n;}static parse(t,e){if(t.length<4)return e.error(`Expected at least 3 arguments, but found only ${t.length-1}.`);if(t.length%2!=0)return e.error("Expected an odd number of arguments.");let n;e.expectedType&&"value"!==e.expectedType.kind&&(n=e.expectedType);const r=[];for(let i=1;ie.outputDefined()))&&this.otherwise.outputDefined()}}class ln{constructor(t,e,n,r){this.type=t,this.input=e,this.beginIndex=n,this.endIndex=r;}static parse(t,e){if(t.length<=2||t.length>=5)return e.error(`Expected 2 or 3 arguments, but found ${t.length-1} instead.`);const n=e.parse(t[1],1,qt),r=e.parse(t[2],2,Ot);if(!n||!r)return null;if(!ne(n.type,[Jt(qt),$t,qt]))return e.error(`Expected first argument to be of type array or string, but found ${Qt(n.type)} instead`);if(4===t.length){const i=e.parse(t[3],3,Ot);return i?new ln(n.type,n,r,i):null}return new ln(n.type,n,r)}evaluate(t){const e=this.input.evaluate(t),n=this.beginIndex.evaluate(t);let r;if(this.endIndex&&(r=this.endIndex.evaluate(t)),re(e,["string"]))return [...e].slice(n,r).join("");if(re(e,["array"]))return e.slice(n,r);throw new Le(`Expected first argument to be of type array or string, but found ${Qt(qe(e))} instead.`)}eachChild(t){t(this.input),t(this.beginIndex),this.endIndex&&t(this.endIndex);}outputDefined(){return !1}}function un(t,e){const n=t.length-1;let r,i,s=0,o=n,a=0;for(;s<=o;)if(a=Math.floor((s+o)/2),r=t[a],i=t[a+1],r<=e){if(a===n||ee))throw new Le("Input is not a number.");o=a-1;}return 0}class cn{constructor(t,e,n){this.type=t,this.input=e,this.labels=[],this.outputs=[];for(const[t,e]of n)this.labels.push(t),this.outputs.push(e);}static parse(t,e){if(t.length-1<4)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if((t.length-1)%2!=0)return e.error("Expected an even number of arguments.");const n=e.parse(t[1],1,Ot);if(!n)return null;const r=[];let i=null;e.expectedType&&"value"!==e.expectedType.kind&&(i=e.expectedType);for(let n=1;n=s)return e.error('Input/output pairs for "step" expressions must be arranged with input values in strictly ascending order.',a);const u=e.parse(o,l,i);if(!u)return null;i=i||u.type,r.push([s,u]);}return new cn(i,n,r)}evaluate(t){const e=this.labels,n=this.outputs;if(1===e.length)return n[0].evaluate(t);const r=this.input.evaluate(t);if(r<=e[0])return n[0].evaluate(t);const i=e.length;return r>=e[i-1]?n[i-1].evaluate(t):n[un(e,r)].evaluate(t)}eachChild(t){t(this.input);for(const e of this.outputs)t(e);}outputDefined(){return this.outputs.every((t=>t.outputDefined()))}}function hn(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var pn,fn,dn=function(){if(fn)return pn;function t(t,e,n,r){this.cx=3*t,this.bx=3*(n-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(r-e)-this.cy,this.ay=1-this.cy-this.by,this.p1x=t,this.p1y=e,this.p2x=n,this.p2y=r;}return fn=1,pn=t,t.prototype={sampleCurveX:function(t){return ((this.ax*t+this.bx)*t+this.cx)*t},sampleCurveY:function(t){return ((this.ay*t+this.by)*t+this.cy)*t},sampleCurveDerivativeX:function(t){return (3*this.ax*t+2*this.bx)*t+this.cx},solveCurveX:function(t,e){if(void 0===e&&(e=1e-6),t<0)return 0;if(t>1)return 1;for(var n=t,r=0;r<8;r++){var i=this.sampleCurveX(n)-t;if(Math.abs(i)i?o=n:a=n,n=.5*(a-o)+o;return n},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}},pn}(),yn=hn(dn);class mn{constructor(t,e,n,r,i){this.type=t,this.operator=e,this.interpolation=n,this.input=r,this.labels=[],this.outputs=[];for(const[t,e]of i)this.labels.push(t),this.outputs.push(e);}static interpolationFactor(t,e,n,r){let i=0;if("exponential"===t.name)i=gn(e,t.base,n,r);else if("linear"===t.name)i=gn(e,1,n,r);else if("cubic-bezier"===t.name){const s=t.controlPoints;i=new yn(s[0],s[1],s[2],s[3]).solve(gn(e,1,n,r));}return i}static parse(t,e){let[n,r,i,...s]=t;if(!Array.isArray(r)||0===r.length)return e.error("Expected an interpolation type expression.",1);if("linear"===r[0])r={name:"linear"};else if("exponential"===r[0]){const t=r[1];if("number"!=typeof t)return e.error("Exponential interpolation requires a numeric base.",1,1);r={name:"exponential",base:t};}else {if("cubic-bezier"!==r[0])return e.error(`Unknown interpolation type ${String(r[0])}`,1,0);{const t=r.slice(1);if(4!==t.length||t.some((t=>"number"!=typeof t||t<0||t>1)))return e.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.",1);r={name:"cubic-bezier",controlPoints:t};}}if(t.length-1<4)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if((t.length-1)%2!=0)return e.error("Expected an even number of arguments.");if(i=e.parse(i,2,Ot),!i)return null;const o=[];let a=null;"interpolate-hcl"!==n&&"interpolate-lab"!==n||e.expectedType==Zt?e.expectedType&&"value"!==e.expectedType.kind&&(a=e.expectedType):a=Nt;for(let t=0;t=n)return e.error('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.',i);const u=e.parse(r,l,a);if(!u)return null;a=a||u.type,o.push([n,u]);}return ie(a,Ot)||ie(a,Ut)||ie(a,Nt)||ie(a,Yt)||ie(a,Ht)||ie(a,Zt)||ie(a,Kt)||ie(a,Jt(Ot))?new mn(a,n,r,i,o):e.error(`Type ${Qt(a)} is not interpolatable.`)}evaluate(t){const e=this.labels,n=this.outputs;if(1===e.length)return n[0].evaluate(t);const r=this.input.evaluate(t);if(r<=e[0])return n[0].evaluate(t);const i=e.length;if(r>=e[i-1])return n[i-1].evaluate(t);const s=un(e,r),o=mn.interpolationFactor(this.interpolation,r,e[s],e[s+1]),a=n[s].evaluate(t),l=n[s+1].evaluate(t);switch(this.operator){case "interpolate":switch(this.type.kind){case "number":return Ie(a,l,o);case "color":return Te.interpolate(a,l,o);case "padding":return Be.interpolate(a,l,o);case "colorArray":return Ve.interpolate(a,l,o);case "numberArray":return Ce.interpolate(a,l,o);case "variableAnchorOffsetCollection":return $e.interpolate(a,l,o);case "array":return Ee(a,l,o);case "projectionDefinition":return Ne.interpolate(a,l,o)}case "interpolate-hcl":switch(this.type.kind){case "color":return Te.interpolate(a,l,o,"hcl");case "colorArray":return Ve.interpolate(a,l,o,"hcl")}case "interpolate-lab":switch(this.type.kind){case "color":return Te.interpolate(a,l,o,"lab");case "colorArray":return Ve.interpolate(a,l,o,"lab")}}}eachChild(t){t(this.input);for(const e of this.outputs)t(e);}outputDefined(){return this.outputs.every((t=>t.outputDefined()))}}function gn(t,e,n,r){const i=r-n,s=t-n;return 0===i?0:1===e?s/i:(Math.pow(e,s)-1)/(Math.pow(e,i)-1)}const xn={color:Te.interpolate,number:Ie,padding:Be.interpolate,numberArray:Ce.interpolate,colorArray:Ve.interpolate,variableAnchorOffsetCollection:$e.interpolate,array:Ee};class vn{constructor(t,e){this.type=t,this.args=e;}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");let n=null;const r=e.expectedType;r&&"value"!==r.kind&&(n=r);const i=[];for(const r of t.slice(1)){const t=e.parse(r,1+i.length,n,void 0,{typeAnnotation:"omit"});if(!t)return null;n=n||t.type,i.push(t);}if(!n)throw new Error("No output type");const s=r&&i.some((t=>ee(r,t.type)));return new vn(s?qt:n,i)}evaluate(t){let e,n=null,r=0;for(const i of this.args)if(r++,n=i.evaluate(t),n&&n instanceof Re&&!n.available&&(e||(e=n.name),n=null,r===this.args.length&&(n=e)),null!==n)break;return n}eachChild(t){this.args.forEach(t);}outputDefined(){return this.args.every((t=>t.outputDefined()))}}function bn(t,e){return "=="===t||"!="===t?"boolean"===e.kind||"string"===e.kind||"number"===e.kind||"null"===e.kind||"value"===e.kind:"string"===e.kind||"number"===e.kind||"value"===e.kind}function wn(t,e,n,r){return 0===r.compare(e,n)}function _n(t,e,n){const r="=="!==t&&"!="!==t;return class i{constructor(t,e,n){this.type=Rt,this.lhs=t,this.rhs=e,this.collator=n,this.hasUntypedArgument="value"===t.type.kind||"value"===e.type.kind;}static parse(t,e){if(3!==t.length&&4!==t.length)return e.error("Expected two or three arguments.");const n=t[0];let s=e.parse(t[1],1,qt);if(!s)return null;if(!bn(n,s.type))return e.concat(1).error(`"${n}" comparisons are not supported for type '${Qt(s.type)}'.`);let o=e.parse(t[2],2,qt);if(!o)return null;if(!bn(n,o.type))return e.concat(2).error(`"${n}" comparisons are not supported for type '${Qt(o.type)}'.`);if(s.type.kind!==o.type.kind&&"value"!==s.type.kind&&"value"!==o.type.kind)return e.error(`Cannot compare types '${Qt(s.type)}' and '${Qt(o.type)}'.`);r&&("value"===s.type.kind&&"value"!==o.type.kind?s=new Ze(o.type,[s]):"value"!==s.type.kind&&"value"===o.type.kind&&(o=new Ze(s.type,[o])));let a=null;if(4===t.length){if("string"!==s.type.kind&&"string"!==o.type.kind&&"value"!==s.type.kind&&"value"!==o.type.kind)return e.error("Cannot use collator to compare non-string types.");if(a=e.parse(t[3],3,Gt),!a)return null}return new i(s,o,a)}evaluate(i){const s=this.lhs.evaluate(i),o=this.rhs.evaluate(i);if(r&&this.hasUntypedArgument){const e=qe(s),n=qe(o);if(e.kind!==n.kind||"string"!==e.kind&&"number"!==e.kind)throw new Le(`Expected arguments for "${t}" to be (string, string) or (number, number), but found (${e.kind}, ${n.kind}) instead.`)}if(this.collator&&!r&&this.hasUntypedArgument){const t=qe(s),n=qe(o);if("string"!==t.kind||"string"!==n.kind)return e(i,s,o)}return this.collator?n(i,s,o,this.collator.evaluate(i)):e(i,s,o)}eachChild(t){t(this.lhs),t(this.rhs),this.collator&&t(this.collator);}outputDefined(){return !0}}}const Sn=_n("==",(function(t,e,n){return e===n}),wn),An=_n("!=",(function(t,e,n){return e!==n}),(function(t,e,n,r){return !wn(0,e,n,r)})),Mn=_n("<",(function(t,e,n){return e",(function(t,e,n){return e>n}),(function(t,e,n,r){return r.compare(e,n)>0})),In=_n("<=",(function(t,e,n){return e<=n}),(function(t,e,n,r){return r.compare(e,n)<=0})),En=_n(">=",(function(t,e,n){return e>=n}),(function(t,e,n,r){return r.compare(e,n)>=0}));class Tn{constructor(t,e,n){this.type=Gt,this.locale=n,this.caseSensitive=t,this.diacriticSensitive=e;}static parse(t,e){if(2!==t.length)return e.error("Expected one argument.");const n=t[1];if("object"!=typeof n||Array.isArray(n))return e.error("Collator options argument must be an object.");const r=e.parse(void 0!==n["case-sensitive"]&&n["case-sensitive"],1,Rt);if(!r)return null;const i=e.parse(void 0!==n["diacritic-sensitive"]&&n["diacritic-sensitive"],1,Rt);if(!i)return null;let s=null;return n.locale&&(s=e.parse(n.locale,1,$t),!s)?null:new Tn(r,i,s)}evaluate(t){return new Fe(this.caseSensitive.evaluate(t),this.diacriticSensitive.evaluate(t),this.locale?this.locale.evaluate(t):null)}eachChild(t){t(this.caseSensitive),t(this.diacriticSensitive),this.locale&&t(this.locale);}outputDefined(){return !1}}class Fn{constructor(t,e,n,r,i,s){this.type=$t,this.number=t,this.locale=e,this.currency=n,this.unit=r,this.minFractionDigits=i,this.maxFractionDigits=s;}static parse(t,e){if(3!==t.length)return e.error("Expected two arguments.");const n=e.parse(t[1],1,Ot);if(!n)return null;const r=t[2];if("object"!=typeof r||Array.isArray(r))return e.error("NumberFormat options argument must be an object.");let i=null;if(r.locale&&(i=e.parse(r.locale,1,$t),!i))return null;let s=null;if(r.currency&&(s=e.parse(r.currency,1,$t),!s))return null;let o=null;if(r.unit&&(o=e.parse(r.unit,1,$t),!o))return null;if(s&&o)return e.error("NumberFormat options `currency` and `unit` are mutually exclusive");let a=null;if(r["min-fraction-digits"]&&(a=e.parse(r["min-fraction-digits"],1,Ot),!a))return null;let l=null;return r["max-fraction-digits"]&&(l=e.parse(r["max-fraction-digits"],1,Ot),!l)?null:new Fn(n,i,s,o,a,l)}evaluate(t){return new Intl.NumberFormat(this.locale?this.locale.evaluate(t):[],{style:this.currency?"currency":this.unit?"unit":"decimal",currency:this.currency?this.currency.evaluate(t):void 0,unit:this.unit?this.unit.evaluate(t):void 0,minimumFractionDigits:this.minFractionDigits?this.minFractionDigits.evaluate(t):void 0,maximumFractionDigits:this.maxFractionDigits?this.maxFractionDigits.evaluate(t):void 0}).format(this.number.evaluate(t))}eachChild(t){t(this.number),this.locale&&t(this.locale),this.currency&&t(this.currency),this.unit&&t(this.unit),this.minFractionDigits&&t(this.minFractionDigits),this.maxFractionDigits&&t(this.maxFractionDigits);}outputDefined(){return !1}}class Pn{constructor(t){this.type=Xt,this.sections=t;}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");const n=t[1];if(!Array.isArray(n)&&"object"==typeof n)return e.error("First argument must be an image or text section.");const r=[];let i=!1;for(let n=1;n<=t.length-1;++n){const s=t[n];if(i&&"object"==typeof s&&!Array.isArray(s)){i=!1;let t=null;if(s["font-scale"]&&(t=e.parse(s["font-scale"],1,Ot),!t))return null;let n=null;if(s["text-font"]&&(n=e.parse(s["text-font"],1,Jt($t)),!n))return null;let o=null;if(s["text-color"]&&(o=e.parse(s["text-color"],1,Nt),!o))return null;let a=null;if(s["vertical-align"]){if("string"==typeof s["vertical-align"]&&!Pe.includes(s["vertical-align"]))return e.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${s["vertical-align"]}' instead.`);if(a=e.parse(s["vertical-align"],1,$t),!a)return null}const l=r[r.length-1];l.scale=t,l.font=n,l.textColor=o,l.verticalAlign=a;}else {const s=e.parse(t[n],1,qt);if(!s)return null;const o=s.type.kind;if("string"!==o&&"value"!==o&&"null"!==o&&"resolvedImage"!==o)return e.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");i=!0,r.push({content:s,scale:null,font:null,textColor:null,verticalAlign:null});}}return new Pn(r)}evaluate(t){return new ze(this.sections.map((e=>{const n=e.content.evaluate(t);return qe(n)===Wt?new De("",n,null,null,null,e.verticalAlign?e.verticalAlign.evaluate(t):null):new De(Ge(n),null,e.scale?e.scale.evaluate(t):null,e.font?e.font.evaluate(t).join(","):null,e.textColor?e.textColor.evaluate(t):null,e.verticalAlign?e.verticalAlign.evaluate(t):null)})))}eachChild(t){for(const e of this.sections)t(e.content),e.scale&&t(e.scale),e.font&&t(e.font),e.textColor&&t(e.textColor),e.verticalAlign&&t(e.verticalAlign);}outputDefined(){return !1}}class Dn{constructor(t){this.type=Wt,this.input=t;}static parse(t,e){if(2!==t.length)return e.error("Expected two arguments.");const n=e.parse(t[1],1,$t);return n?new Dn(n):e.error("No image name provided.")}evaluate(t){const e=this.input.evaluate(t),n=Re.fromString(e);return n&&t.availableImages&&(n.available=t.availableImages.indexOf(e)>-1),n}eachChild(t){t(this.input);}outputDefined(){return !1}}class zn{constructor(t){this.type=Ot,this.input=t;}static parse(t,e){if(2!==t.length)return e.error(`Expected 1 argument, but found ${t.length-1} instead.`);const n=e.parse(t[1],1);return n?"array"!==n.type.kind&&"string"!==n.type.kind&&"value"!==n.type.kind?e.error(`Expected argument of type string or array, but found ${Qt(n.type)} instead.`):new zn(n):null}evaluate(t){const e=this.input.evaluate(t);if("string"==typeof e)return [...e].length;if(Array.isArray(e))return e.length;throw new Le(`Expected value to be of type string or array, but found ${Qt(qe(e))} instead.`)}eachChild(t){t(this.input);}outputDefined(){return !1}}const Bn=8192;function Cn(t,e){const n=(180+t[0])/360,r=(180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t[1]*Math.PI/360)))/360,i=Math.pow(2,e.z);return [Math.round(n*i*Bn),Math.round(r*i*Bn)]}function Vn(t,e){const n=Math.pow(2,e.z);return [(i=(t[0]/Bn+e.x)/n,360*i-180),(r=(t[1]/Bn+e.y)/n,360/Math.PI*Math.atan(Math.exp((180-360*r)*Math.PI/180))-90)];var r,i;}function Ln(t,e){t[0]=Math.min(t[0],e[0]),t[1]=Math.min(t[1],e[1]),t[2]=Math.max(t[2],e[0]),t[3]=Math.max(t[3],e[1]);}function On(t,e){return !(t[0]<=e[0]||t[2]>=e[2]||t[1]<=e[1]||t[3]>=e[3])}function $n(t,e,n){const r=t[0]-e[0],i=t[1]-e[1],s=t[0]-n[0],o=t[1]-n[1];return r*o-s*i==0&&r*s<=0&&i*o<=0}function Rn(t,e,n,r){return 0!=(i=[r[0]-n[0],r[1]-n[1]])[0]*(s=[e[0]-t[0],e[1]-t[1]])[1]-i[1]*s[0]&&!(!Xn(t,e,n,r)||!Xn(n,r,t,e));var i,s;}function Nn(t,e,n){for(const r of n)for(let n=0;n(i=t)[1]!=(o=a[e+1])[1]>i[1]&&i[0]<(o[0]-s[0])*(i[1]-s[1])/(o[1]-s[1])+s[0]&&(r=!r);}var i,s,o;return r}function jn(t,e){for(const n of e)if(Un(t,n))return !0;return !1}function qn(t,e){for(const n of t)if(!Un(n,e))return !1;for(let n=0;n0&&a<0||o<0&&a>0}function Yn(t,e,n){const r=[];for(let i=0;in[2]){const e=.5*r;let i=t[0]-n[0]>e?-r:n[0]-t[0]>e?r:0;0===i&&(i=t[0]-n[2]>e?-r:n[2]-t[0]>e?r:0),t[0]+=i;}Ln(e,t);}function Wn(t,e,n,r){const i=Math.pow(2,r.z)*Bn,s=[r.x*Bn,r.y*Bn],o=[];for(const r of t)for(const t of r){const r=[t.x+s[0],t.y+s[1]];Hn(r,e,n,i),o.push(r);}return o}function Kn(t,e,n,r){const i=Math.pow(2,r.z)*Bn,s=[r.x*Bn,r.y*Bn],o=[];for(const n of t){const t=[];for(const r of n){const n=[r.x+s[0],r.y+s[1]];Ln(e,n),t.push(n);}o.push(t);}if(e[2]-e[0]<=i/2){(a=e)[0]=a[1]=1/0,a[2]=a[3]=-1/0;for(const t of o)for(const r of t)Hn(r,e,n,i);}var a;return o}class Jn{constructor(t,e){this.type=Rt,this.geojson=t,this.geometries=e;}static parse(t,e){if(2!==t.length)return e.error(`'within' expression requires exactly one argument, but found ${t.length-1} instead.`);if(je(t[1])){const e=t[1];if("FeatureCollection"===e.type){const t=[];for(const n of e.features){const{type:e,coordinates:r}=n.geometry;"Polygon"===e&&t.push(r),"MultiPolygon"===e&&t.push(...r);}if(t.length)return new Jn(e,{type:"MultiPolygon",coordinates:t})}else if("Feature"===e.type){const t=e.geometry.type;if("Polygon"===t||"MultiPolygon"===t)return new Jn(e,e.geometry)}else if("Polygon"===e.type||"MultiPolygon"===e.type)return new Jn(e,e)}return e.error("'within' expression requires valid geojson object that contains polygon geometry type.")}evaluate(t){if(null!=t.geometry()&&null!=t.canonicalID()){if("Point"===t.geometryType())return function(t,e){const n=[1/0,1/0,-1/0,-1/0],r=[1/0,1/0,-1/0,-1/0],i=t.canonicalID();if("Polygon"===e.type){const s=Yn(e.coordinates,r,i),o=Wn(t.geometry(),n,r,i);if(!On(n,r))return !1;for(const t of o)if(!Un(t,s))return !1}if("MultiPolygon"===e.type){const s=Zn(e.coordinates,r,i),o=Wn(t.geometry(),n,r,i);if(!On(n,r))return !1;for(const t of o)if(!jn(t,s))return !1}return !0}(t,this.geometries);if("LineString"===t.geometryType())return function(t,e){const n=[1/0,1/0,-1/0,-1/0],r=[1/0,1/0,-1/0,-1/0],i=t.canonicalID();if("Polygon"===e.type){const s=Yn(e.coordinates,r,i),o=Kn(t.geometry(),n,r,i);if(!On(n,r))return !1;for(const t of o)if(!qn(t,s))return !1}if("MultiPolygon"===e.type){const s=Zn(e.coordinates,r,i),o=Kn(t.geometry(),n,r,i);if(!On(n,r))return !1;for(const t of o)if(!Gn(t,s))return !1}return !0}(t,this.geometries)}return !1}eachChild(){}outputDefined(){return !0}}let Qn=class{constructor(t=[],e=(t,e)=>te?1:0){if(this.data=t,this.length=this.data.length,this.compare=e,this.length>0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t);}push(t){this.data.push(t),this._up(this.length++);}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return --this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:n}=this,r=e[t];for(;t>0;){const i=t-1>>1,s=e[i];if(n(r,s)>=0)break;e[t]=s,t=i;}e[t]=r;}_down(t){const{data:e,compare:n}=this,r=this.length>>1,i=e[t];for(;t=0)break;e[t]=e[r],t=r;}e[t]=i;}};function tr(t,e,n=0,r=t.length-1,i=nr){for(;r>n;){if(r-n>600){const s=r-n+1,o=e-n+1,a=Math.log(s),l=.5*Math.exp(2*a/3),u=.5*Math.sqrt(a*l*(s-l)/s)*(o-s/2<0?-1:1);tr(t,e,Math.max(n,Math.floor(e-o*l/s+u)),Math.min(r,Math.floor(e+(s-o)*l/s+u)),i);}const s=t[e];let o=n,a=r;for(er(t,n,e),i(t[r],s)>0&&er(t,n,r);o0;)a--;}0===i(t[n],s)?er(t,n,a):(a++,er(t,a,r)),a<=e&&(n=a+1),e<=a&&(r=a-1);}}function er(t,e,n){const r=t[e];t[e]=t[n],t[n]=r;}function nr(t,e){return te?1:0}function rr(t,e){if(t.length<=1)return [t];const n=[];let r,i;for(const e of t){const t=sr(e);0!==t&&(e.area=Math.abs(t),void 0===i&&(i=t<0),i===t<0?(r&&n.push(r),r=[e]):r.push(e));}if(r&&n.push(r),e>1)for(let t=0;t1?(l=t[a+1][0],u=t[a+1][1]):p>0&&(l+=c/this.kx*p,u+=h/this.ky*p)),c=this.wrap(e[0]-l)*this.kx,h=(e[1]-u)*this.ky;const f=c*c+h*h;f180;)t-=360;return t}}function cr(t,e){return e[0]-t[0]}function hr(t){return t[1]-t[0]+1}function pr(t,e){return t[1]>=t[0]&&t[1]t[1])return [null,null];const n=hr(t);if(e){if(2===n)return [t,null];const e=Math.floor(n/2);return [[t[0],t[0]+e],[t[0]+e,t[1]]]}if(1===n)return [t,null];const r=Math.floor(n/2)-1;return [[t[0],t[0]+r],[t[0]+r+1,t[1]]]}function dr(t,e){if(!pr(e,t.length))return [1/0,1/0,-1/0,-1/0];const n=[1/0,1/0,-1/0,-1/0];for(let r=e[0];r<=e[1];++r)Ln(n,t[r]);return n}function yr(t){const e=[1/0,1/0,-1/0,-1/0];for(const n of t)for(const t of n)Ln(e,t);return e}function mr(t){return t[0]!==-1/0&&t[1]!==-1/0&&t[2]!==1/0&&t[3]!==1/0}function gr(t,e,n){if(!mr(t)||!mr(e))return NaN;let r=0,i=0;return t[2]e[2]&&(r=t[0]-e[2]),t[1]>e[3]&&(i=t[1]-e[3]),t[3]=r)return r;if(On(i,s)){if(Ar(t,e))return 0}else if(Ar(e,t))return 0;let o=1/0;for(const r of t)for(let t=0,i=r.length,s=i-1;t0;){const i=o.pop();if(i[0]>=s)continue;const l=i[1],u=e?50:100;if(hr(l)<=u){if(!pr(l,t.length))return NaN;if(e){const e=Sr(t,l,n,r);if(isNaN(e)||0===e)return e;s=Math.min(s,e);}else for(let e=l[0];e<=l[1];++e){const i=_r(t[e],n,r);if(s=Math.min(s,i),0===s)return 0}}else {const n=fr(l,e);kr(o,s,r,t,a,n[0]),kr(o,s,r,t,a,n[1]);}}return s}function Tr(t,e,n,r,i,s=1/0){let o=Math.min(s,i.distance(t[0],n[0]));if(0===o)return o;const a=new Qn([[0,[0,t.length-1],[0,n.length-1]]],cr);for(;a.length>0;){const s=a.pop();if(s[0]>=o)continue;const l=s[1],u=s[2],c=e?50:100,h=r?50:100;if(hr(l)<=c&&hr(u)<=h){if(!pr(l,t.length)&&pr(u,n.length))return NaN;let s;if(e&&r)s=br(t,l,n,u,i),o=Math.min(o,s);else if(e&&!r){const e=t.slice(l[0],l[1]+1);for(let t=u[0];t<=u[1];++t)if(s=xr(n[t],e,i),o=Math.min(o,s),0===o)return o}else if(!e&&r){const e=n.slice(u[0],u[1]+1);for(let n=l[0];n<=l[1];++n)if(s=xr(t[n],e,i),o=Math.min(o,s),0===o)return o}else s=wr(t,l,n,u,i),o=Math.min(o,s);}else {const s=fr(l,e),c=fr(u,r);Ir(a,o,i,t,n,s[0],c[0]),Ir(a,o,i,t,n,s[0],c[1]),Ir(a,o,i,t,n,s[1],c[0]),Ir(a,o,i,t,n,s[1],c[1]);}}return o}function Fr(t){return "MultiPolygon"===t.type?t.coordinates.map((t=>({type:"Polygon",coordinates:t}))):"MultiLineString"===t.type?t.coordinates.map((t=>({type:"LineString",coordinates:t}))):"MultiPoint"===t.type?t.coordinates.map((t=>({type:"Point",coordinates:t}))):[t]}class Pr{constructor(t,e){this.type=Ot,this.geojson=t,this.geometries=e;}static parse(t,e){if(2!==t.length)return e.error(`'distance' expression requires exactly one argument, but found ${t.length-1} instead.`);if(je(t[1])){const e=t[1];if("FeatureCollection"===e.type)return new Pr(e,e.features.map((t=>Fr(t.geometry))).flat());if("Feature"===e.type)return new Pr(e,Fr(e.geometry));if("type"in e&&"coordinates"in e)return new Pr(e,Fr(e))}return e.error("'distance' expression requires valid geojson object that contains polygon geometry type.")}evaluate(t){if(null!=t.geometry()&&null!=t.canonicalID()){if("Point"===t.geometryType())return function(t,e){const n=t.geometry(),r=n.flat().map((e=>Vn([e.x,e.y],t.canonical)));if(0===n.length)return NaN;const i=new ur(r[0][1]);let s=1/0;for(const t of e){switch(t.type){case "Point":s=Math.min(s,Tr(r,!1,[t.coordinates],!1,i,s));break;case "LineString":s=Math.min(s,Tr(r,!1,t.coordinates,!0,i,s));break;case "Polygon":s=Math.min(s,Er(r,!1,t.coordinates,i,s));}if(0===s)return s}return s}(t,this.geometries);if("LineString"===t.geometryType())return function(t,e){const n=t.geometry(),r=n.flat().map((e=>Vn([e.x,e.y],t.canonical)));if(0===n.length)return NaN;const i=new ur(r[0][1]);let s=1/0;for(const t of e){switch(t.type){case "Point":s=Math.min(s,Tr(r,!0,[t.coordinates],!1,i,s));break;case "LineString":s=Math.min(s,Tr(r,!0,t.coordinates,!0,i,s));break;case "Polygon":s=Math.min(s,Er(r,!0,t.coordinates,i,s));}if(0===s)return s}return s}(t,this.geometries);if("Polygon"===t.geometryType())return function(t,e){const n=t.geometry();if(0===n.length||0===n[0].length)return NaN;const r=rr(n,0).map((e=>e.map((e=>e.map((e=>Vn([e.x,e.y],t.canonical))))))),i=new ur(r[0][0][0][1]);let s=1/0;for(const t of e)for(const e of r){switch(t.type){case "Point":s=Math.min(s,Er([t.coordinates],!1,e,i,s));break;case "LineString":s=Math.min(s,Er(t.coordinates,!0,e,i,s));break;case "Polygon":s=Math.min(s,Mr(e,t.coordinates,i,s));}if(0===s)return s}return s}(t,this.geometries)}return NaN}eachChild(){}outputDefined(){return !0}}class Dr{constructor(t){this.type=qt,this.key=t;}static parse(t,e){if(2!==t.length)return e.error(`Expected 1 argument, but found ${t.length-1} instead.`);const n=t[1];return null==n?e.error("Global state property must be defined."):"string"!=typeof n?e.error(`Global state property must be string, but found ${typeof t[1]} instead.`):new Dr(n)}evaluate(t){var e;const n=null===(e=t.globals)||void 0===e?void 0:e.globalState;return n&&0!==Object.keys(n).length?we(n,this.key):null}eachChild(){}outputDefined(){return !1}}const zr={"==":Sn,"!=":An,">":kn,"<":Mn,">=":En,"<=":In,array:Ze,at:nn,boolean:Ze,case:an,coalesce:vn,collator:Tn,format:Pn,image:Dn,in:rn,"index-of":sn,interpolate:mn,"interpolate-hcl":mn,"interpolate-lab":mn,length:zn,let:tn,literal:Xe,match:on,number:Ze,"number-format":Fn,object:Ze,slice:ln,step:cn,string:Ze,"to-boolean":We,"to-color":We,"to-number":We,"to-string":We,var:en,within:Jn,distance:Pr,"global-state":Dr};class Br{constructor(t,e,n,r){this.name=t,this.type=e,this._evaluate=n,this.args=r;}evaluate(t){return this._evaluate(t,this.args)}eachChild(t){this.args.forEach(t);}outputDefined(){return !1}static parse(t,e){const n=t[0],r=Br.definitions[n];if(!r)return e.error(`Unknown expression "${n}". If you wanted a literal array, use ["literal", [...]].`,0);const i=Array.isArray(r)?r[0]:r.type,s=Array.isArray(r)?[[r[1],r[2]]]:r.overloads,o=s.filter((([e])=>!Array.isArray(e)||e.length===t.length-1));let a=null;for(const[r,s]of o){a=new Qe(e.registry,$r,e.path,null,e.scope);const o=[];let l=!1;for(let e=1;e{return e=t,Array.isArray(e)?`(${e.map(Qt).join(", ")})`:`(${Qt(e.type)}...)`;var e;})).join(" | "),r=[];for(let n=1;n{n=e?n&&$r(t):n&&t instanceof Xe;})),!!n&&Rr(t)&&Ur(t,["zoom","heatmap-density","elevation","line-progress","accumulated","is-supported-script"])}function Rr(t){if(t instanceof Br){if("get"===t.name&&1===t.args.length)return !1;if("feature-state"===t.name)return !1;if("has"===t.name&&1===t.args.length)return !1;if("properties"===t.name||"geometry-type"===t.name||"id"===t.name)return !1;if(/^filter-/.test(t.name))return !1}if(t instanceof Jn)return !1;if(t instanceof Pr)return !1;let e=!0;return t.eachChild((t=>{e&&!Rr(t)&&(e=!1);})),e}function Nr(t){if(t instanceof Br&&"feature-state"===t.name)return !1;let e=!0;return t.eachChild((t=>{e&&!Nr(t)&&(e=!1);})),e}function Ur(t,e){if(t instanceof Br&&e.indexOf(t.name)>=0)return !1;let n=!0;return t.eachChild((t=>{n&&!Ur(t,e)&&(n=!1);})),n}function jr(t){return {result:"success",value:t}}function qr(t){return {result:"error",value:t}}function Gr(t){return "data-driven"===t["property-type"]||"cross-faded-data-driven"===t["property-type"]}function Xr(t){return !!t.expression&&t.expression.parameters.indexOf("zoom")>-1}function Yr(t){return !!t.expression&&t.expression.interpolated}function Zr(t){return t instanceof Number?"number":t instanceof String?"string":t instanceof Boolean?"boolean":Array.isArray(t)?"array":null===t?"null":typeof t}function Hr(t){return "object"==typeof t&&null!==t&&!Array.isArray(t)&&qe(t)===jt}function Wr(t){return t}function Kr(t,e){const n=t.stops&&"object"==typeof t.stops[0][0],r=n||!(n||void 0!==t.property),i=t.type||(Yr(e)?"exponential":"interval"),s=function(t){switch(t.type){case "color":return Te.parse;case "padding":return Be.parse;case "numberArray":return Ce.parse;case "colorArray":return Ve.parse;default:return null}}(e);if(s&&((t=Bt({},t)).stops&&(t.stops=t.stops.map((t=>[t[0],s(t[1])]))),t.default=s(t.default?t.default:e.default)),t.colorSpace&&"rgb"!==(o=t.colorSpace)&&"hcl"!==o&&"lab"!==o)throw new Error(`Unknown color space: "${t.colorSpace}"`);var o;const a=function(t){switch(t){case "exponential":return ei;case "interval":return ti;case "categorical":return Qr;case "identity":return ni;default:throw new Error(`Unknown function type "${t}"`)}}(i);let l,u;if("categorical"===i){l=Object.create(null);for(const e of t.stops)l[e[0]]=e[1];u=typeof t.stops[0][0];}if(n){const n={},r=[];for(let e=0;et[0])),evaluate:({zoom:n},r)=>ei({stops:i,base:t.base},e,n).evaluate(n,r)}}if(r){const n="exponential"===i?{name:"exponential",base:void 0!==t.base?t.base:1}:null;return {kind:"camera",interpolationType:n,interpolationFactor:mn.interpolationFactor.bind(void 0,n),zoomStops:t.stops.map((t=>t[0])),evaluate:({zoom:n})=>a(t,e,n,l,u)}}return {kind:"source",evaluate(n,r){const i=r&&r.properties?r.properties[t.property]:void 0;return void 0===i?Jr(t.default,e.default):a(t,e,i,l,u)}}}function Jr(t,e,n){return void 0!==t?t:void 0!==e?e:void 0!==n?n:void 0}function Qr(t,e,n,r,i){return Jr(typeof n===i?r[n]:void 0,t.default,e.default)}function ti(t,e,n){if("number"!==Zr(n))return Jr(t.default,e.default);const r=t.stops.length;if(1===r)return t.stops[0][1];if(n<=t.stops[0][0])return t.stops[0][1];if(n>=t.stops[r-1][0])return t.stops[r-1][1];const i=un(t.stops.map((t=>t[0])),n);return t.stops[i][1]}function ei(t,e,n){const r=void 0!==t.base?t.base:1;if("number"!==Zr(n))return Jr(t.default,e.default);const i=t.stops.length;if(1===i)return t.stops[0][1];if(n<=t.stops[0][0])return t.stops[0][1];if(n>=t.stops[i-1][0])return t.stops[i-1][1];const s=un(t.stops.map((t=>t[0])),n),o=function(t,e,n,r){const i=r-n,s=t-n;return 0===i?0:1===e?s/i:(Math.pow(e,s)-1)/(Math.pow(e,i)-1)}(n,r,t.stops[s][0],t.stops[s+1][0]),a=t.stops[s][1],l=t.stops[s+1][1],u=xn[e.type]||Wr;return "function"==typeof a.evaluate?{evaluate(...e){const n=a.evaluate.apply(void 0,e),r=l.evaluate.apply(void 0,e);if(void 0!==n&&void 0!==r)return u(n,r,o,t.colorSpace)}}:u(a,l,o,t.colorSpace)}function ni(t,e,n){switch(e.type){case "color":n=Te.parse(n);break;case "formatted":n=ze.fromString(n.toString());break;case "resolvedImage":n=Re.fromString(n.toString());break;case "padding":n=Be.parse(n);break;case "colorArray":n=Ve.parse(n);break;case "numberArray":n=Ce.parse(n);break;default:Zr(n)===e.type||"enum"===e.type&&e.values[n]||(n=void 0);}return Jr(n,t.default,e.default)}Br.register(zr,{error:[{kind:"error"},[$t],(t,[e])=>{throw new Le(e.evaluate(t))}],typeof:[$t,[qt],(t,[e])=>Qt(qe(e.evaluate(t)))],"to-rgba":[Jt(Ot,4),[Nt],(t,[e])=>{const[n,r,i,s]=e.evaluate(t).rgb;return [255*n,255*r,255*i,s]}],rgb:[Nt,[Ot,Ot,Ot],Cr],rgba:[Nt,[Ot,Ot,Ot,Ot],Cr],has:{type:Rt,overloads:[[[$t],(t,[e])=>Vr(e.evaluate(t),t.properties())],[[$t,jt],(t,[e,n])=>Vr(e.evaluate(t),n.evaluate(t))]]},get:{type:qt,overloads:[[[$t],(t,[e])=>Lr(e.evaluate(t),t.properties())],[[$t,jt],(t,[e,n])=>Lr(e.evaluate(t),n.evaluate(t))]]},"feature-state":[qt,[$t],(t,[e])=>Lr(e.evaluate(t),t.featureState||{})],properties:[jt,[],t=>t.properties()],"geometry-type":[$t,[],t=>t.geometryType()],id:[qt,[],t=>t.id()],zoom:[Ot,[],t=>t.globals.zoom],"heatmap-density":[Ot,[],t=>t.globals.heatmapDensity||0],elevation:[Ot,[],t=>t.globals.elevation||0],"line-progress":[Ot,[],t=>t.globals.lineProgress||0],accumulated:[qt,[],t=>void 0===t.globals.accumulated?null:t.globals.accumulated],"+":[Ot,Or(Ot),(t,e)=>{let n=0;for(const r of e)n+=r.evaluate(t);return n}],"*":[Ot,Or(Ot),(t,e)=>{let n=1;for(const r of e)n*=r.evaluate(t);return n}],"-":{type:Ot,overloads:[[[Ot,Ot],(t,[e,n])=>e.evaluate(t)-n.evaluate(t)],[[Ot],(t,[e])=>-e.evaluate(t)]]},"/":[Ot,[Ot,Ot],(t,[e,n])=>e.evaluate(t)/n.evaluate(t)],"%":[Ot,[Ot,Ot],(t,[e,n])=>e.evaluate(t)%n.evaluate(t)],ln2:[Ot,[],()=>Math.LN2],pi:[Ot,[],()=>Math.PI],e:[Ot,[],()=>Math.E],"^":[Ot,[Ot,Ot],(t,[e,n])=>Math.pow(e.evaluate(t),n.evaluate(t))],sqrt:[Ot,[Ot],(t,[e])=>Math.sqrt(e.evaluate(t))],log10:[Ot,[Ot],(t,[e])=>Math.log(e.evaluate(t))/Math.LN10],ln:[Ot,[Ot],(t,[e])=>Math.log(e.evaluate(t))],log2:[Ot,[Ot],(t,[e])=>Math.log(e.evaluate(t))/Math.LN2],sin:[Ot,[Ot],(t,[e])=>Math.sin(e.evaluate(t))],cos:[Ot,[Ot],(t,[e])=>Math.cos(e.evaluate(t))],tan:[Ot,[Ot],(t,[e])=>Math.tan(e.evaluate(t))],asin:[Ot,[Ot],(t,[e])=>Math.asin(e.evaluate(t))],acos:[Ot,[Ot],(t,[e])=>Math.acos(e.evaluate(t))],atan:[Ot,[Ot],(t,[e])=>Math.atan(e.evaluate(t))],min:[Ot,Or(Ot),(t,e)=>Math.min(...e.map((e=>e.evaluate(t))))],max:[Ot,Or(Ot),(t,e)=>Math.max(...e.map((e=>e.evaluate(t))))],abs:[Ot,[Ot],(t,[e])=>Math.abs(e.evaluate(t))],round:[Ot,[Ot],(t,[e])=>{const n=e.evaluate(t);return n<0?-Math.round(-n):Math.round(n)}],floor:[Ot,[Ot],(t,[e])=>Math.floor(e.evaluate(t))],ceil:[Ot,[Ot],(t,[e])=>Math.ceil(e.evaluate(t))],"filter-==":[Rt,[$t,qt],(t,[e,n])=>t.properties()[e.value]===n.value],"filter-id-==":[Rt,[qt],(t,[e])=>t.id()===e.value],"filter-type-==":[Rt,[$t],(t,[e])=>t.geometryType()===e.value],"filter-<":[Rt,[$t,qt],(t,[e,n])=>{const r=t.properties()[e.value],i=n.value;return typeof r==typeof i&&r{const n=t.id(),r=e.value;return typeof n==typeof r&&n":[Rt,[$t,qt],(t,[e,n])=>{const r=t.properties()[e.value],i=n.value;return typeof r==typeof i&&r>i}],"filter-id->":[Rt,[qt],(t,[e])=>{const n=t.id(),r=e.value;return typeof n==typeof r&&n>r}],"filter-<=":[Rt,[$t,qt],(t,[e,n])=>{const r=t.properties()[e.value],i=n.value;return typeof r==typeof i&&r<=i}],"filter-id-<=":[Rt,[qt],(t,[e])=>{const n=t.id(),r=e.value;return typeof n==typeof r&&n<=r}],"filter->=":[Rt,[$t,qt],(t,[e,n])=>{const r=t.properties()[e.value],i=n.value;return typeof r==typeof i&&r>=i}],"filter-id->=":[Rt,[qt],(t,[e])=>{const n=t.id(),r=e.value;return typeof n==typeof r&&n>=r}],"filter-has":[Rt,[qt],(t,[e])=>e.value in t.properties()],"filter-has-id":[Rt,[],t=>null!==t.id()&&void 0!==t.id()],"filter-type-in":[Rt,[Jt($t)],(t,[e])=>e.value.indexOf(t.geometryType())>=0],"filter-id-in":[Rt,[Jt(qt)],(t,[e])=>e.value.indexOf(t.id())>=0],"filter-in-small":[Rt,[$t,Jt(qt)],(t,[e,n])=>n.value.indexOf(t.properties()[e.value])>=0],"filter-in-large":[Rt,[$t,Jt(qt)],(t,[e,n])=>function(t,e,n,r){for(;n<=r;){const i=n+r>>1;if(e[i]===t)return !0;e[i]>t?r=i-1:n=i+1;}return !1}(t.properties()[e.value],n.value,0,n.value.length-1)],all:{type:Rt,overloads:[[[Rt,Rt],(t,[e,n])=>e.evaluate(t)&&n.evaluate(t)],[Or(Rt),(t,e)=>{for(const n of e)if(!n.evaluate(t))return !1;return !0}]]},any:{type:Rt,overloads:[[[Rt,Rt],(t,[e,n])=>e.evaluate(t)||n.evaluate(t)],[Or(Rt),(t,e)=>{for(const n of e)if(n.evaluate(t))return !0;return !1}]]},"!":[Rt,[Rt],(t,[e])=>!e.evaluate(t)],"is-supported-script":[Rt,[$t],(t,[e])=>{const n=t.globals&&t.globals.isSupportedScript;return !n||n(e.evaluate(t))}],upcase:[$t,[$t],(t,[e])=>e.evaluate(t).toUpperCase()],downcase:[$t,[$t],(t,[e])=>e.evaluate(t).toLowerCase()],concat:[$t,Or(qt),(t,e)=>e.map((e=>Ge(e.evaluate(t)))).join("")],split:[Jt($t),[$t,$t],(t,[e,n])=>e.evaluate(t).split(n.evaluate(t))],join:[$t,[Jt($t),$t],(t,[e,n])=>e.evaluate(t).join(n.evaluate(t))],"resolved-locale":[$t,[Gt],(t,[e])=>e.evaluate(t).resolvedLocale()]});class ri{constructor(t,e,n){this.expression=t,this._warningHistory={},this._evaluator=new Je,this._defaultValue=e?function(t){if("color"===t.type&&Hr(t.default))return new Te(0,0,0,0);switch(t.type){case "color":return Te.parse(t.default)||null;case "padding":return Be.parse(t.default)||null;case "numberArray":return Ce.parse(t.default)||null;case "colorArray":return Ve.parse(t.default)||null;case "variableAnchorOffsetCollection":return $e.parse(t.default)||null;case "projectionDefinition":return Ne.parse(t.default)||null;default:return void 0===t.default?null:t.default}}(e):null,this._enumValues=e&&"enum"===e.type?e.values:null,this._globalState=n;}evaluateWithoutErrorHandling(t,e,n,r,i,s){return this._globalState&&(t=pi(t,this._globalState)),this._evaluator.globals=t,this._evaluator.feature=e,this._evaluator.featureState=n,this._evaluator.canonical=r,this._evaluator.availableImages=i||null,this._evaluator.formattedSection=s,this.expression.evaluate(this._evaluator)}evaluate(t,e,n,r,i,s){this._globalState&&(t=pi(t,this._globalState)),this._evaluator.globals=t,this._evaluator.feature=e||null,this._evaluator.featureState=n||null,this._evaluator.canonical=r,this._evaluator.availableImages=i||null,this._evaluator.formattedSection=s||null;try{const t=this.expression.evaluate(this._evaluator);if(null==t||"number"==typeof t&&t!=t)return this._defaultValue;if(this._enumValues&&!(t in this._enumValues))throw new Le(`Expected value to be one of ${Object.keys(this._enumValues).map((t=>JSON.stringify(t))).join(", ")}, but found ${JSON.stringify(t)} instead.`);return t}catch(t){return this._warningHistory[t.message]||(this._warningHistory[t.message]=!0,"undefined"!=typeof console&&console.warn(t.message)),this._defaultValue}}}function ii(t){return Array.isArray(t)&&t.length>0&&"string"==typeof t[0]&&t[0]in zr}function si(t,e,n){const r=new Qe(zr,$r,[],e?function(t){const e={color:Nt,string:$t,number:Ot,enum:$t,boolean:Rt,formatted:Xt,padding:Yt,numberArray:Ht,colorArray:Zt,projectionDefinition:Ut,resolvedImage:Wt,variableAnchorOffsetCollection:Kt};return "array"===t.type?Jt(e[t.value]||qt,t.length):e[t.type]}(e):void 0),i=r.parse(t,void 0,void 0,void 0,e&&"string"===e.type?{typeAnnotation:"coerce"}:void 0);return i?jr(new ri(i,e,n)):qr(r.errors)}class oi{constructor(t,e,n){this.kind=t,this._styleExpression=e,this.isStateDependent="constant"!==t&&!Nr(e.expression),this.globalStateRefs=hi(e.expression),this._globalState=n;}evaluateWithoutErrorHandling(t,e,n,r,i,s){return this._globalState&&(t=pi(t,this._globalState)),this._styleExpression.evaluateWithoutErrorHandling(t,e,n,r,i,s)}evaluate(t,e,n,r,i,s){return this._globalState&&(t=pi(t,this._globalState)),this._styleExpression.evaluate(t,e,n,r,i,s)}}class ai{constructor(t,e,n,r,i){this.kind=t,this.zoomStops=n,this._styleExpression=e,this.isStateDependent="camera"!==t&&!Nr(e.expression),this.globalStateRefs=hi(e.expression),this.interpolationType=r,this._globalState=i;}evaluateWithoutErrorHandling(t,e,n,r,i,s){return this._globalState&&(t=pi(t,this._globalState)),this._styleExpression.evaluateWithoutErrorHandling(t,e,n,r,i,s)}evaluate(t,e,n,r,i,s){return this._globalState&&(t=pi(t,this._globalState)),this._styleExpression.evaluate(t,e,n,r,i,s)}interpolationFactor(t,e,n){return this.interpolationType?mn.interpolationFactor(this.interpolationType,t,e,n):0}}function li(t,e,n){const r=si(t,e,n);if("error"===r.result)return r;const i=r.value.expression,s=Rr(i);if(!s&&!Gr(e))return qr([new Ct("","data expressions not supported")]);const o=Ur(i,["zoom"]);if(!o&&!Xr(e))return qr([new Ct("","zoom expressions not supported")]);const a=ci(i);return a||o?a instanceof Ct?qr([a]):a instanceof mn&&!Yr(e)?qr([new Ct("",'"interpolate" expressions cannot be used with this property')]):jr(a?new ai(s?"camera":"composite",r.value,a.labels,a instanceof mn?a.interpolation:void 0,n):new oi(s?"constant":"source",r.value,n)):qr([new Ct("",'"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.')])}class ui{constructor(t,e){this._parameters=t,this._specification=e,Bt(this,Kr(this._parameters,this._specification));}static deserialize(t){return new ui(t._parameters,t._specification)}static serialize(t){return {_parameters:t._parameters,_specification:t._specification}}}function ci(t){let e=null;if(t instanceof tn)e=ci(t.result);else if(t instanceof vn){for(const n of t.args)if(e=ci(n),e)break}else (t instanceof cn||t instanceof mn)&&t.input instanceof Br&&"zoom"===t.input.name&&(e=t);return e instanceof Ct||t.eachChild((t=>{const n=ci(t);n instanceof Ct?e=n:!e&&n?e=new Ct("",'"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.'):e&&n&&e!==n&&(e=new Ct("",'Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.'));})),e}function hi(t,e=new Set){return t instanceof Dr&&e.add(t.key),t.eachChild((t=>{hi(t,e);})),e}function pi(t,e){const{zoom:n,heatmapDensity:r,elevation:i,lineProgress:s,isSupportedScript:o,accumulated:a}=null!=t?t:{};return {zoom:n,heatmapDensity:r,elevation:i,lineProgress:s,isSupportedScript:o,accumulated:a,globalState:e}}function fi(t){if(!0===t||!1===t)return !0;if(!Array.isArray(t)||0===t.length)return !1;switch(t[0]){case "has":return t.length>=2&&"$id"!==t[1]&&"$type"!==t[1];case "in":return t.length>=3&&("string"!=typeof t[1]||Array.isArray(t[2]));case "!in":case "!has":case "none":return !1;case "==":case "!=":case ">":case ">=":case "<":case "<=":return 3!==t.length||Array.isArray(t[1])||Array.isArray(t[2]);case "any":case "all":for(const e of t.slice(1))if(!fi(e)&&"boolean"!=typeof e)return !1;return !0;default:return !0}}const di={type:"boolean",default:!1,transition:!1,"property-type":"data-driven",expression:{interpolated:!1,parameters:["zoom","feature"]}};function yi(t,e){if(null==t)return {filter:()=>!0,needGeometry:!1,getGlobalStateRefs:()=>new Set};fi(t)||(t=xi(t));const n=si(t,di,e);if("error"===n.result)throw new Error(n.value.map((t=>`${t.key}: ${t.message}`)).join(", "));return {filter:(t,e,r)=>n.value.evaluate(t,e,{},r),needGeometry:gi(t),getGlobalStateRefs:()=>hi(n.value.expression)}}function mi(t,e){return te?1:0}function gi(t){if(!Array.isArray(t))return !1;if("within"===t[0]||"distance"===t[0])return !0;for(let e=1;e"===e||"<="===e||">="===e?vi(t[1],t[2],e):"any"===e?(n=t.slice(1),["any"].concat(n.map(xi))):"all"===e?["all"].concat(t.slice(1).map(xi)):"none"===e?["all"].concat(t.slice(1).map(xi).map(_i)):"in"===e?bi(t[1],t.slice(2)):"!in"===e?_i(bi(t[1],t.slice(2))):"has"===e?wi(t[1]):"!has"!==e||_i(wi(t[1]));var n;}function vi(t,e,n){switch(t){case "$type":return [`filter-type-${n}`,e];case "$id":return [`filter-id-${n}`,e];default:return [`filter-${n}`,t,e]}}function bi(t,e){if(0===e.length)return !1;switch(t){case "$type":return ["filter-type-in",["literal",e]];case "$id":return ["filter-id-in",["literal",e]];default:return e.length>200&&!e.some((t=>typeof t!=typeof e[0]))?["filter-in-large",t,["literal",e.sort(mi)]]:["filter-in-small",t,["literal",e]]}}function wi(t){switch(t){case "$type":return !0;case "$id":return ["filter-has-id"];default:return ["filter-has",t]}}function _i(t){return ["!",t]}function Si(t){const e=typeof t;if("number"===e||"boolean"===e||"string"===e||null==t)return JSON.stringify(t);if(Array.isArray(t)){let e="[";for(const n of t)e+=`${Si(n)},`;return `${e}]`}const n=Object.keys(t).sort();let r="{";for(let e=0;er.maximum?[new zt(e,n,`${n} is greater than the maximum value ${r.maximum}`)]:[]}function Pi(t){const e=t.valueSpec,n=ki(t.value.type);let r,i,s,o={};const a="categorical"!==n&&void 0===t.value.property,l=!a,u="array"===Zr(t.value.stops)&&"array"===Zr(t.value.stops[0])&&"object"===Zr(t.value.stops[0][0]),c=Ei({key:t.key,value:t.value,valueSpec:t.styleSpec.function,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{stops:function(t){if("identity"===n)return [new zt(t.key,t.value,'identity function may not have a "stops" property')];let e=[];const r=t.value;return e=e.concat(Ti({key:t.key,value:r,valueSpec:t.valueSpec,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec,arrayElementValidator:h})),"array"===Zr(r)&&0===r.length&&e.push(new zt(t.key,r,"array must have at least one stop")),e},default:function(t){return t.validateSpec({key:t.key,value:t.value,valueSpec:e,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec})}}});return "identity"===n&&a&&c.push(new zt(t.key,t.value,'missing required property "property"')),"identity"===n||t.value.stops||c.push(new zt(t.key,t.value,'missing required property "stops"')),"exponential"===n&&t.valueSpec.expression&&!Yr(t.valueSpec)&&c.push(new zt(t.key,t.value,"exponential functions not supported")),t.styleSpec.$version>=8&&(l&&!Gr(t.valueSpec)?c.push(new zt(t.key,t.value,"property functions not supported")):a&&!Xr(t.valueSpec)&&c.push(new zt(t.key,t.value,"zoom functions not supported"))),"categorical"!==n&&!u||void 0!==t.value.property||c.push(new zt(t.key,t.value,'"property" property is required')),c;function h(t){let n=[];const r=t.value,a=t.key;if("array"!==Zr(r))return [new zt(a,r,`array expected, ${Zr(r)} found`)];if(2!==r.length)return [new zt(a,r,`array length 2 expected, length ${r.length} found`)];if(u){if("object"!==Zr(r[0]))return [new zt(a,r,`object expected, ${Zr(r[0])} found`)];if(void 0===r[0].zoom)return [new zt(a,r,"object stop key must have zoom")];if(void 0===r[0].value)return [new zt(a,r,"object stop key must have value")];if(s&&s>ki(r[0].zoom))return [new zt(a,r[0].zoom,"stop zoom values must appear in ascending order")];ki(r[0].zoom)!==s&&(s=ki(r[0].zoom),i=void 0,o={}),n=n.concat(Ei({key:`${a}[0]`,value:r[0],valueSpec:{zoom:{}},validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{zoom:Fi,value:p}}));}else n=n.concat(p({key:`${a}[0]`,value:r[0],validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec},r));return ii(Ii(r[1]))?n.concat([new zt(`${a}[1]`,r[1],"expressions are not allowed in function stops.")]):n.concat(t.validateSpec({key:`${a}[1]`,value:r[1],valueSpec:e,validateSpec:t.validateSpec,style:t.style,styleSpec:t.styleSpec}))}function p(t,s){const a=Zr(t.value),l=ki(t.value),u=null!==t.value?t.value:s;if(r){if(a!==r)return [new zt(t.key,u,`${a} stop domain type must match previous stop domain type ${r}`)]}else r=a;if("number"!==a&&"string"!==a&&"boolean"!==a)return [new zt(t.key,u,"stop domain value must be a number, string, or boolean")];if("number"!==a&&"categorical"!==n){let r=`number expected, ${a} found`;return Gr(e)&&void 0===n&&(r+='\nIf you intended to use a categorical function, specify `"type": "categorical"`.'),[new zt(t.key,u,r)]}return "categorical"!==n||"number"!==a||isFinite(l)&&Math.floor(l)===l?"categorical"!==n&&"number"===a&&void 0!==i&&lnew zt(`${t.key}${e.key}`,t.value,e.message)));const n=e.value.expression||e.value._styleExpression.expression;if("property"===t.expressionContext&&"text-font"===t.propertyKey&&!n.outputDefined())return [new zt(t.key,t.value,`Invalid data expression for "${t.propertyKey}". Output values must be contained as literals within the expression.`)];if("property"===t.expressionContext&&"layout"===t.propertyType&&!Nr(n))return [new zt(t.key,t.value,'"feature-state" data expressions are not supported with layout properties.')];if("filter"===t.expressionContext&&!Nr(n))return [new zt(t.key,t.value,'"feature-state" data expressions are not supported with filters.')];if(t.expressionContext&&0===t.expressionContext.indexOf("cluster")){if(!Ur(n,["zoom","feature-state"]))return [new zt(t.key,t.value,'"zoom" and "feature-state" expressions are not supported with cluster properties.')];if("cluster-initial"===t.expressionContext&&!Rr(n))return [new zt(t.key,t.value,"Feature data expressions are not supported with initial expression part of cluster properties.")]}return []}function zi(t){const e=t.key,n=t.value,r=Zr(n);return "string"!==r?[new zt(e,n,`color expected, ${r} found`)]:Te.parse(String(n))?[]:[new zt(e,n,`color expected, "${n}" found`)]}function Bi(t){const e=t.key,n=t.value,r=t.valueSpec,i=[];return Array.isArray(r.values)?-1===r.values.indexOf(ki(n))&&i.push(new zt(e,n,`expected one of [${r.values.join(", ")}], ${JSON.stringify(n)} found`)):-1===Object.keys(r.values).indexOf(ki(n))&&i.push(new zt(e,n,`expected one of [${Object.keys(r.values).join(", ")}], ${JSON.stringify(n)} found`)),i}function Ci(t){return fi(Ii(t.value))?Di(Bt({},t,{expressionContext:"filter",valueSpec:{value:"boolean"}})):Vi(t)}function Vi(t){const e=t.value,n=t.key;if("array"!==Zr(e))return [new zt(n,e,`array expected, ${Zr(e)} found`)];const r=t.styleSpec;let i,s=[];if(e.length<1)return [new zt(n,e,"filter array must have at least 1 element")];switch(s=s.concat(Bi({key:`${n}[0]`,value:e[0],valueSpec:r.filter_operator,style:t.style,styleSpec:t.styleSpec})),ki(e[0])){case "<":case "<=":case ">":case ">=":e.length>=2&&"$type"===ki(e[1])&&s.push(new zt(n,e,`"$type" cannot be use with operator "${e[0]}"`));case "==":case "!=":3!==e.length&&s.push(new zt(n,e,`filter array for operator "${e[0]}" must have 3 elements`));case "in":case "!in":e.length>=2&&(i=Zr(e[1]),"string"!==i&&s.push(new zt(`${n}[1]`,e[1],`string expected, ${i} found`)));for(let o=2;o{t in i&&r.push(new zt(s,i[t],`"${t}" is prohibited for ref layers`));})),o.layers.forEach((e=>{ki(e.id)===u&&(t=e);})),t?t.ref?r.push(new zt(s,i.ref,"ref cannot reference another ref layer")):l=ki(t.type):r.push(new zt(s,i.ref,`ref layer "${u}" not found`));}else if("background"!==l)if(i.source){const t=o.sources&&o.sources[i.source],e=t&&ki(t.type);t?"vector"===e&&"raster"===l?r.push(new zt(s,i.source,`layer "${i.id}" requires a raster source`)):"raster-dem"!==e&&"hillshade"===l||"raster-dem"!==e&&"color-relief"===l?r.push(new zt(s,i.source,`layer "${i.id}" requires a raster-dem source`)):"raster"===e&&"raster"!==l?r.push(new zt(s,i.source,`layer "${i.id}" requires a vector source`)):"vector"!==e||i["source-layer"]?"raster-dem"===e&&"hillshade"!==l&&"color-relief"!==l?r.push(new zt(s,i.source,"raster-dem source can only be used with layer type 'hillshade' or 'color-relief'.")):"line"!==l||!i.paint||!i.paint["line-gradient"]||"geojson"===e&&t.lineMetrics||r.push(new zt(s,i,`layer "${i.id}" specifies a line-gradient, which requires a GeoJSON source with \`lineMetrics\` enabled.`)):r.push(new zt(s,i,`layer "${i.id}" must specify a "source-layer"`)):r.push(new zt(s,i.source,`source "${i.source}" not found`));}else r.push(new zt(s,i,'missing required property "source"'));return "raster"===l&&(null===(e=i.paint)||void 0===e?void 0:e.resampling)&&(null===(n=i.paint)||void 0===n?void 0:n["raster-resampling"])&&r.push(new zt(s,i.paint,`layer "${i.id}" redundantly specifies "resampling" and "raster-resampling" paint properties, but only one is allowed. It is advised to use "resampling".`)),r=r.concat(Ei({key:s,value:i,valueSpec:a.layer,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,objectElementValidators:{"*":()=>[],type:()=>t.validateSpec({key:`${s}.type`,value:i.type,valueSpec:a.layer.type,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,object:i,objectKey:"type"}),filter:Ci,layout:t=>Ei({layer:i,key:t.key,value:t.value,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,objectElementValidators:{"*":t=>$i(Bt({layerType:l},t))}}),paint:t=>Ei({layer:i,key:t.key,value:t.value,style:t.style,styleSpec:t.styleSpec,validateSpec:t.validateSpec,objectElementValidators:{"*":t=>Oi(Bt({layerType:l},t))}})}})),r}function Ni(t){const e=t.value,n=t.key,r=Zr(e);return "string"!==r?[new zt(n,e,`string expected, ${r} found`)]:[]}const Ui={promoteId:function({key:t,value:e}){if("string"===Zr(e))return Ni({key:t,value:e});{const n=[];for(const r in e)n.push(...Ni({key:`${t}.${r}`,value:e[r]}));return n}}};function ji(t){const e=t.value,n=t.key,r=t.styleSpec,i=t.style,s=t.validateSpec;if(!e.type)return [new zt(n,e,'"type" is required')];const o=ki(e.type);let a;switch(o){case "vector":case "raster":return a=Ei({key:n,value:e,valueSpec:r[`source_${o.replace("-","_")}`],style:t.style,styleSpec:r,objectElementValidators:Ui,validateSpec:s}),a;case "raster-dem":return a=function(t){var e;const n=null!==(e=t.sourceName)&&void 0!==e?e:"",r=t.value,i=t.styleSpec,s=i.source_raster_dem,o=t.style;let a=[];const l=Zr(r);if(void 0===r)return a;if("object"!==l)return a.push(new zt("source_raster_dem",r,`object expected, ${l} found`)),a;const u="custom"===ki(r.encoding),c=["redFactor","greenFactor","blueFactor","baseShift"],h=t.value.encoding?`"${t.value.encoding}"`:"Default";for(const e in r)!u&&c.includes(e)?a.push(new zt(e,r[e],`In "${n}": "${e}" is only valid when "encoding" is set to "custom". ${h} encoding found`)):s[e]?a=a.concat(t.validateSpec({key:e,value:r[e],valueSpec:s[e],validateSpec:t.validateSpec,style:o,styleSpec:i})):a.push(new zt(e,r[e],`unknown property "${e}"`));return a}({sourceName:n,value:e,style:t.style,styleSpec:r,validateSpec:s}),a;case "geojson":if(a=Ei({key:n,value:e,valueSpec:r.source_geojson,style:i,styleSpec:r,validateSpec:s,objectElementValidators:Ui}),e.cluster)for(const t in e.clusterProperties){const[r,i]=e.clusterProperties[t],s="string"==typeof r?[r,["accumulated"],["get",t]]:r;a.push(...Di({key:`${n}.${t}.map`,value:i,expressionContext:"cluster-map"})),a.push(...Di({key:`${n}.${t}.reduce`,value:s,expressionContext:"cluster-reduce"}));}return a;case "video":return Ei({key:n,value:e,valueSpec:r.source_video,style:i,validateSpec:s,styleSpec:r});case "image":return Ei({key:n,value:e,valueSpec:r.source_image,style:i,validateSpec:s,styleSpec:r});case "canvas":return [new zt(n,null,"Please use runtime APIs to add canvas sources, rather than including them in stylesheets.","source.canvas")];default:return Bi({key:`${n}.type`,value:e.type,valueSpec:{values:["vector","raster","raster-dem","geojson","video","image"]}})}}function qi(t){const e=t.value,n=t.styleSpec,r=n.light,i=t.style;let s=[];const o=Zr(e);if(void 0===e)return s;if("object"!==o)return s=s.concat([new zt("light",e,`object expected, ${o} found`)]),s;for(const o in e){const a=o.match(/^(.*)-transition$/);s=s.concat(a&&r[a[1]]&&r[a[1]].transition?t.validateSpec({key:o,value:e[o],valueSpec:n.transition,validateSpec:t.validateSpec,style:i,styleSpec:n}):r[o]?t.validateSpec({key:o,value:e[o],valueSpec:r[o],validateSpec:t.validateSpec,style:i,styleSpec:n}):[new zt(o,e[o],`unknown property "${o}"`)]);}return s}function Gi(t){const e=t.value,n=t.styleSpec,r=n.sky,i=t.style,s=Zr(e);if(void 0===e)return [];if("object"!==s)return [new zt("sky",e,`object expected, ${s} found`)];let o=[];for(const s in e)o=o.concat(r[s]?t.validateSpec({key:s,value:e[s],valueSpec:r[s],style:i,styleSpec:n}):[new zt(s,e[s],`unknown property "${s}"`)]);return o}function Xi(t){const e=t.value,n=t.styleSpec,r=n.terrain,i=t.style;let s=[];const o=Zr(e);if(void 0===e)return s;if("object"!==o)return s=s.concat([new zt("terrain",e,`object expected, ${o} found`)]),s;for(const o in e)s=s.concat(r[o]?t.validateSpec({key:o,value:e[o],valueSpec:r[o],validateSpec:t.validateSpec,style:i,styleSpec:n}):[new zt(o,e[o],`unknown property "${o}"`)]);return s}function Yi(t){let e=[];const n=t.value,r=t.key;if(Array.isArray(n)){const i=[],s=[];for(const o in n)n[o].id&&i.includes(n[o].id)&&e.push(new zt(r,n,`all the sprites' ids must be unique, but ${n[o].id} is duplicated`)),i.push(n[o].id),n[o].url&&s.includes(n[o].url)&&e.push(new zt(r,n,`all the sprites' URLs must be unique, but ${n[o].url} is duplicated`)),s.push(n[o].url),e=e.concat(Ei({key:`${r}[${o}]`,value:n[o],valueSpec:{id:{type:"string",required:!0},url:{type:"string",required:!0}},validateSpec:t.validateSpec}));return e}return Ni({key:r,value:n})}function Zi(t){return Boolean(t)&&t.constructor===Object}function Hi(t){return Zi(t.value)?[]:[new zt(t.key,t.value,`object expected, ${Zr(t.value)} found`)]}const Wi={"*":()=>[],array:Ti,boolean:function(t){const e=t.value,n=t.key,r=Zr(e);return "boolean"!==r?[new zt(n,e,`boolean expected, ${r} found`)]:[]},number:Fi,color:zi,constants:Mi,enum:Bi,filter:Ci,function:Pi,layer:Ri,object:Ei,source:ji,light:qi,sky:Gi,terrain:Xi,projection:function(t){const e=t.value,n=t.styleSpec,r=n.projection,i=t.style,s=Zr(e);if(void 0===e)return [];if("object"!==s)return [new zt("projection",e,`object expected, ${s} found`)];let o=[];for(const s in e)o=o.concat(r[s]?t.validateSpec({key:s,value:e[s],valueSpec:r[s],style:i,styleSpec:n}):[new zt(s,e[s],`unknown property "${s}"`)]);return o},projectionDefinition:function(t){const e=t.key;let n=t.value;n=n instanceof String?n.valueOf():n;const r=Zr(n);return "array"!==r||function(t){return Array.isArray(t)&&3===t.length&&"string"==typeof t[0]&&"string"==typeof t[1]&&"number"==typeof t[2]}(n)||function(t){return !!["interpolate","step","literal"].includes(t[0])}(n)?["array","string"].includes(r)?[]:[new zt(e,n,`projection expected, invalid type "${r}" found`)]:[new zt(e,n,`projection expected, invalid array ${JSON.stringify(n)} found`)]},string:Ni,formatted:function(t){return 0===Ni(t).length?[]:Di(t)},resolvedImage:function(t){return 0===Ni(t).length?[]:Di(t)},padding:function(t){const e=t.key,n=t.value;if("array"===Zr(n)){if(n.length<1||n.length>4)return [new zt(e,n,`padding requires 1 to 4 values; ${n.length} values found`)];const r={type:"number"};let i=[];for(let s=0;s[]}})),t.constants&&(n=n.concat(Mi({key:"constants",value:t.constants}))),es(n)}function ts(t){return function(e){return t(Object.assign({},e,{validateSpec:Ki}))}}function es(t){return [].concat(t).sort(((t,e)=>t.line-e.line))}function ns(t){return function(...e){return es(t.apply(this,e))}}Qi.source=ns(ts(ji)),Qi.sprite=ns(ts(Yi)),Qi.glyphs=ns(ts(Ji)),Qi.light=ns(ts(qi)),Qi.sky=ns(ts(Gi)),Qi.terrain=ns(ts(Xi)),Qi.state=ns(ts(Hi)),Qi.layer=ns(ts(Ri)),Qi.filter=ns(ts(Ci)),Qi.paintProperty=ns(ts(Oi)),Qi.layoutProperty=ns(ts($i));const rs={type:"enum","property-type":"data-constant",expression:{interpolated:!1,parameters:["global-state"]},values:{visible:{},none:{}},transition:!1,default:"visible"};class is{constructor(t,e){this._globalState=e,this.setValue(t);}evaluate(){var t;return null!==(t=this._literalValue)&&void 0!==t?t:this._compiledValue.evaluate({})}setValue(t){if(null==t||"visible"===t||"none"===t)return this._literalValue="none"===t?"none":"visible",this._compiledValue=void 0,void(this._globalStateRefs=new Set);const e=si(t,rs,this._globalState);if("error"===e.result)throw this._literalValue="visible",this._compiledValue=void 0,new Error(e.value.map((t=>`${t.key}: ${t.message}`)).join(", "));this._literalValue=void 0,this._compiledValue=e.value,this._globalStateRefs=hi(e.value.expression);}getGlobalStateRefs(){return this._globalStateRefs}}const ss=wt,os=Qi,as=os.light,ls=os.sky,us=os.paintProperty,cs=os.layoutProperty;function hs(t,e){let n=!1;if(null==e?void 0:e.length)for(const r of e)t.fire(new vt(new Error(r.message))),n=!0;return n}class ps{constructor(t,e,n){const r=this.cells=[];if(t instanceof ArrayBuffer){this.arrayBuffer=t;const i=new Int32Array(this.arrayBuffer);t=i[0],this.d=(e=i[1])+2*(n=i[2]);for(let t=0;t=u[l+0]&&r>=u[l+1])?(o[c]=!0,s.push(i[c])):o[c]=!1;}}}_forEachCell(t,e,n,r,i,s,o,a){const l=this._convertToCellCoord(t),u=this._convertToCellCoord(e),c=this._convertToCellCoord(n),h=this._convertToCellCoord(r);for(let p=l;p<=c;p++)for(let l=u;l<=h;l++){const u=this.d*l+p;if((!a||a(this._convertFromCellCoord(p),this._convertFromCellCoord(l),this._convertFromCellCoord(p+1),this._convertFromCellCoord(l+1)))&&i.call(this,t,e,n,r,u,s,o,a))return}}_convertFromCellCoord(t){return (t-this.padding)/this.scale}_convertToCellCoord(t){return Math.max(0,Math.min(this.d-1,Math.floor(t*this.scale)+this.padding))}toArrayBuffer(){if(this.arrayBuffer)return this.arrayBuffer;const t=this.cells,e=3+this.cells.length+1+1;let n=0;for(const t of this.cells)n+=t.length;const r=new Int32Array(e+n+this.keys.length+this.bboxes.length);r[0]=this.extent,r[1]=this.n,r[2]=this.padding;let i=e;for(let e=0;en?(this.lastIntegerZoom=n+1,this.lastIntegerZoomTime=e):this.lastFloorZoom{try{return new RegExp(`\\p{sc=${t}}`,"u").source}catch(t){return null}})).filter((t=>t));return new RegExp(e.join("|"),"u")}const Is=ks(["Arab","Dupl","Mong","Ougr","Syrc"]);function Es(t){return !Is.test(String.fromCodePoint(t))}function Ts(t){return !(_s(t)||(e=t,/[\xA7\xA9\xAE\xB1\xBC-\xBE\xD7\xF7\u2016\u2020\u2021\u2030\u2031\u203B\u203C\u2042\u2047-\u2049\u2051\u2100-\u218F\u221E\u2234\u2235\u2300-\u2307\u230C-\u231F\u2324-\u2328\u232B\u237D-\u239A\u23BE-\u23CD\u23CF\u23D1-\u23DB\u23E2-\u2422\u2424-\u24FF\u25A0-\u2619\u2620-\u2767\u2776-\u2793\u2B12-\u2B2F\u2B50-\u2B59\u2BB8-\u2BEB\u3000-\u303F\u30A0-\u30FF\uE000-\uF8FF\uFE30-\uFE6F\uFF00-\uFFEF\uFFFC\uFFFD]|[\uDB80-\uDBFF][\uDC00-\uDFFF]/gim.test(String.fromCodePoint(e))));var e;}const Fs=ks(["Adlm","Arab","Armi","Avst","Chrs","Cprt","Egyp","Elym","Gara","Hatr","Hebr","Hung","Khar","Lydi","Mand","Mani","Mend","Merc","Mero","Narb","Nbat","Nkoo","Orkh","Palm","Phli","Phlp","Phnx","Prti","Rohg","Samr","Sarb","Sogo","Syrc","Thaa","Todr","Yezi"]);function Ps(t){return Fs.test(String.fromCodePoint(t))}function Ds(t,e){return !(!e&&Ps(t)||/[\u0900-\u0DFF\u0F00-\u109F\u1780-\u17FF]/gim.test(String.fromCodePoint(t)))}function zs(t){for(const e of t)if(Ps(e.codePointAt(0)))return !0;return !1}const Bs=new class{constructor(){this.TIMEOUT=5e3,this.applyArabicShaping=null,this.processBidirectionalText=null,this.processStyledBidirectionalText=null,this.pluginStatus="unavailable",this.pluginURL=null,this.loadScriptResolve=()=>{};}setState(t){this.pluginStatus=t.pluginStatus,this.pluginURL=t.pluginURL;}getState(){return {pluginStatus:this.pluginStatus,pluginURL:this.pluginURL}}setMethods(t){if(Bs.isParsed())throw new Error("RTL text plugin already registered.");this.applyArabicShaping=t.applyArabicShaping,this.processBidirectionalText=t.processBidirectionalText,this.processStyledBidirectionalText=t.processStyledBidirectionalText,this.loadScriptResolve();}isParsed(){return null!=this.applyArabicShaping&&null!=this.processBidirectionalText&&null!=this.processStyledBidirectionalText}getRTLTextPluginStatus(){return this.pluginStatus}syncState(t,n){return e(this,void 0,void 0,(function*(){if(this.isParsed())return this.getState();if("loading"!==t.pluginStatus)return this.setState(t),t;const e=t.pluginURL,r=new Promise((t=>{this.loadScriptResolve=t;}));n(e);const i=new Promise((t=>setTimeout((()=>t()),this.TIMEOUT)));if(yield Promise.race([r,i]),this.isParsed()){const t={pluginStatus:"loaded",pluginURL:e};return this.setState(t),t}throw this.setState({pluginStatus:"error",pluginURL:""}),new Error(`RTL Text Plugin failed to import scripts from ${e}`)}))}};class Cs{constructor(t,e){this.isSupportedScript=Vs,this.zoom=t,e?(this.now=e.now||0,this.fadeDuration=e.fadeDuration||0,this.zoomHistory=e.zoomHistory||new bs,this.transition=e.transition||{}):(this.now=0,this.fadeDuration=0,this.zoomHistory=new bs,this.transition={});}crossFadingFactor(){return 0===this.fadeDuration?1:Math.min((this.now-this.zoomHistory.lastIntegerZoomTime)/this.fadeDuration,1)}getCrossfadeParameters(){const t=this.zoom,e=t-Math.floor(t),n=this.crossFadingFactor();return t>this.zoomHistory.lastIntegerZoom?{fromScale:2,toScale:1,t:e+(1-e)*n}:{fromScale:.5,toScale:1,t:1-(1-n)*e}}}function Vs(t){return function(t,e){for(const n of t)if(!Ds(n.codePointAt(0),e))return !1;return !0}(t,"loaded"===Bs.getRTLTextPluginStatus())}const Ls="-transition";class Os{constructor(t,e,n){this.property=t,this.value=e,this.expression=function(t,e,n){if(Hr(t))return new ui(t,e);if(ii(t)){const r=li(t,e,n);if("error"===r.result)throw new Error(r.value.map((t=>`${t.key}: ${t.message}`)).join(", "));return r.value}{let n=t;return "color"===e.type&&"string"==typeof t?n=Te.parse(t):"padding"!==e.type||"number"!=typeof t&&!Array.isArray(t)?"numberArray"!==e.type||"number"!=typeof t&&!Array.isArray(t)?"colorArray"!==e.type||"string"!=typeof t&&!Array.isArray(t)?"variableAnchorOffsetCollection"===e.type&&Array.isArray(t)?n=$e.parse(t):"projectionDefinition"===e.type&&"string"==typeof t&&(n=Ne.parse(t)):n=Ve.parse(t):n=Ce.parse(t):n=Be.parse(t),{globalStateRefs:new Set,_globalState:null,kind:"constant",evaluate:()=>n}}}(void 0===e?t.specification.default:e,t.specification,n);}isDataDriven(){return "source"===this.expression.kind||"composite"===this.expression.kind}getGlobalStateRefs(){return this.expression.globalStateRefs||new Set}possiblyEvaluate(t,e,n){return this.property.possiblyEvaluate(this,t,e,n)}}class $s{constructor(t,e){this.property=t,this.value=new Os(t,void 0,e);}transitioned(t,e){return new Ns(this.property,this.value,e,$({},t.transition,this.transition),t.now)}untransitioned(){return new Ns(this.property,this.value,null,{},0)}}class Rs{constructor(t,e){this._properties=t,this._values=Object.create(t.defaultTransitionablePropertyValues),this._globalState=e;}hasProperty(t){return t in this._properties.defaultTransitionablePropertyValues}getValue(t){return j(this._values[t].value.value)}setValue(t,e){Object.hasOwn(this._values,t)||(this._values[t]=new $s(this._values[t].property,this._globalState)),this._values[t].value=new Os(this._values[t].property,null===e?void 0:j(e),this._globalState);}getTransition(t){return j(this._values[t].transition)}setTransition(t,e){Object.hasOwn(this._values,t)||(this._values[t]=new $s(this._values[t].property,this._globalState)),this._values[t].transition=j(e)||void 0;}serialize(){const t={};for(const e of Object.keys(this._values)){const n=this.getValue(e);void 0!==n&&(t[e]=n);const r=this.getTransition(e);void 0!==r&&(t[`${e}${Ls}`]=r);}return t}transitioned(t,e){const n=new Us(this._properties);for(const r of Object.keys(this._values))n._values[r]=this._values[r].transitioned(t,e._values[r]);return n}untransitioned(){const t=new Us(this._properties);for(const e of Object.keys(this._values))t._values[e]=this._values[e].untransitioned();return t}}class Ns{constructor(t,e,n,r,i){this.property=t,this.value=e,this.begin=i+r.delay||0,this.end=this.begin+r.duration||0,t.specification.transition&&(r.delay||r.duration)&&(this.prior=n);}possiblyEvaluate(t,e,n){const r=t.now||0,i=this.value.possiblyEvaluate(t,e,n),s=this.prior;if(s){if(r>this.end)return this.prior=null,i;if(this.value.isDataDriven())return this.prior=null,i;if(rr.zoomHistory.lastIntegerZoom?{from:t,to:e}:{from:n,to:e}}interpolate(t){return t}}class Hs{constructor(t){this.specification=t;}possiblyEvaluate(t,e,n,r){if(void 0!==t.value){if("constant"===t.expression.kind){const i=t.expression.evaluate(e,null,{},n,r);return this._calculate(i,i,i,e)}return this._calculate(t.expression.evaluate(new Cs(Math.floor(e.zoom-1),e)),t.expression.evaluate(new Cs(Math.floor(e.zoom),e)),t.expression.evaluate(new Cs(Math.floor(e.zoom+1),e)),e)}}_calculate(t,e,n,r){return r.zoom>r.zoomHistory.lastIntegerZoom?{from:t,to:e}:{from:n,to:e}}interpolate(t){return t}}class Ws{constructor(t){this.specification=t;}possiblyEvaluate(t,e,n,r){return !!t.expression.evaluate(e,null,{},n,r)}interpolate(){return !1}}class Ks{constructor(t){this.properties=t,this.defaultPropertyValues={},this.defaultTransitionablePropertyValues={},this.defaultTransitioningPropertyValues={},this.defaultPossiblyEvaluatedValues={},this.overridableProperties=[];for(const e in t){const n=t[e];n.specification.overridable&&this.overridableProperties.push(e);const r=this.defaultPropertyValues[e]=new Os(n,void 0,void 0),i=this.defaultTransitionablePropertyValues[e]=new $s(n,void 0);this.defaultTransitioningPropertyValues[e]=i.untransitioned(),this.defaultPossiblyEvaluatedValues[e]=r.possiblyEvaluate({});}}}ds("DataDrivenProperty",Ys),ds("DataConstantProperty",Xs),ds("CrossFadedDataDrivenProperty",Zs),ds("CrossFadedProperty",Hs),ds("ColorRampProperty",Ws);const Js=" is a PAINT property not a LAYOUT property. Use get/setPaintProperty instead?",Qs=" is a LAYOUT property not a PAINT property. Use get/setLayoutProperty instead?";class to extends bt{constructor(t,e,n){if(super(),this.id=t.id,this.type=t.type,this._globalState=n,this._featureFilter={filter:()=>!0,needGeometry:!1,getGlobalStateRefs:()=>new Set},this._visibilityExpression=function(t,e){return new is(t,e)}(this.visibility,n),"custom"!==t.type&&(this.metadata=t.metadata,this.minzoom=t.minzoom,this.maxzoom=t.maxzoom,"background"!==t.type&&(this.source=t.source,this.sourceLayer=t["source-layer"],this.filter=t.filter,this._featureFilter=yi(t.filter,n)),e.layout&&(this._unevaluatedLayout=new js(e.layout,n)),e.paint)){this._transitionablePaint=new Rs(e.paint,n);for(const e in t.paint)this.setPaintProperty(e,t.paint[e],{validate:!1});for(const e in t.layout)this.setLayoutProperty(e,t.layout[e],{validate:!1});this._transitioningPaint=this._transitionablePaint.untransitioned(),this.paint=new Gs(e.paint);}}setFilter(t){this.filter=t,this._featureFilter=yi(t,this._globalState);}getCrossfadeParameters(){return this._crossfadeParameters}getLayoutProperty(t){var e;if("visibility"===t)return this.visibility;if(null===(e=this._transitionablePaint)||void 0===e?void 0:e.hasProperty(t))throw new Error(t+Js);if(!this._unevaluatedLayout)throw new Error(`Cannot get layout property "${t}" on layer type "${this.type}" which has no layout properties.`);return this._unevaluatedLayout.getValue(t)}getLayoutAffectingGlobalStateRefs(){const t=new Set;for(const e of this._visibilityExpression.getGlobalStateRefs())t.add(e);if(this._unevaluatedLayout)for(const e in this._unevaluatedLayout._values){const n=this._unevaluatedLayout._values[e];for(const e of n.getGlobalStateRefs())t.add(e);}for(const e of this._featureFilter.getGlobalStateRefs())t.add(e);return t}getPaintAffectingGlobalStateRefs(){var t;const e=new globalThis.Map;if(this._transitionablePaint)for(const n in this._transitionablePaint._values){const r=this._transitionablePaint._values[n].value;for(const i of r.getGlobalStateRefs()){const s=null!==(t=e.get(i))&&void 0!==t?t:[];s.push({name:n,value:r.value}),e.set(i,s);}}return e}getVisibilityAffectingGlobalStateRefs(){return this._visibilityExpression.getGlobalStateRefs()}setLayoutProperty(t,e,n={}){var r;if("visibility"===t)return this.visibility=e,this._visibilityExpression.setValue(e),void this.recalculateVisibility();(null===(r=this._transitionablePaint)||void 0===r?void 0:r.hasProperty(t))?this.fire(new vt(new Error(t+Js))):null!=e&&this._validate(cs,`layers.${this.id}.layout.${t}`,t,e,n)||this._unevaluatedLayout.setValue(t,e);}getPaintProperty(t){var e,n;if(t.endsWith(Ls)){const n=t.slice(0,-11);if("visibility"===n||(null===(e=this._unevaluatedLayout)||void 0===e?void 0:e.hasProperty(n)))throw new Error(t+Qs);return this._transitionablePaint.getTransition(n)}if("visibility"===t||(null===(n=this._unevaluatedLayout)||void 0===n?void 0:n.hasProperty(t)))throw new Error(t+Qs);return this._transitionablePaint.getValue(t)}setPaintProperty(t,e,n={}){var r;if("visibility"===t||(null===(r=this._unevaluatedLayout)||void 0===r?void 0:r.hasProperty(t)))return this.fire(new vt(new Error(t+Qs))),!1;if(null!=e&&this._validate(us,`layers.${this.id}.paint.${t}`,t,e,n))return !1;if(t.endsWith(Ls))return this._transitionablePaint.setTransition(t.slice(0,-11),e||void 0),!1;{const n=this._transitionablePaint._values[t],r="cross-faded-data-driven"===n.property.specification["property-type"],i=n.value.isDataDriven(),s=n.value;this._transitionablePaint.setValue(t,e),this._handleSpecialPaintPropertyUpdate(t);const o=this._transitionablePaint._values[t].value;return o.isDataDriven()||i||r||this._handleOverridablePaintPropertyUpdate(t,s,o)}}_handleSpecialPaintPropertyUpdate(t){}_handleOverridablePaintPropertyUpdate(t,e,n){return !1}isHidden(t=this.minzoom,e=!1){return !!(this.minzoom&&t<(e?Math.floor(this.minzoom):this.minzoom))||!!(this.maxzoom&&t>=this.maxzoom)||"none"===this._evaluatedVisibility}updateTransitions(t){this._transitioningPaint=this._transitionablePaint.transitioned(t,this._transitioningPaint);}hasTransition(){return this._transitioningPaint.hasTransition()}recalculateVisibility(){this._evaluatedVisibility=this._visibilityExpression.evaluate();}recalculate(t,e){t.getCrossfadeParameters&&(this._crossfadeParameters=t.getCrossfadeParameters()),this._unevaluatedLayout&&(this.layout=this._unevaluatedLayout.possiblyEvaluate(t,void 0,e)),this.paint=this._transitioningPaint.possiblyEvaluate(t,void 0,e);}serialize(){var t,e;const n={id:this.id,type:this.type,source:this.source,"source-layer":this.sourceLayer,metadata:this.metadata,minzoom:this.minzoom,maxzoom:this.maxzoom,filter:this.filter,layout:null===(t=this._unevaluatedLayout)||void 0===t?void 0:t.serialize(),paint:null===(e=this._transitionablePaint)||void 0===e?void 0:e.serialize()};return this.visibility&&(n.layout||(n.layout={}),n.layout.visibility=this.visibility),U(n,((t,e)=>!(void 0===t||"layout"===e&&!Object.keys(t).length||"paint"===e&&!Object.keys(t).length)))}_validate(t,e,n,r,i={}){return !1!==(null==i?void 0:i.validate)&&hs(this,t.call(os,{key:e,layerType:this.type,objectKey:n,value:r,styleSpec:wt,style:{glyphs:!0,sprite:!0}}))}is3D(){return !1}isTileClipped(){return !1}hasOffscreenPass(){return !1}resize(){}isStateDependent(){for(const t in this.paint._values){const e=this.paint.get(t);if(e instanceof qs&&Gr(e.property.specification)&&("source"===e.value.kind||"composite"===e.value.kind)&&e.value.isStateDependent)return !0}return !1}}let eo;var no={get paint(){return eo=eo||new Ks({"raster-opacity":new Xs(wt.paint_raster["raster-opacity"]),"raster-hue-rotate":new Xs(wt.paint_raster["raster-hue-rotate"]),"raster-brightness-min":new Xs(wt.paint_raster["raster-brightness-min"]),"raster-brightness-max":new Xs(wt.paint_raster["raster-brightness-max"]),"raster-saturation":new Xs(wt.paint_raster["raster-saturation"]),"raster-contrast":new Xs(wt.paint_raster["raster-contrast"]),resampling:new Xs(wt.paint_raster.resampling),"raster-resampling":new Xs(wt.paint_raster["raster-resampling"]),"raster-fade-duration":new Xs(wt.paint_raster["raster-fade-duration"])})}};class ro extends to{constructor(t,e){super(t,no,e);}}const io={Int8:Int8Array,Uint8:Uint8Array,Int16:Int16Array,Uint16:Uint16Array,Int32:Int32Array,Uint32:Uint32Array,Float32:Float32Array};class so{constructor(t,e){this._structArray=t,this._pos1=e*this.size,this._pos2=this._pos1/2,this._pos4=this._pos1/4,this._pos8=this._pos1/8;}}class oo{constructor(){this.isTransferred=!1,this.capacity=-1,this.resize(0);}static serialize(t,e){return t._trim(),e&&(t.isTransferred=!0,e.push(t.arrayBuffer)),{length:t.length,arrayBuffer:t.arrayBuffer}}static deserialize(t){const e=Object.create(this.prototype);return e.arrayBuffer=t.arrayBuffer,e.length=t.length,e.capacity=t.arrayBuffer.byteLength/e.bytesPerElement,e._refreshViews(),e}_trim(){this.length!==this.capacity&&(this.capacity=this.length,this.arrayBuffer=this.arrayBuffer.slice(0,this.length*this.bytesPerElement),this._refreshViews());}clear(){this.length=0;}resize(t){this.reserve(t),this.length=t;}reserve(t){if(t>this.capacity){this.capacity=Math.max(t,Math.floor(5*this.capacity),128),this.arrayBuffer=new ArrayBuffer(this.capacity*this.bytesPerElement);const e=this.uint8;this._refreshViews(),e&&this.uint8.set(e);}}_refreshViews(){throw new Error("_refreshViews() must be implemented by each concrete StructArray layout")}freeBufferAfterUpload(){this.arrayBuffer=new ArrayBuffer(0),this._refreshViews();}}function ao(t,e=1){let n=0,r=0;return {members:t.map((t=>{const i=io[t.type].BYTES_PER_ELEMENT,s=n=lo(n,Math.max(e,i)),o=t.components||1;return r=Math.max(r,i),n+=i*o,{name:t.name,type:t.type,components:o,offset:s}})),size:lo(n,Math.max(r,e)),alignment:e}}function lo(t,e){return Math.ceil(t/e)*e}class uo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e){const n=this.length;return this.resize(n+1),this.emplace(n,t,e)}emplace(t,e,n){const r=2*t;return this.int16[r+0]=e,this.int16[r+1]=n,t}}uo.prototype.bytesPerElement=4,ds("StructArrayLayout2i4",uo);class co extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,n)}emplace(t,e,n,r){const i=3*t;return this.int16[i+0]=e,this.int16[i+1]=n,this.int16[i+2]=r,t}}co.prototype.bytesPerElement=6,ds("StructArrayLayout3i6",co);class ho extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n,r){const i=this.length;return this.resize(i+1),this.emplace(i,t,e,n,r)}emplace(t,e,n,r,i){const s=4*t;return this.int16[s+0]=e,this.int16[s+1]=n,this.int16[s+2]=r,this.int16[s+3]=i,t}}ho.prototype.bytesPerElement=8,ds("StructArrayLayout4i8",ho);class po extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,n,r,i,s)}emplace(t,e,n,r,i,s,o){const a=6*t;return this.int16[a+0]=e,this.int16[a+1]=n,this.int16[a+2]=r,this.int16[a+3]=i,this.int16[a+4]=s,this.int16[a+5]=o,t}}po.prototype.bytesPerElement=12,ds("StructArrayLayout2i4i12",po);class fo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,n,r,i,s)}emplace(t,e,n,r,i,s,o){const a=4*t,l=8*t;return this.int16[a+0]=e,this.int16[a+1]=n,this.uint8[l+4]=r,this.uint8[l+5]=i,this.uint8[l+6]=s,this.uint8[l+7]=o,t}}fo.prototype.bytesPerElement=8,ds("StructArrayLayout2i4ub8",fo);class yo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e){const n=this.length;return this.resize(n+1),this.emplace(n,t,e)}emplace(t,e,n){const r=2*t;return this.float32[r+0]=e,this.float32[r+1]=n,t}}yo.prototype.bytesPerElement=8,ds("StructArrayLayout2f8",yo);class mo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s,o,a,l,u){const c=this.length;return this.resize(c+1),this.emplace(c,t,e,n,r,i,s,o,a,l,u)}emplace(t,e,n,r,i,s,o,a,l,u,c){const h=10*t;return this.uint16[h+0]=e,this.uint16[h+1]=n,this.uint16[h+2]=r,this.uint16[h+3]=i,this.uint16[h+4]=s,this.uint16[h+5]=o,this.uint16[h+6]=a,this.uint16[h+7]=l,this.uint16[h+8]=u,this.uint16[h+9]=c,t}}mo.prototype.bytesPerElement=20,ds("StructArrayLayout10ui20",mo);class go extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s,o,a){const l=this.length;return this.resize(l+1),this.emplace(l,t,e,n,r,i,s,o,a)}emplace(t,e,n,r,i,s,o,a,l){const u=8*t;return this.uint16[u+0]=e,this.uint16[u+1]=n,this.uint16[u+2]=r,this.uint16[u+3]=i,this.uint16[u+4]=s,this.uint16[u+5]=o,this.uint16[u+6]=a,this.uint16[u+7]=l,t}}go.prototype.bytesPerElement=16,ds("StructArrayLayout8ui16",go);class xo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s,o,a,l,u,c,h){const p=this.length;return this.resize(p+1),this.emplace(p,t,e,n,r,i,s,o,a,l,u,c,h)}emplace(t,e,n,r,i,s,o,a,l,u,c,h,p){const f=12*t;return this.int16[f+0]=e,this.int16[f+1]=n,this.int16[f+2]=r,this.int16[f+3]=i,this.uint16[f+4]=s,this.uint16[f+5]=o,this.uint16[f+6]=a,this.uint16[f+7]=l,this.int16[f+8]=u,this.int16[f+9]=c,this.int16[f+10]=h,this.int16[f+11]=p,t}}xo.prototype.bytesPerElement=24,ds("StructArrayLayout4i4ui4i24",xo);class vo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,n){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,n)}emplace(t,e,n,r){const i=3*t;return this.float32[i+0]=e,this.float32[i+1]=n,this.float32[i+2]=r,t}}vo.prototype.bytesPerElement=12,ds("StructArrayLayout3f12",vo);class bo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer);}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.uint32[1*t+0]=e,t}}bo.prototype.bytesPerElement=4,ds("StructArrayLayout1ul4",bo);class wo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s,o,a,l){const u=this.length;return this.resize(u+1),this.emplace(u,t,e,n,r,i,s,o,a,l)}emplace(t,e,n,r,i,s,o,a,l,u){const c=10*t,h=5*t;return this.int16[c+0]=e,this.int16[c+1]=n,this.int16[c+2]=r,this.int16[c+3]=i,this.int16[c+4]=s,this.int16[c+5]=o,this.uint32[h+3]=a,this.uint16[c+8]=l,this.uint16[c+9]=u,t}}wo.prototype.bytesPerElement=20,ds("StructArrayLayout6i1ul2ui20",wo);class _o extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,n,r,i,s)}emplace(t,e,n,r,i,s,o){const a=6*t;return this.int16[a+0]=e,this.int16[a+1]=n,this.int16[a+2]=r,this.int16[a+3]=i,this.int16[a+4]=s,this.int16[a+5]=o,t}}_o.prototype.bytesPerElement=12,ds("StructArrayLayout2i2i2i12",_o);class So extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i){const s=this.length;return this.resize(s+1),this.emplace(s,t,e,n,r,i)}emplace(t,e,n,r,i,s){const o=4*t,a=8*t;return this.float32[o+0]=e,this.float32[o+1]=n,this.float32[o+2]=r,this.int16[a+6]=i,this.int16[a+7]=s,t}}So.prototype.bytesPerElement=16,ds("StructArrayLayout2f1f2i16",So);class Ao extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,n,r,i,s)}emplace(t,e,n,r,i,s,o){const a=16*t,l=4*t,u=8*t;return this.uint8[a+0]=e,this.uint8[a+1]=n,this.float32[l+1]=r,this.float32[l+2]=i,this.int16[u+6]=s,this.int16[u+7]=o,t}}Ao.prototype.bytesPerElement=16,ds("StructArrayLayout2ub2f2i16",Ao);class Mo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,n){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,n)}emplace(t,e,n,r){const i=3*t;return this.uint16[i+0]=e,this.uint16[i+1]=n,this.uint16[i+2]=r,t}}Mo.prototype.bytesPerElement=6,ds("StructArrayLayout3ui6",Mo);class ko extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m){const g=this.length;return this.resize(g+1),this.emplace(g,t,e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m)}emplace(t,e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m,g){const x=24*t,v=12*t,b=48*t;return this.int16[x+0]=e,this.int16[x+1]=n,this.uint16[x+2]=r,this.uint16[x+3]=i,this.uint32[v+2]=s,this.uint32[v+3]=o,this.uint32[v+4]=a,this.uint16[x+10]=l,this.uint16[x+11]=u,this.uint16[x+12]=c,this.float32[v+7]=h,this.float32[v+8]=p,this.uint8[b+36]=f,this.uint8[b+37]=d,this.uint8[b+38]=y,this.uint32[v+10]=m,this.int16[x+22]=g,t}}ko.prototype.bytesPerElement=48,ds("StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48",ko);class Io extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,M,k,I){const E=this.length;return this.resize(E+1),this.emplace(E,t,e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,M,k,I)}emplace(t,e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,M,k,I,E){const T=32*t,F=16*t;return this.int16[T+0]=e,this.int16[T+1]=n,this.int16[T+2]=r,this.int16[T+3]=i,this.int16[T+4]=s,this.int16[T+5]=o,this.int16[T+6]=a,this.int16[T+7]=l,this.uint16[T+8]=u,this.uint16[T+9]=c,this.uint16[T+10]=h,this.uint16[T+11]=p,this.uint16[T+12]=f,this.uint16[T+13]=d,this.uint16[T+14]=y,this.uint16[T+15]=m,this.uint16[T+16]=g,this.uint16[T+17]=x,this.uint16[T+18]=v,this.uint16[T+19]=b,this.uint16[T+20]=w,this.uint16[T+21]=_,this.uint16[T+22]=S,this.uint32[F+12]=A,this.float32[F+13]=M,this.float32[F+14]=k,this.uint16[T+30]=I,this.uint16[T+31]=E,t}}Io.prototype.bytesPerElement=64,ds("StructArrayLayout8i15ui1ul2f2ui64",Io);class Eo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.float32[1*t+0]=e,t}}Eo.prototype.bytesPerElement=4,ds("StructArrayLayout1f4",Eo);class To extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,n){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,n)}emplace(t,e,n,r){const i=3*t;return this.uint16[6*t+0]=e,this.float32[i+1]=n,this.float32[i+2]=r,t}}To.prototype.bytesPerElement=12,ds("StructArrayLayout1ui2f12",To);class Fo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e,n){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,n)}emplace(t,e,n,r){const i=4*t;return this.uint32[2*t+0]=e,this.uint16[i+2]=n,this.uint16[i+3]=r,t}}Fo.prototype.bytesPerElement=8,ds("StructArrayLayout1ul2ui8",Fo);class Po extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t,e){const n=this.length;return this.resize(n+1),this.emplace(n,t,e)}emplace(t,e,n){const r=2*t;return this.uint16[r+0]=e,this.uint16[r+1]=n,t}}Po.prototype.bytesPerElement=4,ds("StructArrayLayout2ui4",Po);class Do extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer);}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.uint16[1*t+0]=e,t}}Do.prototype.bytesPerElement=2,ds("StructArrayLayout1ui2",Do);class zo extends oo{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer);}emplaceBack(t,e,n,r){const i=this.length;return this.resize(i+1),this.emplace(i,t,e,n,r)}emplace(t,e,n,r,i){const s=4*t;return this.float32[s+0]=e,this.float32[s+1]=n,this.float32[s+2]=r,this.float32[s+3]=i,t}}zo.prototype.bytesPerElement=16,ds("StructArrayLayout4f16",zo);class Bo extends so{get anchorPointX(){return this._structArray.int16[this._pos2+0]}get anchorPointY(){return this._structArray.int16[this._pos2+1]}get x1(){return this._structArray.int16[this._pos2+2]}get y1(){return this._structArray.int16[this._pos2+3]}get x2(){return this._structArray.int16[this._pos2+4]}get y2(){return this._structArray.int16[this._pos2+5]}get featureIndex(){return this._structArray.uint32[this._pos4+3]}get sourceLayerIndex(){return this._structArray.uint16[this._pos2+8]}get bucketIndex(){return this._structArray.uint16[this._pos2+9]}get anchorPoint(){return new n(this.anchorPointX,this.anchorPointY)}}Bo.prototype.size=20;class Co extends wo{get(t){return new Bo(this,t)}}ds("CollisionBoxArray",Co);class Vo extends so{get anchorX(){return this._structArray.int16[this._pos2+0]}get anchorY(){return this._structArray.int16[this._pos2+1]}get glyphStartIndex(){return this._structArray.uint16[this._pos2+2]}get numGlyphs(){return this._structArray.uint16[this._pos2+3]}get vertexStartIndex(){return this._structArray.uint32[this._pos4+2]}get lineStartIndex(){return this._structArray.uint32[this._pos4+3]}get lineLength(){return this._structArray.uint32[this._pos4+4]}get segment(){return this._structArray.uint16[this._pos2+10]}get lowerSize(){return this._structArray.uint16[this._pos2+11]}get upperSize(){return this._structArray.uint16[this._pos2+12]}get lineOffsetX(){return this._structArray.float32[this._pos4+7]}get lineOffsetY(){return this._structArray.float32[this._pos4+8]}get writingMode(){return this._structArray.uint8[this._pos1+36]}get placedOrientation(){return this._structArray.uint8[this._pos1+37]}set placedOrientation(t){this._structArray.uint8[this._pos1+37]=t;}get hidden(){return this._structArray.uint8[this._pos1+38]}set hidden(t){this._structArray.uint8[this._pos1+38]=t;}get crossTileID(){return this._structArray.uint32[this._pos4+10]}set crossTileID(t){this._structArray.uint32[this._pos4+10]=t;}get associatedIconIndex(){return this._structArray.int16[this._pos2+22]}}Vo.prototype.size=48;class Lo extends ko{get(t){return new Vo(this,t)}}ds("PlacedSymbolArray",Lo);class Oo extends so{get anchorX(){return this._structArray.int16[this._pos2+0]}get anchorY(){return this._structArray.int16[this._pos2+1]}get rightJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+2]}get centerJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+3]}get leftJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+4]}get verticalPlacedTextSymbolIndex(){return this._structArray.int16[this._pos2+5]}get placedIconSymbolIndex(){return this._structArray.int16[this._pos2+6]}get verticalPlacedIconSymbolIndex(){return this._structArray.int16[this._pos2+7]}get key(){return this._structArray.uint16[this._pos2+8]}get textBoxStartIndex(){return this._structArray.uint16[this._pos2+9]}get textBoxEndIndex(){return this._structArray.uint16[this._pos2+10]}get verticalTextBoxStartIndex(){return this._structArray.uint16[this._pos2+11]}get verticalTextBoxEndIndex(){return this._structArray.uint16[this._pos2+12]}get iconBoxStartIndex(){return this._structArray.uint16[this._pos2+13]}get iconBoxEndIndex(){return this._structArray.uint16[this._pos2+14]}get verticalIconBoxStartIndex(){return this._structArray.uint16[this._pos2+15]}get verticalIconBoxEndIndex(){return this._structArray.uint16[this._pos2+16]}get featureIndex(){return this._structArray.uint16[this._pos2+17]}get numHorizontalGlyphVertices(){return this._structArray.uint16[this._pos2+18]}get numVerticalGlyphVertices(){return this._structArray.uint16[this._pos2+19]}get numIconVertices(){return this._structArray.uint16[this._pos2+20]}get numVerticalIconVertices(){return this._structArray.uint16[this._pos2+21]}get useRuntimeCollisionCircles(){return this._structArray.uint16[this._pos2+22]}get crossTileID(){return this._structArray.uint32[this._pos4+12]}set crossTileID(t){this._structArray.uint32[this._pos4+12]=t;}get textBoxScale(){return this._structArray.float32[this._pos4+13]}get collisionCircleDiameter(){return this._structArray.float32[this._pos4+14]}get textAnchorOffsetStartIndex(){return this._structArray.uint16[this._pos2+30]}get textAnchorOffsetEndIndex(){return this._structArray.uint16[this._pos2+31]}}Oo.prototype.size=64;class $o extends Io{get(t){return new Oo(this,t)}}ds("SymbolInstanceArray",$o);class Ro extends Eo{getoffsetX(t){return this.float32[1*t+0]}}ds("GlyphOffsetArray",Ro);class No extends co{getx(t){return this.int16[3*t+0]}gety(t){return this.int16[3*t+1]}gettileUnitDistanceFromAnchor(t){return this.int16[3*t+2]}}ds("SymbolLineVertexArray",No);class Uo extends so{get textAnchor(){return this._structArray.uint16[this._pos2+0]}get textOffset0(){return this._structArray.float32[this._pos4+1]}get textOffset1(){return this._structArray.float32[this._pos4+2]}}Uo.prototype.size=12;class jo extends To{get(t){return new Uo(this,t)}}ds("TextAnchorOffsetArray",jo);class qo extends so{get featureIndex(){return this._structArray.uint32[this._pos4+0]}get sourceLayerIndex(){return this._structArray.uint16[this._pos2+2]}get bucketIndex(){return this._structArray.uint16[this._pos2+3]}}qo.prototype.size=8;class Go extends Fo{get(t){return new qo(this,t)}}ds("FeatureIndexArray",Go);class Xo extends uo{}class Yo extends uo{}class Zo extends uo{}class Ho extends po{}class Wo extends fo{}class Ko extends yo{}class Jo extends mo{}class Qo extends go{}class ta extends xo{}class ea extends vo{}class na extends bo{}class ra extends _o{}class ia extends Ao{}class sa extends Mo{}class oa extends Po{}const aa=ao([{name:"a_pos",components:2,type:"Int16"}],4),{members:la}=aa;class ua{constructor(t=[]){this._forceNewSegmentOnNextPrepare=!1,this.segments=t;}prepareSegment(t,e,n,r){const i=this.segments[this.segments.length-1];return t>ua.MAX_VERTEX_ARRAY_LENGTH&&G(`Max vertices per segment is ${ua.MAX_VERTEX_ARRAY_LENGTH}: bucket requested ${t}. Consider using the \`fillLargeMeshArrays\` function if you require meshes with more than ${ua.MAX_VERTEX_ARRAY_LENGTH} vertices.`),this._forceNewSegmentOnNextPrepare||!i||i.vertexLength+t>ua.MAX_VERTEX_ARRAY_LENGTH||i.sortKey!==r?this.createNewSegment(e,n,r):i}createNewSegment(t,e,n){const r={vertexOffset:t.length,primitiveOffset:e.length,vertexLength:0,primitiveLength:0,vaos:{}};return void 0!==n&&(r.sortKey=n),this._forceNewSegmentOnNextPrepare=!1,this.segments.push(r),r}getOrCreateLatestSegment(t,e,n){return this.prepareSegment(0,t,e,n)}forceNewSegmentOnNextPrepare(){this._forceNewSegmentOnNextPrepare=!0;}get(){return this.segments}destroy(){for(const t of this.segments)for(const e in t.vaos)t.vaos[e].destroy();}static simpleSegment(t,e,n,r){return new ua([{vertexOffset:t,primitiveOffset:e,vertexLength:n,primitiveLength:r,vaos:{},sortKey:0}])}}function ca(t,e){return 256*(t=L(Math.floor(t),0,255))+L(Math.floor(e),0,255)}ua.MAX_VERTEX_ARRAY_LENGTH=Math.pow(2,16)-1,ds("SegmentVector",ua);const ha=ao([{name:"a_pattern_from",components:4,type:"Uint16"},{name:"a_pattern_to",components:4,type:"Uint16"},{name:"a_pixel_ratio_from",components:1,type:"Uint16"},{name:"a_pixel_ratio_to",components:1,type:"Uint16"}]),pa=ao([{name:"a_dasharray_from",components:4,type:"Uint16"},{name:"a_dasharray_to",components:4,type:"Uint16"}]);var fa,da,ya,ma={exports:{}},ga={exports:{}},xa={exports:{}},va=function(){if(ya)return ma.exports;ya=1;var t=(fa||(fa=1,ga.exports=function(t,e){var n,r,i,s,o,a,l,u;for(r=t.length-(n=3&t.length),i=e,o=3432918353,a=461845907,u=0;u>>16)*o&65535)<<16)&4294967295)<<15|l>>>17))*a+(((l>>>16)*a&65535)<<16)&4294967295)<<13|i>>>19))+((5*(i>>>16)&65535)<<16)&4294967295))+((58964+(s>>>16)&65535)<<16);switch(l=0,n){case 3:l^=(255&t.charCodeAt(u+2))<<16;case 2:l^=(255&t.charCodeAt(u+1))<<8;case 1:i^=l=(65535&(l=(l=(65535&(l^=255&t.charCodeAt(u)))*o+(((l>>>16)*o&65535)<<16)&4294967295)<<15|l>>>17))*a+(((l>>>16)*a&65535)<<16)&4294967295;}return i^=t.length,i=2246822507*(65535&(i^=i>>>16))+((2246822507*(i>>>16)&65535)<<16)&4294967295,i=3266489909*(65535&(i^=i>>>13))+((3266489909*(i>>>16)&65535)<<16)&4294967295,(i^=i>>>16)>>>0}),ga.exports),e=(da||(da=1,xa.exports=function(t,e){for(var n,r=t.length,i=e^r,s=0;r>=4;)n=1540483477*(65535&(n=255&t.charCodeAt(s)|(255&t.charCodeAt(++s))<<8|(255&t.charCodeAt(++s))<<16|(255&t.charCodeAt(++s))<<24))+((1540483477*(n>>>16)&65535)<<16),i=1540483477*(65535&i)+((1540483477*(i>>>16)&65535)<<16)^(n=1540483477*(65535&(n^=n>>>24))+((1540483477*(n>>>16)&65535)<<16)),r-=4,++s;switch(r){case 3:i^=(255&t.charCodeAt(s+2))<<16;case 2:i^=(255&t.charCodeAt(s+1))<<8;case 1:i=1540483477*(65535&(i^=255&t.charCodeAt(s)))+((1540483477*(i>>>16)&65535)<<16);}return i=1540483477*(65535&(i^=i>>>13))+((1540483477*(i>>>16)&65535)<<16),(i^=i>>>15)>>>0}),xa.exports);return ma.exports=t,ma.exports.murmur3=t,ma.exports.murmur2=e,ma.exports}(),ba=r(va);class wa{constructor(){this.ids=[],this.positions=[],this.indexed=!1;}add(t,e,n,r){this.ids.push(_a(t)),this.positions.push(e,n,r);}getPositions(t){if(!this.indexed)throw new Error("Trying to get index, but feature positions are not indexed");const e=_a(t);let n=0,r=this.ids.length-1;for(;n>1;this.ids[t]>=e?r=t:n=t+1;}const i=[];for(;this.ids[n]===e;)i.push({index:this.positions[3*n],start:this.positions[3*n+1],end:this.positions[3*n+2]}),n++;return i}static serialize(t,e){const n=new Float64Array(t.ids),r=new Uint32Array(t.positions);return Sa(n,r,0,n.length-1),e&&e.push(n.buffer,r.buffer),{ids:n,positions:r}}static deserialize(t){const e=new wa;return e.ids=t.ids,e.positions=t.positions,e.indexed=!0,e}}function _a(t){const e=+t;return !isNaN(e)&&e<=Number.MAX_SAFE_INTEGER?e:ba(String(t))}function Sa(t,e,n,r){for(;n>1];let s=n-1,o=r+1;for(;;){do{s++;}while(t[s]i);if(s>=o)break;Aa(t,s,o),Aa(e,3*s,3*o),Aa(e,3*s+1,3*o+1),Aa(e,3*s+2,3*o+2);}o-n`u_${t}`)),this.type=n;}setUniform(t,e,n){t.set(n.constantOr(this.value));}getBinding(t,e,n){return "color"===this.type?new Ea(t,e):new ka(t,e)}}class Da{constructor(t,e){this.uniformNames=e.map((t=>`u_${t}`)),this.patternFrom=null,this.patternTo=null,this.pixelRatioFrom=1,this.pixelRatioTo=1;}setConstantPatternPositions(t,e){this.pixelRatioFrom=e.pixelRatio,this.pixelRatioTo=t.pixelRatio,this.patternFrom=e.tlbr,this.patternTo=t.tlbr;}setConstantDashPositions(t,e){this.dashTo=[0,t.y,t.height,t.width],this.dashFrom=[0,e.y,e.height,e.width];}setUniform(t,e,n,r){let i=null;"u_pattern_to"===r?i=this.patternTo:"u_pattern_from"===r?i=this.patternFrom:"u_dasharray_to"===r?i=this.dashTo:"u_dasharray_from"===r?i=this.dashFrom:"u_pixel_ratio_to"===r?i=this.pixelRatioTo:"u_pixel_ratio_from"===r&&(i=this.pixelRatioFrom),null!==i&&t.set(i);}getBinding(t,e,n){return n.startsWith("u_pattern")||n.startsWith("u_dasharray_")?new Ia(t,e):new ka(t,e)}}class za{constructor(t,e,n,r){this.expression=t,this.type=n,this.maxValue=0,this.paintVertexAttributes=e.map((t=>({name:`a_${t}`,type:"Float32",components:"color"===n?2:1,offset:0}))),this.paintVertexArray=new r;}populatePaintArray(t,e,n){const r=this.paintVertexArray.length,i=this.expression.evaluate(new Cs(0,n),e,{},n.canonical,[],n.formattedSection);this.paintVertexArray.resize(t),this._setPaintValue(r,t,i);}updatePaintArray(t,e,n,r,i){const s=this.expression.evaluate(new Cs(0,i),n,r);this._setPaintValue(t,e,s);}_setPaintValue(t,e,n){if("color"===this.type){const r=Fa(n);for(let n=t;n`u_${t}_t`)),this.type=n,this.useIntegerZoom=r,this.zoom=i,this.maxValue=0,this.paintVertexAttributes=e.map((t=>({name:`a_${t}`,type:"Float32",components:"color"===n?4:2,offset:0}))),this.paintVertexArray=new s;}populatePaintArray(t,e,n){const r=this.expression.evaluate(new Cs(this.zoom,n),e,{},n.canonical,[],n.formattedSection),i=this.expression.evaluate(new Cs(this.zoom+1,n),e,{},n.canonical,[],n.formattedSection),s=this.paintVertexArray.length;this.paintVertexArray.resize(t),this._setPaintValue(s,t,r,i);}updatePaintArray(t,e,n,r,i){const s=this.expression.evaluate(new Cs(this.zoom,i),n,r),o=this.expression.evaluate(new Cs(this.zoom+1,i),n,r);this._setPaintValue(t,e,s,o);}_setPaintValue(t,e,n,r){if("color"===this.type){const i=Fa(n),s=Fa(r);for(let n=t;n`#define HAS_UNIFORM_${t}`)));}return t}getBinderAttributes(){const t=[];for(const e in this.binders){const n=this.binders[e];if(n instanceof za||n instanceof Ba)for(const e of n.paintVertexAttributes)t.push(e.name);else if(n instanceof Ca){const e=n.getVertexAttributes();for(const n of e)t.push(n.name);}}return t}getBinderUniforms(){const t=[];for(const e in this.binders){const n=this.binders[e];if(n instanceof Pa||n instanceof Da||n instanceof Ba)for(const e of n.uniformNames)t.push(e);}return t}getPaintVertexBuffers(){return this._buffers}getUniforms(t,e){const n=[];for(const r in this.binders){const i=this.binders[r];if(i instanceof Pa||i instanceof Da||i instanceof Ba)for(const s of i.uniformNames)if(e[s]){const o=i.getBinding(t,e[s],s);n.push({name:s,property:r,binding:o});}}return n}setUniforms(t,e,n,r){for(const{name:t,property:i,binding:s}of e)this.binders[i].setUniform(s,r,n.get(i),t);}updatePaintBuffers(t){this._buffers=[];for(const e in this.binders){const n=this.binders[e];if(t&&n instanceof Ca){const e=2===t.fromScale?n.zoomInPaintVertexBuffer:n.zoomOutPaintVertexBuffer;e&&this._buffers.push(e);}else (n instanceof za||n instanceof Ba)&&n.paintVertexBuffer&&this._buffers.push(n.paintVertexBuffer);}}upload(t){for(const e in this.binders){const n=this.binders[e];(n instanceof za||n instanceof Ba||n instanceof Ca)&&n.upload(t);}this.updatePaintBuffers();}destroy(){for(const t in this.binders){const e=this.binders[t];(e instanceof za||e instanceof Ba||e instanceof Ca)&&e.destroy();}}}class $a{constructor(t,e,n=()=>!0){this.programConfigurations={};for(const r of t)this.programConfigurations[r.id]=new Oa(r,e,n);this.needsUpload=!1,this._featureMap=new wa,this._bufferOffset=0;}populatePaintArrays(t,e,n,r){for(const n in this.programConfigurations)this.programConfigurations[n].populatePaintArrays(t,e,r);void 0!==e.id&&this._featureMap.add(e.id,n,this._bufferOffset,t),this._bufferOffset=t,this.needsUpload=!0;}updatePaintArrays(t,e,n,r){for(const i of n)this.needsUpload=this.programConfigurations[i.id].updatePaintArrays(t,this._featureMap,e,i,r)||this.needsUpload;}get(t){return this.programConfigurations[t]}upload(t){if(this.needsUpload){for(const e in this.programConfigurations)this.programConfigurations[e].upload(t);this.needsUpload=!1;}}destroy(){for(const t in this.programConfigurations)this.programConfigurations[t].destroy();}}function Ra(t,e){return {"text-opacity":["opacity"],"icon-opacity":["opacity"],"text-color":["fill_color"],"icon-color":["fill_color"],"text-halo-color":["halo_color"],"icon-halo-color":["halo_color"],"text-halo-blur":["halo_blur"],"icon-halo-blur":["halo_blur"],"text-halo-width":["halo_width"],"icon-halo-width":["halo_width"],"line-gap-width":["gapwidth"],"line-dasharray":["dasharray_to","dasharray_from"],"line-pattern":["pattern_to","pattern_from","pixel_ratio_to","pixel_ratio_from"],"fill-pattern":["pattern_to","pattern_from","pixel_ratio_to","pixel_ratio_from"],"fill-extrusion-pattern":["pattern_to","pattern_from","pixel_ratio_to","pixel_ratio_from"]}[t]||[t.replace(`${e}-`,"").replace(/-/g,"_")]}function Na(t,e,n){const r={color:{source:yo,composite:zo},number:{source:Eo,composite:yo}},i=function(t){return {"line-pattern":{source:Jo,composite:Jo},"fill-pattern":{source:Jo,composite:Jo},"fill-extrusion-pattern":{source:Jo,composite:Jo},"line-dasharray":{source:Qo,composite:Qo}}[t]}(t);return (null==i?void 0:i[n])||r[e][n]}ds("ConstantBinder",Pa),ds("CrossFadedConstantBinder",Da),ds("SourceExpressionBinder",za),ds("CrossFadedPatternBinder",Va),ds("CrossFadedDasharrayBinder",La),ds("CompositeExpressionBinder",Ba),ds("ProgramConfiguration",Oa,{omit:["_buffers"]}),ds("ProgramConfigurationSet",$a);const Ua=Math.pow(2,14)-1,ja=-Ua-1;function qa(t){const e=T/t.extent,n=t.loadGeometry();for(const t of n)for(const n of t){const t=Math.round(n.x*e),r=Math.round(n.y*e);n.x=L(t,ja,Ua),n.y=L(r,ja,Ua),(tn.x+1||rn.y+1)&&G("Geometry exceeds allowed extent, reduce your vector tile buffer size");}return n}function Ga(t,e){return {type:t.type,id:t.id,properties:t.properties,geometry:e?qa(t):[]}}const Xa=-32768;function Ya(t,e,n,r,i){t.emplaceBack(Xa+8*e+r,Xa+8*n+i);}class Za{constructor(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasDependencies=!1,this.layoutVertexArray=new Yo,this.indexArray=new sa,this.segments=new ua,this.programConfigurations=new $a(t.layers,t.zoom),this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,n){const r=this.layers[0],i=[];let s=null,o=!1,a="heatmap"===r.type;if("circle"===r.type){const t=r;s=t.layout.get("circle-sort-key"),o=!s.isConstant(),a||(a="map"===t.paint.get("circle-pitch-alignment"));}const l=a?e.subdivisionGranularity.circle:1;for(const{feature:e,id:r,index:a,sourceLayerIndex:l}of t){const t=this.layers[0]._featureFilter.needGeometry,u=Ga(e,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom),u,n))continue;const c=o?s.evaluate(u,{},n):void 0,h={id:r,properties:e.properties,type:e.type,sourceLayerIndex:l,index:a,geometry:t?u.geometry:qa(e),patterns:{},sortKey:c};i.push(h);}o&&i.sort(((t,e)=>t.sortKey-e.sortKey));for(const r of i){const{geometry:i,index:s,sourceLayerIndex:o}=r,a=t[s].feature;this.addFeature(r,i,s,n,l),e.featureIndex.insert(a,i,s,o,this.index);}}update(t,e,n){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,{imagePositions:n});}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,la),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy());}addFeature(t,e,n,r,i=1){let s;switch(i){case 1:s=[0,7];break;case 3:s=[0,2,5,7];break;case 5:s=[0,1,3,4,6,7];break;case 7:s=[0,1,2,3,4,5,6,7];break;default:throw new Error(`Invalid circle bucket granularity: ${i}; valid values are 1, 3, 5, 7.`)}const o=s.length;for(const n of e)for(const e of n){const n=e.x,r=e.y;if(n<0||n>=T||r<0||r>=T)continue;const i=this.segments.prepareSegment(o*o,this.layoutVertexArray,this.indexArray,t.sortKey),a=i.vertexLength;for(let t=0;t1){if(Qa(t,e))return !0;for(const r of e)if(el(r,t,n))return !0}for(const r of t)if(el(r,e,n))return !0;return !1}function Qa(t,e){if(0===t.length||0===e.length)return !1;for(let n=0;n1?n:n.sub(e)._mult(i)._add(e))}function rl(t,e){let n,r,i,s=!1;for(const o of t){n=o;for(let t=0,o=n.length-1;te.y!=i.y>e.y&&e.x<(i.x-r.x)*(e.y-r.y)/(i.y-r.y)+r.x&&(s=!s);}return s}function il(t,e){let n=!1;for(let r=0,i=t.length-1;re.y!=o.y>e.y&&e.x<(o.x-s.x)*(e.y-s.y)/(o.y-s.y)+s.x&&(n=!n);}return n}function sl(t,e,n){const r=n[0],i=n[2];if(t.xi.x&&e.x>i.x||t.yi.y&&e.y>i.y)return !1;const s=X(t,e,n[0]);return s!==X(t,e,n[1])||s!==X(t,e,n[2])||s!==X(t,e,n[3])}function ol(t,e,n){const r=e.paint.get(t).value;return "constant"===r.kind?r.value:n.programConfigurations.get(e.id).getMaxValue(t)}function al(t){return Math.sqrt(t[0]*t[0]+t[1]*t[1])}function ll(t,e,r,i,s){if(!e[0]&&!e[1])return t;const o=n.convert(e)._mult(s);"viewport"===r&&o._rotate(-i);const a=[];for(const e of t)a.push(e.sub(o));return a}function ul(t){const e=[];for(let n=0;nyl(t,e,n,r)))}(l,i,o,a),f=u),dl({queryGeometry:p,size:f,transform:i,unwrappedTileID:o,getElevation:a,pitchAlignment:h,pitchScale:c},r)}}class bl extends Za{}let wl;ds("HeatmapBucket",bl,{omit:["layers"]});var _l={get paint(){return wl=wl||new Ks({"heatmap-radius":new Ys(wt.paint_heatmap["heatmap-radius"]),"heatmap-weight":new Ys(wt.paint_heatmap["heatmap-weight"]),"heatmap-intensity":new Xs(wt.paint_heatmap["heatmap-intensity"]),"heatmap-color":new Ws(wt.paint_heatmap["heatmap-color"]),"heatmap-opacity":new Xs(wt.paint_heatmap["heatmap-opacity"])})}};function Sl(t,{width:e,height:n},r,i){if(i){if(i instanceof Uint8ClampedArray)i=new Uint8Array(i.buffer);else if(i.length!==e*n*r)throw new RangeError(`mismatched image size. expected: ${i.length} but got: ${e*n*r}`)}else i=new Uint8Array(e*n*r);return t.width=e,t.height=n,t.data=i,t}function Al(t,{width:e,height:n},r){if(e===t.width&&n===t.height)return;const i=Sl({},{width:e,height:n},r);Ml(t,i,{x:0,y:0},{x:0,y:0},{width:Math.min(t.width,e),height:Math.min(t.height,n)},r),t.width=e,t.height=n,t.data=i.data;}function Ml(t,e,n,r,i,s){if(0===i.width||0===i.height)return e;if(i.width>t.width||i.height>t.height||n.x>t.width-i.width||n.y>t.height-i.height)throw new RangeError("out of range source coordinates for image copy");if(i.width>e.width||i.height>e.height||r.x>e.width-i.width||r.y>e.height-i.height)throw new RangeError("out of range destination coordinates for image copy");const o=t.data,a=e.data;if(o===a)throw new Error("srcData equals dstData, so image is already copied");for(let l=0;l{e[t.evaluationKey]=o;const a=t.expression.evaluate(e);i.setPixel(r/4/n,s/4,a);};if(t.clips)for(let e=0,i=0;ethis.max&&(this.max=n),n=this.dim+1||e<-1||e>=this.dim+1)throw new RangeError(`Out of range source coordinates for DEM data. x: ${t}, y: ${e}, dim: ${this.dim}`);return (e+1)*this.stride+(t+1)}unpack(t,e,n){return t*this.redFactor+e*this.greenFactor+n*this.blueFactor-this.baseShift}pack(t){return Rl(t,this.getUnpackVector())}getPixels(){return new Il({width:this.stride,height:this.stride},new Uint8Array(this.data.buffer))}backfillBorder(t,e,n){if(this.dim!==t.dim)throw new Error("dem dimension mismatch");let r=e*this.dim,i=e*this.dim+this.dim,s=n*this.dim,o=n*this.dim+this.dim;switch(e){case -1:r=i-1;break;case 1:i=r+1;}switch(n){case -1:s=o-1;break;case 1:o=s+1;}const a=-e*this.dim,l=-n*this.dim;for(let e=s;e0)for(let i=e;i=e;i-=r)s=du(i/r|0,t[i],t[i+1],s);return s&&lu(s,s.next)&&(yu(s),s=s.next),s}function Yl(t,e){if(!t)return t;e||(e=t);let n,r=t;do{if(n=!1,r.steiner||!lu(r,r.next)&&0!==au(r.prev,r,r.next))r=r.next;else {if(yu(r),r=e=r.prev,r===r.next)break;n=!0;}}while(n||r!==e);return e}function Zl(t,e,n,r,i,s,o){if(!t)return;!o&&s&&function(t,e,n,r){let i=t;do{0===i.z&&(i.z=nu(i.x,i.y,e,n,r)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next;}while(i!==t);i.prevZ.nextZ=null,i.prevZ=null,function(t){let e,n=1;do{let r,i=t;t=null;let s=null;for(e=0;i;){e++;let o=i,a=0;for(let t=0;t0||l>0&&o;)0!==a&&(0===l||!o||i.z<=o.z)?(r=i,i=i.nextZ,a--):(r=o,o=o.nextZ,l--),s?s.nextZ=r:t=r,r.prevZ=s,s=r;i=o;}s.nextZ=null,n*=2;}while(e>1)}(i);}(t,r,i,s);let a=t;for(;t.prev!==t.next;){const l=t.prev,u=t.next;if(s?Wl(t,r,i,s):Hl(t))e.push(l.i,t.i,u.i),yu(t),t=u.next,a=u.next;else if((t=u)===a){o?1===o?Zl(t=Kl(Yl(t),e),e,n,r,i,s,2):2===o&&Jl(t,e,n,r,i,s):Zl(Yl(t),e,n,r,i,s,1);break}}}function Hl(t){const e=t.prev,n=t,r=t.next;if(au(e,n,r)>=0)return !1;const i=e.x,s=n.x,o=r.x,a=e.y,l=n.y,u=r.y,c=Math.min(i,s,o),h=Math.min(a,l,u),p=Math.max(i,s,o),f=Math.max(a,l,u);let d=r.next;for(;d!==e;){if(d.x>=c&&d.x<=p&&d.y>=h&&d.y<=f&&su(i,a,s,l,o,u,d.x,d.y)&&au(d.prev,d,d.next)>=0)return !1;d=d.next;}return !0}function Wl(t,e,n,r){const i=t.prev,s=t,o=t.next;if(au(i,s,o)>=0)return !1;const a=i.x,l=s.x,u=o.x,c=i.y,h=s.y,p=o.y,f=Math.min(a,l,u),d=Math.min(c,h,p),y=Math.max(a,l,u),m=Math.max(c,h,p),g=nu(f,d,e,n,r),x=nu(y,m,e,n,r);let v=t.prevZ,b=t.nextZ;for(;v&&v.z>=g&&b&&b.z<=x;){if(v.x>=f&&v.x<=y&&v.y>=d&&v.y<=m&&v!==i&&v!==o&&su(a,c,l,h,u,p,v.x,v.y)&&au(v.prev,v,v.next)>=0)return !1;if(v=v.prevZ,b.x>=f&&b.x<=y&&b.y>=d&&b.y<=m&&b!==i&&b!==o&&su(a,c,l,h,u,p,b.x,b.y)&&au(b.prev,b,b.next)>=0)return !1;b=b.nextZ;}for(;v&&v.z>=g;){if(v.x>=f&&v.x<=y&&v.y>=d&&v.y<=m&&v!==i&&v!==o&&su(a,c,l,h,u,p,v.x,v.y)&&au(v.prev,v,v.next)>=0)return !1;v=v.prevZ;}for(;b&&b.z<=x;){if(b.x>=f&&b.x<=y&&b.y>=d&&b.y<=m&&b!==i&&b!==o&&su(a,c,l,h,u,p,b.x,b.y)&&au(b.prev,b,b.next)>=0)return !1;b=b.nextZ;}return !0}function Kl(t,e){let n=t;do{const r=n.prev,i=n.next.next;!lu(r,i)&&uu(r,n,n.next,i)&&pu(r,i)&&pu(i,r)&&(e.push(r.i,n.i,i.i),yu(n),yu(n.next),n=t=i),n=n.next;}while(n!==t);return Yl(n)}function Jl(t,e,n,r,i,s){let o=t;do{let t=o.next.next;for(;t!==o.prev;){if(o.i!==t.i&&ou(o,t)){let a=fu(o,t);return o=Yl(o,o.next),a=Yl(a,a.next),Zl(o,e,n,r,i,s,0),void Zl(a,e,n,r,i,s,0)}t=t.next;}o=o.next;}while(o!==t)}function Ql(t,e){let n=t.x-e.x;return 0===n&&(n=t.y-e.y,0===n)&&(n=(t.next.y-t.y)/(t.next.x-t.x)-(e.next.y-e.y)/(e.next.x-e.x)),n}function tu(t,e){const n=function(t,e){let n=e;const r=t.x,i=t.y;let s,o=-1/0;if(lu(t,n))return n;do{if(lu(t,n.next))return n.next;if(i<=n.y&&i>=n.next.y&&n.next.y!==n.y){const t=n.x+(i-n.y)*(n.next.x-n.x)/(n.next.y-n.y);if(t<=r&&t>o&&(o=t,s=n.x=n.x&&n.x>=l&&r!==n.x&&iu(is.x||n.x===s.x&&eu(s,n)))&&(s=n,c=e);}n=n.next;}while(n!==a);return s}(t,e);if(!n)return e;const r=fu(n,t);return Yl(r,r.next),Yl(n,n.next)}function eu(t,e){return au(t.prev,t,e.prev)<0&&au(e.next,t,t.next)<0}function nu(t,e,n,r,i){return (t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=(t-n)*i|0)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=(e-r)*i|0)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function ru(t){let e=t,n=t;do{(e.x=(t-o)*(s-a)&&(t-o)*(r-a)>=(n-o)*(e-a)&&(n-o)*(s-a)>=(i-o)*(r-a)}function su(t,e,n,r,i,s,o,a){return !(t===o&&e===a)&&iu(t,e,n,r,i,s,o,a)}function ou(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){let n=t;do{if(n.i!==t.i&&n.next.i!==t.i&&n.i!==e.i&&n.next.i!==e.i&&uu(n,n.next,t,e))return !0;n=n.next;}while(n!==t);return !1}(t,e)&&(pu(t,e)&&pu(e,t)&&function(t,e){let n=t,r=!1;const i=(t.x+e.x)/2,s=(t.y+e.y)/2;do{n.y>s!=n.next.y>s&&n.next.y!==n.y&&i<(n.next.x-n.x)*(s-n.y)/(n.next.y-n.y)+n.x&&(r=!r),n=n.next;}while(n!==t);return r}(t,e)&&(au(t.prev,t,e.prev)||au(t,e.prev,e))||lu(t,e)&&au(t.prev,t,t.next)>0&&au(e.prev,e,e.next)>0)}function au(t,e,n){return (e.y-t.y)*(n.x-e.x)-(e.x-t.x)*(n.y-e.y)}function lu(t,e){return t.x===e.x&&t.y===e.y}function uu(t,e,n,r){const i=hu(au(t,e,n)),s=hu(au(t,e,r)),o=hu(au(n,r,t)),a=hu(au(n,r,e));return i!==s&&o!==a||!(0!==i||!cu(t,n,e))||!(0!==s||!cu(t,r,e))||!(0!==o||!cu(n,t,r))||!(0!==a||!cu(n,e,r))}function cu(t,e,n){return e.x<=Math.max(t.x,n.x)&&e.x>=Math.min(t.x,n.x)&&e.y<=Math.max(t.y,n.y)&&e.y>=Math.min(t.y,n.y)}function hu(t){return t>0?1:t<0?-1:0}function pu(t,e){return au(t.prev,t,t.next)<0?au(t,e,t.next)>=0&&au(t,t.prev,e)>=0:au(t,e,t.prev)<0||au(t,t.next,e)<0}function fu(t,e){const n=mu(t.i,t.x,t.y),r=mu(e.i,e.x,e.y),i=t.next,s=e.prev;return t.next=e,e.prev=t,n.next=i,i.prev=n,r.next=n,n.prev=r,s.next=r,r.prev=s,r}function du(t,e,n,r){const i=mu(t,e,n);return r?(i.next=r.next,i.prev=r,r.next.prev=i,r.next=i):(i.prev=i,i.next=i),i}function yu(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ);}function mu(t,e,n){return {i:t,x:e,y:n,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}class gu{constructor(t,e){if(e>t)throw new Error("Min granularity must not be greater than base granularity.");this._baseZoomGranularity=t,this._minGranularity=e;}getGranularityForZoomLevel(t){return Math.max(Math.floor(this._baseZoomGranularity/(1<32767||e>32767)throw new Error("Vertex coordinates are out of signed 16 bit integer range.");const n=0|Math.round(t),r=0|Math.round(e),i=this._getKey(n,r);if(this._vertexDictionary.has(i))return this._vertexDictionary.get(i);const s=this._vertexBuffer.length/2;return this._vertexDictionary.set(i,s),this._vertexBuffer.push(n,r),s}_subdivideTrianglesScanline(t){if(this._granularity<2)return function(t,e){const n=[];for(let r=0;r0?(n.push(i),n.push(o),n.push(s)):(n.push(i),n.push(s),n.push(o));}return n}(this._vertexBuffer,t);const e=[],n=t.length;for(let r=0;r=1||v<=0)||y&&(ai)){u>=r&&u<=i&&s.push(n[(t+1)%3]);continue}!y&&x>0&&s.push(this._vertexToIndex(o+p*x,a+f*x));const b=o+p*Math.max(x,0),w=o+p*Math.min(v,1);d||this._generateIntraEdgeVertices(s,o,a,l,u,b,w),!y&&v<1&&s.push(this._vertexToIndex(o+p*v,a+f*v)),(y||u>=r&&u<=i)&&s.push(n[(t+1)%3]),!y&&(u<=r||u>=i)&&this._generateInterEdgeVertices(s,o,a,l,u,c,h,w,r,i);}return s}_generateIntraEdgeVertices(t,e,n,r,i,s,o){const a=r-e,l=i-n,u=0===l,c=u?Math.min(e,r):Math.min(s,o),h=u?Math.max(e,r):Math.max(s,o),p=Math.floor(c/this._granularityCellSize)+1,f=Math.ceil(h/this._granularityCellSize)-1;if(u?e=p;r--){const i=r*this._granularityCellSize;t.push(this._vertexToIndex(i,n+l*(i-e)/a));}}_generateInterEdgeVertices(t,e,n,r,i,s,o,a,l,u){const c=i-n,h=s-r,p=o-i,f=(l-i)/p,d=(u-i)/p,y=Math.min(f,d),m=Math.max(f,d),g=r+h*y;let x=Math.floor(Math.min(g,a)/this._granularityCellSize)+1,v=Math.ceil(Math.max(g,a)/this._granularityCellSize)-1,b=a=1||m<=0){const t=n-o,r=s+(e-s)*Math.min((l-o)/t,(u-o)/t);x=Math.floor(Math.min(r,a)/this._granularityCellSize)+1,v=Math.ceil(Math.max(r,a)/this._granularityCellSize)-1,b=a0?u:l;if(b)for(let e=x;e<=v;e++)t.push(this._vertexToIndex(e*this._granularityCellSize,_));else for(let e=v;e>=x;e--)t.push(this._vertexToIndex(e*this._granularityCellSize,_));}_generateOutline(t){const e=[];for(const n of t){const t=Su(n,this._granularity,!0),r=this._pointArrayToIndices(t),i=[];for(let t=1;ti!=(s===vu)?(t.push(e),t.push(n),t.push(this._vertexToIndex(r,s)),t.push(n),t.push(this._vertexToIndex(i,s)),t.push(this._vertexToIndex(r,s))):(t.push(n),t.push(e),t.push(this._vertexToIndex(r,s)),t.push(this._vertexToIndex(i,s)),t.push(n),t.push(this._vertexToIndex(r,s)));}_fillPoles(t,e,n){const r=this._vertexBuffer,i=T,s=t.length;for(let o=2;o80*n){a=t[0],l=t[1];let e=a,r=l;for(let s=n;se&&(e=n),i>r&&(r=i);}u=Math.max(e-a,r-l),u=0!==u?32767/u:0;}return Zl(s,o,n,a,l,u,0),o}(n,r),e=this._convertIndices(n,t);i=this._subdivideTrianglesScanline(e);}catch(t){console.error(t);}let s=[];return e&&(s=this._generateOutline(t)),this._ensureNoPoleVertices(),this._handlePoles(i),{verticesFlattened:this._vertexBuffer,indicesTriangles:i,indicesLineList:s}}_convertIndices(t,e){const n=[];for(const r of e)n.push(this._vertexToIndex(t[2*r],t[2*r+1]));return n}_pointArrayToIndices(t){const e=[];for(const n of t)e.push(this._vertexToIndex(n.x,n.y));return e}}function _u(t,e,n,r=!0){return new wu(n,e).subdividePolygonInternal(t,r)}function Su(t,e,r=!1){if(!t||t.length<1)return [];if(t.length<2)return [];const i=t[0],s=t[t.length-1],o=r&&(i.x!==s.x||i.y!==s.y);if(e<2)return o?[...t,t[0]]:[...t];const a=Math.floor(T/e),l=[];l.push(new n(t[0].x,t[0].y));const u=t.length,c=o?u:u-1;for(let e=0;e0?(Math.floor(x/a)+1)*a:(Math.ceil(x/a)-1)*a,e=y>0?(Math.floor(v/a)+1)*a:(Math.ceil(v/a)-1)*a,r=Math.abs(x-t),i=Math.abs(v-e),s=Math.abs(x-c),o=Math.abs(v-h),u=p?r/m:Number.POSITIVE_INFINITY,b=f?i/g:Number.POSITIVE_INFINITY;if((s<=r||!p)&&(o<=i||!f))break;if(u=0?o-1:s-1,i=(a+1)%s,l=t[2*e[r]],u=t[2*e[i]],c=t[2*e[o]],h=t[2*e[o]+1],p=t[2*e[a]+1];let f=!1;if(lu)f=!1;else {const n=p-h,s=-(t[2*e[a]]-c),o=h((u-c)*n+(t[2*e[i]+1]-h)*s)*o&&(f=!0);}if(f){const t=e[r],i=e[o],l=e[a];t!==i&&t!==l&&i!==l&&n.push(l,i,t),o--,o<0&&(o=s-1);}else {const t=e[i],r=e[o],l=e[a];t!==r&&t!==l&&r!==l&&n.push(l,r,t),a++,a>=s&&(a=0);}if(r===i)break}}function Mu(t,e,n,r,i,s,o,a,l){const u=i.length/2,c=o&&a&&l;if(uua.MAX_VERTEX_ARRAY_LENGTH&&(u=t.createNewSegment(e,n),l=a.count,y=!0,m=!0,g=!0,c=0);const x=ku(o,r,s,a,p,y,u),v=ku(o,r,s,a,f,m,u),b=ku(o,r,s,a,d,g,u);n.emplaceBack(c+x-l,c+v-l,c+b-l),u.primitiveLength++;}}(e,n,r,i,s,t),c&&function(t,e,n,r,i,s){const o=[];for(let t=0;tua.MAX_VERTEX_ARRAY_LENGTH&&(u=t.createNewSegment(e,n),l=a.count,d=!0,y=!0,c=0);const m=ku(o,r,s,a,p,d,u),g=ku(o,r,s,a,f,y,u);n.emplaceBack(c+m-l,c+g-l),u.primitiveLength++;}}(o,n,a,i,l,t),e.forceNewSegmentOnNextPrepare(),null==o||o.forceNewSegmentOnNextPrepare();}function ku(t,e,n,r,i,s,o){if(s){const s=r.count;return n(e[2*i],e[2*i+1]),t[i]=r.count,r.count++,o.vertexLength++,s}return t[i]}class Iu{constructor(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasDependencies=!1,this.patternFeatures=[],this.layoutVertexArray=new Zo,this.indexArray=new sa,this.indexArray2=new oa,this.programConfigurations=new $a(t.layers,t.zoom),this.segments=new ua,this.segments2=new ua,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,n){this.hasDependencies=ql("fill",this.layers,e);const r=this.layers[0].layout.get("fill-sort-key"),i=!r.isConstant(),s=[];for(const{feature:o,id:a,index:l,sourceLayerIndex:u}of t){const t=this.layers[0]._featureFilter.needGeometry,c=Ga(o,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom),c,n))continue;const h=i?r.evaluate(c,{},n,e.availableImages):void 0,p={id:a,properties:o.properties,type:o.type,sourceLayerIndex:u,index:l,geometry:t?c.geometry:qa(o),patterns:{},sortKey:h};s.push(p);}i&&s.sort(((t,e)=>t.sortKey-e.sortKey));for(const r of s){const{geometry:i,index:s,sourceLayerIndex:o}=r;if(this.hasDependencies){const t=Gl("fill",this.layers,r,{zoom:this.zoom},e);this.patternFeatures.push(t);}else this.addFeature(r,i,s,n,{},e.subdivisionGranularity);e.featureIndex.insert(t[s].feature,i,s,o,this.index);}}update(t,e,n){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,{imagePositions:n});}addFeatures(t,e,n){for(const r of this.patternFeatures)this.addFeature(r,r.geometry,r.index,e,n,t.subdivisionGranularity);}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,jl),this.indexBuffer=t.createIndexBuffer(this.indexArray),this.indexBuffer2=t.createIndexBuffer(this.indexArray2)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.indexBuffer2.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.segments2.destroy());}addFeature(t,e,n,r,i,s){for(const t of rr(e,500)){const e=_u(t,r,s.fill.getGranularityForZoomLevel(r.z)),n=this.layoutVertexArray;Mu(((t,e)=>{n.emplaceBack(t,e);}),this.segments,this.layoutVertexArray,this.indexArray,e.verticesFlattened,e.indicesTriangles,this.segments2,this.indexArray2,e.indicesLineList);}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,n,{imagePositions:i,canonical:r});}}let Eu,Tu;ds("FillBucket",Iu,{omit:["layers","patternFeatures"]});var Fu={get paint(){return Tu=Tu||new Ks({"fill-antialias":new Xs(wt.paint_fill["fill-antialias"]),"fill-opacity":new Ys(wt.paint_fill["fill-opacity"]),"fill-color":new Ys(wt.paint_fill["fill-color"]),"fill-outline-color":new Ys(wt.paint_fill["fill-outline-color"]),"fill-translate":new Xs(wt.paint_fill["fill-translate"]),"fill-translate-anchor":new Xs(wt.paint_fill["fill-translate-anchor"]),"fill-pattern":new Zs(wt.paint_fill["fill-pattern"])})},get layout(){return Eu=Eu||new Ks({"fill-sort-key":new Ys(wt.layout_fill["fill-sort-key"])})}};class Pu extends to{constructor(t,e){super(t,Fu,e);}recalculate(t,e){super.recalculate(t,e);const n=this.paint._values["fill-outline-color"];"constant"===n.value.kind&&void 0===n.value.value&&(this.paint._values["fill-outline-color"]=this.paint._values["fill-color"]);}createBucket(t){return new Iu(t)}queryRadius(){return al(this.paint.get("fill-translate"))}queryIntersectsFeature({queryGeometry:t,geometry:e,transform:n,pixelsToTileUnits:r}){return Ka(ll(t,this.paint.get("fill-translate"),this.paint.get("fill-translate-anchor"),-n.bearingInRadians,r),e)}isTileClipped(){return !0}}const Du=ao([{name:"a_pos",components:2,type:"Int16"},{name:"a_normal_ed",components:4,type:"Int16"}],4),zu=ao([{name:"a_centroid",components:2,type:"Int16"}],4),{members:Bu}=Du;class Cu{constructor(t,e,n,r,i){this.properties={},this.extent=n,this.type=0,this.id=void 0,this._pbf=t,this._geometry=-1,this._keys=r,this._values=i,t.readFields(Vu,this,e);}loadGeometry(){const t=this._pbf;t.pos=this._geometry;const e=t.readVarint()+t.pos,r=[];let i,s=1,o=0,a=0,l=0;for(;t.pos>3;}if(o--,1===s||2===s)a+=t.readSVarint(),l+=t.readSVarint(),1===s&&(i&&r.push(i),i=[]),i&&i.push(new n(a,l));else {if(7!==s)throw new Error(`unknown command ${s}`);i&&i.push(i[0].clone());}}return i&&r.push(i),r}bbox(){const t=this._pbf;t.pos=this._geometry;const e=t.readVarint()+t.pos;let n=1,r=0,i=0,s=0,o=1/0,a=-1/0,l=1/0,u=-1/0;for(;t.pos>3;}if(r--,1===n||2===n)i+=t.readSVarint(),s+=t.readSVarint(),ia&&(a=i),su&&(u=s);else if(7!==n)throw new Error(`unknown command ${n}`)}return [o,l,a,u]}toGeoJSON(t,e,n){const r=this.extent*Math.pow(2,n),i=this.extent*t,s=this.extent*e,o=this.loadGeometry();function a(t){return [360*(t.x+i)/r-180,360/Math.PI*Math.atan(Math.exp((1-2*(t.y+s)/r)*Math.PI))-90]}function l(t){return t.map(a)}let u;if(1===this.type){const t=[];for(const e of o)t.push(e[0]);const e=l(t);u=1===t.length?{type:"Point",coordinates:e[0]}:{type:"MultiPoint",coordinates:e};}else if(2===this.type){const t=o.map(l);u=1===t.length?{type:"LineString",coordinates:t[0]}:{type:"MultiLineString",coordinates:t};}else {if(3!==this.type)throw new Error("unknown feature type");{const t=Lu(o),e=[];for(const n of t)e.push(n.map(l));u=1===e.length?{type:"Polygon",coordinates:e[0]}:{type:"MultiPolygon",coordinates:e};}}const c={type:"Feature",geometry:u,properties:this.properties};return null!=this.id&&(c.id=this.id),c}}function Vu(t,e,n){1===t?e.id=n.readVarint():2===t?function(t,e){const n=t.readVarint()+t.pos;for(;t.pos=this._features.length)throw new Error("feature index out of bounds");this._pbf.pos=this._features[t];const e=this._pbf.readVarint()+this._pbf.pos;return new Cu(this._pbf,e,this.extent,this._keys,this._values)}}function Ru(t,e,n){15===t?e.version=n.readVarint():1===t?e.name=n.readString():5===t?e.extent=n.readVarint():2===t?e._features.push(n.pos):3===t?e._keys.push(n.readString()):4===t&&e._values.push(function(t){let e=null;const n=t.readVarint()+t.pos;for(;t.pos>3;e=1===n?t.readString():2===n?t.readFloat():3===n?t.readDouble():4===n?t.readVarint64():5===n?t.readVarint():6===n?t.readSVarint():7===n?t.readBoolean():null;}if(null==e)throw new Error("unknown feature value");return e}(n));}class Nu{constructor(t,e){this.layers=t.readFields(Uu,{},e);}}function Uu(t,e,n){if(3===t){const t=new $u(n,n.readVarint()+n.pos);t.length&&(e[t.name]=t);}}const ju=Math.pow(2,13);function qu(t,e,n,r,i,s,o,a){t.emplaceBack(e,n,2*Math.floor(r*ju)+o,i*ju*2,s*ju*2,Math.round(a));}class Gu{constructor(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasDependencies=!1,this.layoutVertexArray=new Ho,this.centroidVertexArray=new Xo,this.indexArray=new sa,this.programConfigurations=new $a(t.layers,t.zoom),this.segments=new ua,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,n){this.features=[],this.hasDependencies=ql("fill-extrusion",this.layers,e);for(const{feature:r,id:i,index:s,sourceLayerIndex:o}of t){const t=this.layers[0]._featureFilter.needGeometry,a=Ga(r,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom),a,n))continue;const l={id:i,sourceLayerIndex:o,index:s,geometry:t?a.geometry:qa(r),properties:r.properties,type:r.type,patterns:{}};this.hasDependencies?this.features.push(Gl("fill-extrusion",this.layers,l,{zoom:this.zoom},e)):this.addFeature(l,l.geometry,s,n,{},e.subdivisionGranularity),e.featureIndex.insert(r,l.geometry,s,o,this.index,!0);}}addFeatures(t,e,n){for(const r of this.features){const{geometry:i}=r;this.addFeature(r,i,r.index,e,n,t.subdivisionGranularity);}}update(t,e,n){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,{imagePositions:n});}isEmpty(){return 0===this.layoutVertexArray.length&&0===this.centroidVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,Bu),this.centroidVertexBuffer=t.createVertexBuffer(this.centroidVertexArray,zu.members,!0),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.centroidVertexBuffer.destroy());}addFeature(t,e,n,r,i,s){for(const n of rr(e,500)){const e={x:0,y:0,sampleCount:0},i=this.layoutVertexArray.length;this.processPolygon(e,r,t,n,s);const o=this.layoutVertexArray.length-i,a=Math.floor(e.x/e.sampleCount),l=Math.floor(e.y/e.sampleCount);for(let t=0;t{qu(u,t,e,0,0,1,1,0);}),this.segments,this.layoutVertexArray,this.indexArray,l.verticesFlattened,l.indicesTriangles);}_generateSideFaces(t,e){let n=0;for(let r=1;rua.MAX_VERTEX_ARRAY_LENGTH&&(e.segment=this.segments.prepareSegment(4,this.layoutVertexArray,this.indexArray));const o=i.sub(s)._perp()._unit(),a=s.dist(i);n+a>32768&&(n=0),qu(this.layoutVertexArray,i.x,i.y,o.x,o.y,0,0,n),qu(this.layoutVertexArray,i.x,i.y,o.x,o.y,0,1,n),n+=a,qu(this.layoutVertexArray,s.x,s.y,o.x,o.y,0,0,n),qu(this.layoutVertexArray,s.x,s.y,o.x,o.y,0,1,n);const l=e.segment.vertexLength;this.indexArray.emplaceBack(l,l+2,l+1),this.indexArray.emplaceBack(l+1,l+2,l+3),e.segment.vertexLength+=4,e.segment.primitiveLength+=2;}}}function Xu(t,e){for(let n=0;nT)||t.y===e.y&&(t.y<0||t.y>T)}function Zu(t){return t.every((t=>t.x<0))||t.every((t=>t.x>T))||t.every((t=>t.y<0))||t.every((t=>t.y>T))}let Hu;ds("FillExtrusionBucket",Gu,{omit:["layers","features"]});var Wu={get paint(){return Hu=Hu||new Ks({"fill-extrusion-opacity":new Xs(wt["paint_fill-extrusion"]["fill-extrusion-opacity"]),"fill-extrusion-color":new Ys(wt["paint_fill-extrusion"]["fill-extrusion-color"]),"fill-extrusion-translate":new Xs(wt["paint_fill-extrusion"]["fill-extrusion-translate"]),"fill-extrusion-translate-anchor":new Xs(wt["paint_fill-extrusion"]["fill-extrusion-translate-anchor"]),"fill-extrusion-pattern":new Zs(wt["paint_fill-extrusion"]["fill-extrusion-pattern"]),"fill-extrusion-height":new Ys(wt["paint_fill-extrusion"]["fill-extrusion-height"]),"fill-extrusion-base":new Ys(wt["paint_fill-extrusion"]["fill-extrusion-base"]),"fill-extrusion-vertical-gradient":new Xs(wt["paint_fill-extrusion"]["fill-extrusion-vertical-gradient"])})}};class Ku extends to{constructor(t,e){super(t,Wu,e);}createBucket(t){return new Gu(t)}queryRadius(){return al(this.paint.get("fill-extrusion-translate"))}is3D(){return !0}queryIntersectsFeature({queryGeometry:t,feature:e,featureState:r,geometry:i,transform:s,pixelsToTileUnits:o,pixelPosMatrix:a}){const l=ll(t,this.paint.get("fill-extrusion-translate"),this.paint.get("fill-extrusion-translate-anchor"),-s.bearingInRadians,o),u=this.paint.get("fill-extrusion-height").evaluate(e,r),c=this.paint.get("fill-extrusion-base").evaluate(e,r),h=function(t,e){const r=[];for(const i of t){const t=[i.x,i.y,0,1];A(t,t,e),r.push(new n(t[0]/t[3],t[1]/t[3]));}return r}(l,a),p=function(t,e,r,i){const s=[],o=[],a=i[8]*e,l=i[9]*e,u=i[10]*e,c=i[11]*e,h=i[8]*r,p=i[9]*r,f=i[10]*r,d=i[11]*r;for(const e of t){const t=[],r=[];for(const s of e){const e=s.x,o=s.y,y=i[0]*e+i[4]*o+i[12],m=i[1]*e+i[5]*o+i[13],g=i[2]*e+i[6]*o+i[14],x=i[3]*e+i[7]*o+i[15],v=g+u,b=x+c,w=y+h,_=m+p,S=g+f,A=x+d,M=new n((y+a)/b,(m+l)/b);M.z=v/b,t.push(M);const k=new n(w/A,_/A);k.z=S/A,r.push(k);}s.push(t),o.push(r);}return [s,o]}(i,c,u,a);return function(t,e,n){let r=1/0;Ka(n,e)&&(r=Qu(n,e[0]));for(let i=0;i>4;if(1!==r)throw new Error(`Got v${r} data when expected v1.`);const i=tc[15&n];if(!i)throw new Error("Unrecognized array type.");const[s]=new Uint16Array(t,2,1),[o]=new Uint32Array(t,4,1);return new ec(o,s,i,t)}constructor(t,e=64,n=Float64Array,r){if(isNaN(t)||t<0)throw new Error(`Unpexpected numItems value: ${t}.`);this.numItems=+t,this.nodeSize=Math.min(Math.max(+e,2),65535),this.ArrayType=n,this.IndexArrayType=t<65536?Uint16Array:Uint32Array;const i=tc.indexOf(this.ArrayType),s=2*t*this.ArrayType.BYTES_PER_ELEMENT,o=t*this.IndexArrayType.BYTES_PER_ELEMENT,a=(8-o%8)%8;if(i<0)throw new Error(`Unexpected typed array class: ${n}.`);r&&r instanceof ArrayBuffer?(this.data=r,this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+o+a,2*t),this._pos=2*t,this._finished=!0):(this.data=new ArrayBuffer(8+s+o+a),this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+o+a,2*t),this._pos=0,this._finished=!1,new Uint8Array(this.data,0,2).set([219,16+i]),new Uint16Array(this.data,2,1)[0]=e,new Uint32Array(this.data,4,1)[0]=t);}add(t,e){const n=this._pos>>1;return this.ids[n]=n,this.coords[this._pos++]=t,this.coords[this._pos++]=e,n}finish(){const t=this._pos>>1;if(t!==this.numItems)throw new Error(`Added ${t} items when expected ${this.numItems}.`);return nc(this.ids,this.coords,this.nodeSize,0,this.numItems-1,0),this._finished=!0,this}range(t,e,n,r){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:i,coords:s,nodeSize:o}=this,a=[0,i.length-1,0],l=[];for(;a.length;){const u=a.pop()||0,c=a.pop()||0,h=a.pop()||0;if(c-h<=o){for(let o=h;o<=c;o++){const a=s[2*o],u=s[2*o+1];a>=t&&a<=n&&u>=e&&u<=r&&l.push(i[o]);}continue}const p=h+c>>1,f=s[2*p],d=s[2*p+1];f>=t&&f<=n&&d>=e&&d<=r&&l.push(i[p]),(0===u?t<=f:e<=d)&&(a.push(h),a.push(p-1),a.push(1-u)),(0===u?n>=f:r>=d)&&(a.push(p+1),a.push(c),a.push(1-u));}return l}within(t,e,n){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:r,coords:i,nodeSize:s}=this,o=[0,r.length-1,0],a=[],l=n*n;for(;o.length;){const u=o.pop()||0,c=o.pop()||0,h=o.pop()||0;if(c-h<=s){for(let n=h;n<=c;n++)oc(i[2*n],i[2*n+1],t,e)<=l&&a.push(r[n]);continue}const p=h+c>>1,f=i[2*p],d=i[2*p+1];oc(f,d,t,e)<=l&&a.push(r[p]),(0===u?t-n<=f:e-n<=d)&&(o.push(h),o.push(p-1),o.push(1-u)),(0===u?t+n>=f:e+n>=d)&&(o.push(p+1),o.push(c),o.push(1-u));}return a}}function nc(t,e,n,r,i,s){if(i-r<=n)return;const o=r+i>>1;rc(t,e,o,r,i,s),nc(t,e,n,r,o-1,1-s),nc(t,e,n,o+1,i,1-s);}function rc(t,e,n,r,i,s){for(;i>r;){if(i-r>600){const o=i-r+1,a=n-r+1,l=Math.log(o),u=.5*Math.exp(2*l/3),c=.5*Math.sqrt(l*u*(o-u)/o)*(a-o/2<0?-1:1);rc(t,e,n,Math.max(r,Math.floor(n-a*u/o+c)),Math.min(i,Math.floor(n+(o-a)*u/o+c)),s);}const o=e[2*n+s];let a=r,l=i;for(ic(t,e,r,n),e[2*i+s]>o&&ic(t,e,r,i);ao;)l--;}e[2*r+s]===o?ic(t,e,r,l):(l++,ic(t,e,l,i)),l<=n&&(r=l+1),n<=l&&(i=l-1);}}function ic(t,e,n,r){sc(t,n,r),sc(e,2*n,2*r),sc(e,2*n+1,2*r+1);}function sc(t,e,n){const r=t[e];t[e]=t[n],t[n]=r;}function oc(t,e,n,r){const i=t-n,s=e-r;return i*i+s*s}function ac(t,e,n,r){let i=r;const s=e+(n-e>>1);let o,a=n-e;const l=t[e],u=t[e+1],c=t[n],h=t[n+1];for(let r=e+3;ri)o=r,i=e;else if(e===i){const t=Math.abs(r-s);tr&&(o-e>3&&ac(t,e,o,r),t[o+2]=i,n-o>3&&ac(t,o,n,r));}function lc(t,e,n,r,i,s){let o=i-n,a=s-r;if(0!==o||0!==a){const l=((t-n)*o+(e-r)*a)/(o*o+a*a);l>1?(n=i,r=s):l>0&&(n+=o*l,r+=a*l);}return o=t-n,a=e-r,o*o+a*a}function uc(t,e,n,r){const i={type:e,geom:n},s={id:null==t?null:t,type:i.type,geometry:i.geom,tags:r,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0};switch(i.type){case "Point":case "MultiPoint":hc(s,i.geom);break;case "LineString":hc(s,i.geom.points);break;case "Polygon":hc(s,i.geom[0].points);break;case "MultiLineString":for(const t of i.geom)hc(s,t.points);break;case "MultiPolygon":for(const t of i.geom)hc(s,t[0].points);}return s}function cc(t){t.points.length>64&&(t.points=new Float64Array(t.points));}function hc(t,e){for(let n=0;n0&&(o+=r?(i*l-a*s)/2:Math.sqrt(Math.pow(a-i,2)+Math.pow(l-s,2))),i=a,s=l;}const a=e.points.length-3;e.points[2]=1,n>0&&ac(e.points,0,a,n),e.points[a+2]=1,cc(e),e.size=Math.abs(o),e.start=0,e.end=e.size;}function yc(t,e,n,r){for(let i=0;i1?1:n}function xc(t){const e={type:"Feature",geometry:vc(t),properties:t.tags};return null!=t.id&&(e.id=t.id),e}function vc(t){const{type:e,geometry:n}=t;switch(e){case "Point":return {type:e,coordinates:wc(n[0],n[1])};case "MultiPoint":return {type:e,coordinates:bc(n)};case "LineString":return {type:e,coordinates:bc(n.points)};case "MultiLineString":case "Polygon":return {type:e,coordinates:n.map((t=>bc(t.points)))};case "MultiPolygon":return {type:e,coordinates:n.map((t=>t.map((t=>bc(t.points)))))}}}function bc(t){const e=[];for(let n=0;n=(n/=e)&&o=r)return null;const l=[];for(const e of t){const t=i===Ac.X?e.minX:e.minY,s=i===Ac.X?e.maxX:e.maxY;if(t>=n&&s=r))switch(e.type){case "Point":case "MultiPoint":kc(e,l,n,r,i);continue;case "LineString":Ic(e,l,n,r,i,a);continue;case "MultiLineString":Ec(e,l,n,r,i);continue;case "Polygon":Tc(e,l,n,r,i);continue;case "MultiPolygon":Fc(e,l,n,r,i);continue}}return l.length?l:null}function kc(t,e,n,r,i){const s=[];((function(t,e,n,r,i){for(let s=0;s=n&&o<=r&&Bc(e,t[s],t[s+1],t[s+2]);}}))(t.geometry,s,n,r,i),s.length&&e.push(uc(t.id,3===s.length?"Point":"MultiPoint",s,t.tags));}function Ic(t,e,n,r,i,s){const o=[];if(Pc(t.geometry,o,n,r,i,!1,s.lineMetrics),o.length)if(s.lineMetrics)for(const n of o)e.push(uc(t.id,"LineString",n,t.tags));else e.push(o.length>1?uc(t.id,"MultiLineString",o,t.tags):uc(t.id,"LineString",o[0],t.tags));}function Ec(t,e,n,r,i){const s=[];zc(t.geometry,s,n,r,i,!1),s.length&&e.push(1!==s.length?uc(t.id,"MultiLineString",s,t.tags):uc(t.id,"LineString",s[0],t.tags));}function Tc(t,e,n,r,i){const s=[];zc(t.geometry,s,n,r,i,!0),s.length&&e.push(uc(t.id,"Polygon",s,t.tags));}function Fc(t,e,n,r,i){const s=[];for(const e of t.geometry){const t=[];zc(e,t,n,r,i,!0),t.length&&s.push(t);}s.length&&e.push(uc(t.id,"MultiPolygon",s,t.tags));}function Pc(t,e,n,r,i,s,o){let a=Dc(t);const l=i===Ac.X?Cc:Vc;let u,c,h=t.start;for(let p=0;pn&&(c=l(a,f,d,m,g,n),o&&(a.start=h+u*c)):x>r?v=n&&(c=l(a,f,d,m,g,n),b=!0),v>r&&x<=r&&(c=l(a,f,d,m,g,r),b=!0),!s&&b&&(o&&(a.end=h+u*c),e.push(a),a=Dc(t)),o&&(h+=u);}let p=t.points.length-3;const f=t.points[p],d=t.points[p+1],y=i===Ac.X?f:d;y>=n&&y<=r&&Bc(a.points,f,d,t.points[p+2]),p=a.points.length-3,s&&p>=3&&(a.points[p]!==a.points[0]||a.points[p+1]!==a.points[1])&&Bc(a.points,a.points[0],a.points[1],a.points[2]),a.points.length&&(cc(a),e.push(a));}function Dc(t){return {points:[],size:t.size,start:t.start,end:t.end}}function zc(t,e,n,r,i,s){for(const o of t)Pc(o,e,n,r,i,s,!1);}function Bc(t,e,n,r){t.push(e,n,r);}function Cc(t,e,n,r,i,s){const o=(s-e)/(r-e);return Bc(t.points,s,n+(i-n)*o,1),o}function Vc(t,e,n,r,i,s){const o=(s-n)/(i-n);return Bc(t.points,e+(r-e)*o,s,1),o}function Lc(t,e){const n=e.buffer/e.extent;let r=t;const i=Mc(t,1,-1-n,n,Ac.X,-1,2,e),s=Mc(t,1,1-n,2+n,Ac.X,-1,2,e);return i||s?(r=Mc(t,1,-n,1+n,Ac.X,-1,2,e)||[],i&&(r=Oc(i,1).concat(r)),s&&(r=r.concat(Oc(s,-1))),r):r}function Oc(t,e){const n=[];for(const r of t)switch(r.type){case "Point":case "MultiPoint":{const t=$c(r.geometry,e);n.push(uc(r.id,r.type,t,r.tags));continue}case "LineString":{const t=Rc(r.geometry,e);n.push(uc(r.id,r.type,t,r.tags));continue}case "MultiLineString":case "Polygon":{const t=[];for(const n of r.geometry)t.push(Rc(n,e));n.push(uc(r.id,r.type,t,r.tags));continue}case "MultiPolygon":{const t=[];for(const n of r.geometry){const r=[];for(const t of n)r.push(Rc(t,e));t.push(r);}n.push(uc(r.id,r.type,t,r.tags));continue}}return n}function $c(t,e){const n=[];for(let r=0;r0||e.addOrUpdateProperties?.length>0;if(r){const r=t[0];let s=pc({type:"FeatureCollection",features:[{type:"Feature",id:r.id,geometry:e.newGeometry,properties:i?Uc(r.tags,e):r.tags}]},n);return s=Lc(s,n),s}if(i){const n=[];for(const r of t){const t={...r};t.tags=Uc(t.tags,e),n.push(t);}return n}return t}function Uc(t,e){if(e.removeAllProperties)return {};const n={...t||{}};if(e.removeProperties)for(const t of e.removeProperties)delete n[t];if(e.addOrUpdateProperties)for(const{key:t,value:r}of e.addOrUpdateProperties)n[t]=r;return n}!function(t){t[t.X=0]="X",t[t.Y=1]="Y";}(Ac||(Ac={}));const jc={minZoom:0,maxZoom:16,minPoints:2,radius:40,extent:512,nodeSize:64,log:!1,generateId:!1,reduce:null,map:t=>t};class qc{constructor(t){this.options=Object.assign(Object.create(jc),t),this.trees=new Array(this.options.maxZoom+1),this.stride=this.options.reduce?7:6,this.clusterProps=[],this.points=[];}load(t){const e=[];for(const n of t){if(!n.geometry)continue;const[t,r]=n.geometry.coordinates,[i,s]=[mc(t),gc(r)];e.push({id:n.id,type:"Point",geometry:[i,s],tags:n.properties});}this.createIndex(e);}initialize(t){const e=[];for(const n of t)"Point"===n.type&&e.push(n);this.createIndex(e);}updateIndex(t,e,n){this.options=Object.assign(Object.create(jc),n.clusterOptions),this.initialize(t);}createIndex(t){const{log:e,minZoom:n,maxZoom:r}=this.options;e&&console.time("total time");const i=`prepare ${t.length} points`;e&&console.time(i),this.points=t;const s=[];for(let e=0;e=n;t--){const n=Date.now();o=this.trees[t]=this.createTree(this.cluster(o,t)),e&&console.log("z%d: %d clusters in %dms",t,o.numItems,Date.now()-n);}e&&console.timeEnd("total time");}getClusters(t,e){return this.getClustersInternal(t,e).map((t=>xc(t)))}getClustersInternal(t,e){let n=((t[0]+180)%360+360)%360-180;const r=Math.max(-90,Math.min(90,t[1]));let i=180===t[2]?180:((t[2]+180)%360+360)%360-180;const s=Math.max(-90,Math.min(90,t[3]));if(t[2]-t[0]>=360)n=-180,i=180;else if(n>i){const t=this.getClustersInternal([n,r,180,s],e),o=this.getClustersInternal([-180,r,i,s],e);return t.concat(o)}const o=this.trees[this.limitZoom(e)],a=o.range(mc(n),gc(s),mc(i),gc(r)),l=o.flatData,u=[];for(const t of a){const e=this.stride*t;u.push(l[e+5]>1?Gc(l,e,this.clusterProps):this.points[l[e+3]]);}return u}getChildren(t){const e=this.getOriginId(t),n=this.getOriginZoom(t),r=new Error("No cluster with the specified id: "+t),i=this.trees[n];if(!i)throw r;const s=i.flatData;if(e*this.stride>=s.length)throw r;const o=this.options.radius/(this.options.extent*Math.pow(2,n-1)),a=i.within(s[e*this.stride],s[e*this.stride+1],o),l=[];for(const e of a){const n=e*this.stride;s[n+4]===t&&l.push(s[n+5]>1?Xc(s,n,this.clusterProps):xc(this.points[s[n+3]]));}if(0===l.length)throw r;return l}getLeaves(t,e,n){const r=[];return this.appendLeaves(r,t,e=e||10,n=n||0,0),r}getTile(t,e,n){const r=this.trees[this.limitZoom(t)];if(!r)return null;const i=Math.pow(2,t),{extent:s,radius:o}=this.options,a=o/s,l=(n-a)/i,u=(n+1+a)/i,c={transformed:!0,features:[],source:null,x:e,y:n,z:t};return this.addTileFeatures(r.range((e-a)/i,l,(e+1+a)/i,u),r.flatData,e,n,i,c),0===e&&this.addTileFeatures(r.range(1-a/i,l,1,u),r.flatData,i,n,i,c),e===i-1&&this.addTileFeatures(r.range(0,l,a/i,u),r.flatData,-1,n,i,c),c}getClusterExpansionZoom(t){return this.getOriginZoom(t)}appendLeaves(t,e,n,r,i){const s=this.getChildren(e);for(const e of s){const s=e.properties;if(s?.cluster?i+s.point_count<=r?i+=s.point_count:i=this.appendLeaves(t,s.cluster_id,n,r,i):i1;let l,u,c;if(a)l=Yc(e,t,this.clusterProps),u=e[t],c=e[t+1];else {const n=this.points[e[t+3]];l=n.tags,[u,c]=n.geometry;}const h={type:1,geometry:[[Math.round(this.options.extent*(u*i-n)),Math.round(this.options.extent*(c*i-r))]],tags:l};let p;p=a||this.options.generateId?e[t+3]:this.points[e[t+3]].id,void 0!==p&&(h.id=p),s.features.push(h);}}limitZoom(t){return Math.max(this.options.minZoom,Math.min(Math.floor(+t),this.options.maxZoom+1))}cluster(t,e){const{radius:n,extent:r,reduce:i,minPoints:s}=this.options,o=n/(r*Math.pow(2,e)),a=t.flatData,l=[],u=this.stride;for(let n=0;ne&&(f+=a[n+5]);}if(f>p&&f>=s){let t,s=r*p,o=c*p,d=-1;const y=(n/u<<5)+(e+1)+this.points.length;for(const r of h){const l=r*u;if(a[l+2]<=e)continue;a[l+2]=e;const c=a[l+5];s+=a[l]*c,o+=a[l+1]*c,a[l+4]=y,i&&(t||(t=this.map(a,n,!0),d=this.clusterProps.length,this.clusterProps.push(t)),i(t,this.map(a,l)));}a[n+4]=y,l.push(s/f,o/f,1/0,y,-1,f),i&&l.push(d);}else {for(let t=0;t1)for(const t of h){const n=t*u;if(!(a[n+2]<=e)){a[n+2]=e;for(let t=0;t>5}getOriginZoom(t){return (t-this.points.length)%32}map(t,e,n){if(t[e+5]>1){const r=this.clusterProps[t[e+6]];return n?Object.assign({},r):r}const r=this.points[t[e+3]].tags,i=this.options.map(r);return n&&i===r?Object.assign({},i):i}}function Gc(t,e,n){return {id:t[e+3],type:"Point",tags:Yc(t,e,n),geometry:[t[e],t[e+1]]}}function Xc(t,e,n){return {type:"Feature",id:t[e+3],properties:Yc(t,e,n),geometry:{type:"Point",coordinates:[_c(t[e]),Sc(t[e+1])]}}}function Yc(t,e,n){const r=t[e+5],i=r>=1e4?`${Math.round(r/1e3)}k`:r>=1e3?Math.round(r/100)/10+"k":r,s=t[e+6],o=-1===s?{}:Object.assign({},n[s]);return Object.assign(o,{cluster:!0,cluster_id:t[e+3],point_count:r,point_count_abbreviated:i})}const Zc="geojsonvt_clip_start",Hc="geojsonvt_clip_end";function Wc(t,e,n,r,i){const s=e===i.maxZoom?0:i.tolerance/((1<0&&e.size<(i?o:r))return void(n.numPoints+=e.points.length/3);const a=[];for(let t=0;to)&&(n.numSimplified++,a.push(e.points[t],e.points[t+1])),n.numPoints++;i&&function(t,e){let n=0;for(let e=0,r=t.length,i=r-2;e0===e)for(let e=0,n=t.length;e1&&(console.log("invalidating tiles"),console.time("invalidating")),this.invalidateTiles(e),n.debug>1&&console.timeEnd("invalidating");const[r,i,s]=[0,0,0],o=Wc(t,r,i,s,n);o.source=t;const a=ih(r,i,s);if(this.tiles[a]=o,this.tileCoords.push({z:r,x:i,y:s,id:a}),n.debug){const t=`z${r}`;this.stats[t]=(this.stats[t]||0)+1,this.total++;}}getClusterExpansionZoom(t){return null}getChildren(t){return null}getLeaves(t,e,n){return null}getTile(t,e,n){const{extent:r,debug:i}=this.options,s=1<1&&console.log("drilling down to z%d-%d-%d",t,e,n);let a,l=t,u=e,c=n;for(;!a&&l>0;)l--,u>>=1,c>>=1,a=this.tiles[ih(l,u,c)];return a?.source?(i>1&&(console.log("found parent tile z%d-%d-%d",l,u,c),console.time("drilling down")),this.splitTile(a.source,l,u,c,t,e,n),i>1&&console.timeEnd("drilling down"),this.tiles[o]?Qc(this.tiles[o],r):null):null}splitTile(t,e,n,r,i,s,o){const a=[t,e,n,r],l=this.options,u=l.debug;for(;a.length;){r=a.pop(),n=a.pop(),e=a.pop(),t=a.pop();const c=1<1&&console.time("creation"),p=this.tiles[h]=Wc(t,e,n,r,l),this.tileCoords.push({z:e,x:n,y:r,id:h}),u)){u>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",e,n,r,p.numFeatures,p.numPoints,p.numSimplified),console.timeEnd("creation"));const t=`z${e}`;this.stats[t]=(this.stats[t]||0)+1,this.total++;}if(p.source=t,null==i){if(e===l.indexMaxZoom||p.numPoints<=l.indexMaxPoints)continue}else {if(e===l.maxZoom||e===i)continue;if(null!=i){const t=i-e;if(n!==s>>t||r!==o>>t)continue}}if(p.source=null,!t.length)continue;u>1&&console.time("clipping");const f=.5*l.buffer/l.extent,d=.5-f,y=.5+f,m=1+f;let g=null,x=null,v=null,b=null;const w=Mc(t,c,n-f,n+y,Ac.X,p.minX,p.maxX,l),_=Mc(t,c,n+d,n+m,Ac.X,p.minX,p.maxX,l);w&&(g=Mc(w,c,r-f,r+y,Ac.Y,p.minY,p.maxY,l),x=Mc(w,c,r+d,r+m,Ac.Y,p.minY,p.maxY,l)),_&&(v=Mc(_,c,r-f,r+y,Ac.Y,p.minY,p.maxY,l),b=Mc(_,c,r+d,r+m,Ac.Y,p.minY,p.maxY,l)),u>1&&console.timeEnd("clipping"),a.push(g||[],e+1,2*n,2*r),a.push(x||[],e+1,2*n,2*r+1),a.push(v||[],e+1,2*n+1,2*r),a.push(b||[],e+1,2*n+1,2*r+1);}}invalidateTiles(t){if(!t.length)return;const e=this.options,{debug:n}=e;let r=1/0,i=-1/0,s=1/0,o=-1/0;for(const e of t)r=Math.min(r,e.minX),i=Math.max(i,e.maxX),s=Math.min(s,e.minY),o=Math.max(o,e.maxY);const a=e.buffer/e.extent,l=new Set;for(const e in this.tiles){const u=this.tiles[e],c=1<=p||o=d)continue;let y=!1;for(const e of t)if(e.maxX>=h&&e.minX=f&&e.minY1&&console.log("invalidate tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",u.z,u.x,u.y,u.numFeatures,u.numPoints,u.numSimplified);const t=`z${u.z}`;this.stats[t]=(this.stats[t]||0)-1,this.total--;}delete this.tiles[e],l.add(e);}}l.size&&(this.tileCoords=this.tileCoords.filter((t=>!l.has(t.id))));}}function ih(t,e,n){return 32*((1<t.id)),this.index=t.index,this.hasDependencies=!1,this.patternFeatures=[],this.lineClipsArray=[],this.gradients={};for(const t of this.layers)this.gradients[t.id]={};this.layoutVertexArray=new Wo,this.layoutVertexArray2=new Ko,this.indexArray=new sa,this.programConfigurations=new $a(t.layers,t.zoom),this.segments=new ua,this.maxLineLength=0,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id));}populate(t,e,n){this.hasDependencies=ql("line",this.layers,e)||this.hasLineDasharray(this.layers);const r=this.layers[0].layout.get("line-sort-key"),i=!r.isConstant(),s=[];for(const{feature:e,id:o,index:a,sourceLayerIndex:l}of t){const t=this.layers[0]._featureFilter.needGeometry,u=Ga(e,t);if(!this.layers[0]._featureFilter.filter(new Cs(this.zoom),u,n))continue;const c=i?r.evaluate(u,{},n):void 0,h={id:o,properties:e.properties,type:e.type,sourceLayerIndex:l,index:a,geometry:t?u.geometry:qa(e),patterns:{},dashes:{},sortKey:c};s.push(h);}i&&s.sort(((t,e)=>t.sortKey-e.sortKey));for(const r of s){const{geometry:i,index:s,sourceLayerIndex:o}=r;this.hasDependencies?(ql("line",this.layers,e)?Gl("line",this.layers,r,{zoom:this.zoom},e):this.hasLineDasharray(this.layers)&&this.addLineDashDependencies(this.layers,r,this.zoom,e),this.patternFeatures.push(r)):this.addFeature(r,i,s,n,{},{},e.subdivisionGranularity),e.featureIndex.insert(t[s].feature,i,s,o,this.index);}}update(t,e,n,r){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,{imagePositions:n,dashPositions:r});}addFeatures(t,e,n,r){for(const i of this.patternFeatures)this.addFeature(i,i.geometry,i.index,e,n,r,t.subdivisionGranularity);}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return !this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(0!==this.layoutVertexArray2.length&&(this.layoutVertexBuffer2=t.createVertexBuffer(this.layoutVertexArray2,uh)),this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,ah),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0;}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy());}lineFeatureClips(t){if(t.properties&&Object.hasOwn(t.properties,Zc)&&Object.hasOwn(t.properties,Hc))return {start:+t.properties[Zc],end:+t.properties[Hc]}}addFeature(t,e,n,r,i,s,o){const a=this.layers[0].layout,l=a.get("line-join").evaluate(t,{}),u=a.get("line-cap").evaluate(t,{}),c=a.get("line-miter-limit").evaluate(t,{}),h=a.get("line-round-limit").evaluate(t,{});this.lineClips=this.lineFeatureClips(t);for(const n of e)this.addLine(n,t,l,u,c,h,r,o);this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,n,{imagePositions:i,dashPositions:s,canonical:r});}addLine(t,e,n,r,i,s,o,a){if(this.distance=0,this.scaledDistance=0,this.totalDistance=0,t=Su(t,o?a.line.getGranularityForZoomLevel(o.z):1),this.lineClips){this.lineClipsArray.push(this.lineClips);for(let e=0;e=2&&t[u-1].equals(t[u-2]);)u--;let c=0;for(;c0;if(w&&e>c){const t=f.dist(d);if(t>2*h){const e=f.sub(f.sub(d)._mult(h/t)._round());this.updateDistance(d,e),this.addCurrentVertex(e,m,0,0,p),d=e;}}const S=d&&y;let A=S?n:l?"butt":r;if(S&&"round"===A&&(vi&&(A="bevel"),"bevel"===A&&(v>2&&(A="flipbevel"),v100)o=g.mult(-1);else {const t=v*m.add(g).mag()/m.sub(g).mag();o._perp()._mult(t*(_?-1:1));}this.addCurrentVertex(f,o,0,0,p),this.addCurrentVertex(f,o.mult(-1),0,0,p);}else if("bevel"===A||"fakeround"===A){const t=-Math.sqrt(v*v-1),e=_?t:0,n=_?0:t;if(d&&this.addCurrentVertex(f,m,e,n,p),"fakeround"===A){const t=Math.round(180*b/Math.PI/20);for(let e=1;e2*h){const e=f.add(y.sub(f)._mult(h/t)._round());this.updateDistance(f,e),this.addCurrentVertex(e,g,0,0,p),f=e;}}}}addCurrentVertex(t,e,n,r,i,s=!1){const o=e.y*r-e.x,a=-e.y-e.x*r;this.addHalfVertex(t,e.x+e.y*n,e.y-e.x*n,s,!1,n,i),this.addHalfVertex(t,o,a,s,!0,-r,i),this.distance>hh/2&&0===this.totalDistance&&(this.distance=0,this.updateScaledDistance(),this.addCurrentVertex(t,e,n,r,i,s));}addHalfVertex({x:t,y:e},n,r,i,s,o,a){const l=.5*(this.lineClips?this.scaledDistance*(hh-1):this.scaledDistance);this.layoutVertexArray.emplaceBack((t<<1)+(i?1:0),(e<<1)+(s?1:0),Math.round(63*n)+128,Math.round(63*r)+128,1+(0===o?0:o<0?-1:1)|(63&l)<<2,l>>6),this.lineClips&&this.layoutVertexArray2.emplaceBack((this.scaledDistance-this.lineClips.start)/(this.lineClips.end-this.lineClips.start),this.lineClipsArray.length);const u=a.vertexLength++;this.e1>=0&&this.e2>=0&&(this.indexArray.emplaceBack(this.e1,u,this.e2),a.primitiveLength++),s?this.e2=u:this.e1=u;}updateScaledDistance(){this.scaledDistance=this.lineClips?this.lineClips.start+(this.lineClips.end-this.lineClips.start)*this.distance/this.totalDistance:this.distance;}updateDistance(t,e){this.distance+=t.dist(e),this.updateScaledDistance();}hasLineDasharray(t){for(const e of t){const t=e.paint.get("line-dasharray");if(t&&!t.isConstant())return !0}return !1}addLineDashDependencies(t,e,n,r){for(const i of t){const t=i.paint.get("line-dasharray");if(!t||"constant"===t.value.kind)continue;const s="round"===i.layout.get("line-cap").evaluate(e,{}),o={dasharray:t.value.evaluate({zoom:n-1},e,{}),round:s},a={dasharray:t.value.evaluate({zoom:n},e,{}),round:s},l={dasharray:t.value.evaluate({zoom:n+1},e,{}),round:s},u=`${o.dasharray.join(",")},${o.round}`,c=`${a.dasharray.join(",")},${a.round}`,h=`${l.dasharray.join(",")},${l.round}`;r.dashDependencies[u]=o,r.dashDependencies[c]=a,r.dashDependencies[h]=l,e.dashes[i.id]={min:u,mid:c,max:h};}}}let fh,dh;ds("LineBucket",ph,{omit:["layers","patternFeatures"]});var yh={get paint(){return dh=dh||new Ks({"line-opacity":new Ys(wt.paint_line["line-opacity"]),"line-color":new Ys(wt.paint_line["line-color"]),"line-translate":new Xs(wt.paint_line["line-translate"]),"line-translate-anchor":new Xs(wt.paint_line["line-translate-anchor"]),"line-width":new Ys(wt.paint_line["line-width"]),"line-gap-width":new Ys(wt.paint_line["line-gap-width"]),"line-offset":new Ys(wt.paint_line["line-offset"]),"line-blur":new Ys(wt.paint_line["line-blur"]),"line-dasharray":new Zs(wt.paint_line["line-dasharray"]),"line-pattern":new Zs(wt.paint_line["line-pattern"]),"line-gradient":new Ws(wt.paint_line["line-gradient"])})},get layout(){return fh=fh||new Ks({"line-cap":new Ys(wt.layout_line["line-cap"]),"line-join":new Ys(wt.layout_line["line-join"]),"line-miter-limit":new Ys(wt.layout_line["line-miter-limit"]),"line-round-limit":new Ys(wt.layout_line["line-round-limit"]),"line-sort-key":new Ys(wt.layout_line["line-sort-key"])})}};class mh extends Ys{possiblyEvaluate(t,e){return e=new Cs(Math.floor(e.zoom),{now:e.now,fadeDuration:e.fadeDuration,zoomHistory:e.zoomHistory,transition:e.transition}),super.possiblyEvaluate(t,e)}evaluate(t,e,n,r){return e=$({},e,{zoom:Math.floor(e.zoom)}),super.evaluate(t,e,n,r)}}let gh;class xh extends to{constructor(t,e){super(t,yh,e),this.gradientVersion=0,gh||(gh=new mh(yh.paint.properties["line-width"].specification),gh.useIntegerZoom=!0);}_handleSpecialPaintPropertyUpdate(t){if("line-gradient"===t){const t=this.gradientExpression();this.stepInterpolant=!!function(t){return void 0!==t._styleExpression}(t)&&t._styleExpression.expression instanceof cn,this.gradientVersion=(this.gradientVersion+1)%Number.MAX_SAFE_INTEGER;}}gradientExpression(){return this._transitionablePaint._values["line-gradient"].value.expression}recalculate(t,e){super.recalculate(t,e),this.paint._values["line-floorwidth"]=gh.possiblyEvaluate(this._transitioningPaint._values["line-width"].value,t);}createBucket(t){return new ph(t)}queryRadius(t){const e=t,n=vh(ol("line-width",this,e),ol("line-gap-width",this,e)),r=ol("line-offset",this,e);return n/2+Math.abs(r)+al(this.paint.get("line-translate"))}queryIntersectsFeature({queryGeometry:t,feature:e,featureState:r,geometry:i,transform:s,pixelsToTileUnits:o}){const a=ll(t,this.paint.get("line-translate"),this.paint.get("line-translate-anchor"),-s.bearingInRadians,o),l=o/2*vh(this.paint.get("line-width").evaluate(e,r),this.paint.get("line-gap-width").evaluate(e,r)),u=this.paint.get("line-offset").evaluate(e,r);return u&&(i=function(t,e){const r=[];for(const i of t){const t=ul(i),s=[];for(let r=0;r=3)for(const e of r)if(il(t,e))return !0;if(Ja(t,r,n))return !0}return !1}(a,i,l)}isTileClipped(){return !0}}function vh(t,e){return e>0?e+2*t:t}const bh=ao([{name:"a_pos_offset",components:4,type:"Int16"},{name:"a_data",components:4,type:"Uint16"},{name:"a_pixeloffset",components:4,type:"Int16"}],4),wh=ao([{name:"a_projected_pos",components:3,type:"Float32"}],4);ao([{name:"a_fade_opacity",components:1,type:"Uint32"}],4);const _h=ao([{name:"a_placed",components:2,type:"Uint8"},{name:"a_shift",components:2,type:"Float32"},{name:"a_box_real",components:2,type:"Int16"}]);ao([{type:"Int16",name:"anchorPointX"},{type:"Int16",name:"anchorPointY"},{type:"Int16",name:"x1"},{type:"Int16",name:"y1"},{type:"Int16",name:"x2"},{type:"Int16",name:"y2"},{type:"Uint32",name:"featureIndex"},{type:"Uint16",name:"sourceLayerIndex"},{type:"Uint16",name:"bucketIndex"}]);const Sh=ao([{name:"a_pos",components:2,type:"Int16"},{name:"a_anchor_pos",components:2,type:"Int16"},{name:"a_extrude",components:2,type:"Int16"}],4),Ah=ao([{name:"a_pos",components:2,type:"Float32"},{name:"a_radius",components:1,type:"Float32"},{name:"a_flags",components:2,type:"Int16"}],4);function Mh(t,e,n){const r=e.layout.get("text-transform").evaluate(n,{});return "uppercase"===r?t=t.toLocaleUpperCase():"lowercase"===r&&(t=t.toLocaleLowerCase()),Bs.applyArabicShaping&&(t=Bs.applyArabicShaping(t)),t}function kh(t,e,n){for(const r of t.sections)r.text=Mh(r.text,e,n);return t}ao([{name:"triangle",components:3,type:"Uint16"}]),ao([{type:"Int16",name:"anchorX"},{type:"Int16",name:"anchorY"},{type:"Uint16",name:"glyphStartIndex"},{type:"Uint16",name:"numGlyphs"},{type:"Uint32",name:"vertexStartIndex"},{type:"Uint32",name:"lineStartIndex"},{type:"Uint32",name:"lineLength"},{type:"Uint16",name:"segment"},{type:"Uint16",name:"lowerSize"},{type:"Uint16",name:"upperSize"},{type:"Float32",name:"lineOffsetX"},{type:"Float32",name:"lineOffsetY"},{type:"Uint8",name:"writingMode"},{type:"Uint8",name:"placedOrientation"},{type:"Uint8",name:"hidden"},{type:"Uint32",name:"crossTileID"},{type:"Int16",name:"associatedIconIndex"}]),ao([{type:"Int16",name:"anchorX"},{type:"Int16",name:"anchorY"},{type:"Int16",name:"rightJustifiedTextSymbolIndex"},{type:"Int16",name:"centerJustifiedTextSymbolIndex"},{type:"Int16",name:"leftJustifiedTextSymbolIndex"},{type:"Int16",name:"verticalPlacedTextSymbolIndex"},{type:"Int16",name:"placedIconSymbolIndex"},{type:"Int16",name:"verticalPlacedIconSymbolIndex"},{type:"Uint16",name:"key"},{type:"Uint16",name:"textBoxStartIndex"},{type:"Uint16",name:"textBoxEndIndex"},{type:"Uint16",name:"verticalTextBoxStartIndex"},{type:"Uint16",name:"verticalTextBoxEndIndex"},{type:"Uint16",name:"iconBoxStartIndex"},{type:"Uint16",name:"iconBoxEndIndex"},{type:"Uint16",name:"verticalIconBoxStartIndex"},{type:"Uint16",name:"verticalIconBoxEndIndex"},{type:"Uint16",name:"featureIndex"},{type:"Uint16",name:"numHorizontalGlyphVertices"},{type:"Uint16",name:"numVerticalGlyphVertices"},{type:"Uint16",name:"numIconVertices"},{type:"Uint16",name:"numVerticalIconVertices"},{type:"Uint16",name:"useRuntimeCollisionCircles"},{type:"Uint32",name:"crossTileID"},{type:"Float32",name:"textBoxScale"},{type:"Float32",name:"collisionCircleDiameter"},{type:"Uint16",name:"textAnchorOffsetStartIndex"},{type:"Uint16",name:"textAnchorOffsetEndIndex"}]),ao([{type:"Float32",name:"offsetX"}]),ao([{type:"Int16",name:"x"},{type:"Int16",name:"y"},{type:"Int16",name:"tileUnitDistanceFromAnchor"}]),ao([{type:"Uint16",name:"textAnchor"},{type:"Float32",components:2,name:"textOffset"}]);var Ih=24;const Eh={"!":"︕","#":"#",$:"$","%":"%","&":"&","(":"︵",")":"︶","*":"*","+":"+",",":"︐","-":"︲",".":"・","/":"/",":":"︓",";":"︔","<":"︿","=":"=",">":"﹀","?":"︖","@":"@","[":"﹇","\\":"\","]":"﹈","^":"^",_:"︳","`":"`","{":"︷","|":"―","}":"︸","~":"~","¢":"¢","£":"£","¥":"¥","¦":"¦","¬":"¬","¯":" ̄","–":"︲","—":"︱","‘":"﹃","’":"﹄","“":"﹁","”":"﹂","…":"︙","⋯":"︙","‧":"・","₩":"₩","、":"︑","。":"︒","〈":"︿","〉":"﹀","《":"︽","》":"︾","「":"﹁","」":"﹂","『":"﹃","』":"﹄","【":"︻","】":"︼","〔":"︹","〕":"︺","〖":"︗","〗":"︘","!":"︕","(":"︵",")":"︶",",":"︐","-":"︲",".":"・",":":"︓",";":"︔","<":"︿",">":"﹀","?":"︖","[":"﹇","]":"﹈","_":"︳","{":"︷","|":"―","}":"︸","⦅":"︵","⦆":"︶","。":"︒","「":"﹁","」":"﹂"},Th={10:!0,32:!0,38:!0,41:!0,43:!0,45:!0,47:!0,173:!0,183:!0,8203:!0,8208:!0,8211:!0,8231:!0},Fh={40:!0};function Ph(t,e,n,r,i,s){if("fontStack"in e){const r=n[e.fontStack],s=null==r?void 0:r[t];return s?s.metrics.advance*e.scale+i:0}{const t=r[e.imageName];return t?t.displaySize[0]*e.scale*Ih/s+i:0}}function Dh(t,e,n,r){const i=Math.pow(t-e,2);return r?tMath.max(t,this.sections[e].scale)),0)}getMaxImageSize(t){let e=0,n=0;for(let r=0;rn)));}addImageSection(t){const e=t.image?t.image.name:"";if(0===e.length)return void G("Can't add FormattedSection with an empty image.");const n=this.getNextImageSectionCharCode();n?(this.text+=String.fromCharCode(n),this.sections.push({scale:1,verticalAlign:t.verticalAlign||"bottom",imageName:e}),this.sectionIndex.push(this.sections.length-1)):G("Reached maximum number of images 6401");}getNextImageSectionCharCode(){return this.imageSectionID?this.imageSectionID>=63743?null:++this.imageSectionID:(this.imageSectionID=57344,this.imageSectionID)}determineLineBreaks(t,e,n,r,i){const s=[],o=this.determineAverageLineWidth(t,e,n,r,i),a=this.hasZeroWidthSpaces();let l=0,u=0;const c=this.text[Symbol.iterator]();let h=c.next();const p=this.text[Symbol.iterator]();p.next();let f=p.next();const d=this.text[Symbol.iterator]();d.next(),d.next();let y=d.next();for(;!h.done;){const e=this.getSection(u),m=h.value.codePointAt(0);if(Ss(m)||(l+=Ph(m,e,n,r,t,i)),!f.done){const t=ws(m),n=f.value.codePointAt(0);(Th[m]||t||"imageName"in e||!y.done&&Fh[n])&&s.push(Bh(u+1,l,o,s,zh(m,n,t&&a),!1));}u++,h=c.next(),f=p.next(),y=d.next();}return Ch(Bh(this.length(),l,o,s,0,!0))}determineAverageLineWidth(t,e,n,r,i){let s=0,o=0;for(const e of this.text){const a=this.getSection(o);s+=Ph(e.codePointAt(0),a,n,r,t,i),o++;}return s/Math.max(1,Math.ceil(s/e))}}const Lh=4294967296,Oh=1/Lh,$h="undefined"==typeof TextDecoder?null:new TextDecoder("utf-8");class Rh{constructor(t=new Uint8Array(16)){this.buf=ArrayBuffer.isView(t)?t:new Uint8Array(t),this.dataView=new DataView(this.buf.buffer),this.pos=0,this.type=0,this.length=this.buf.length;}readFields(t,e,n=this.length){for(;this.pos>3,i=this.pos;this.type=7&n,t(r,e,this),this.pos===i&&this.skip(n);}return e}readMessage(t,e){return this.readFields(t,e,this.readVarint()+this.pos)}readFixed32(){const t=this.dataView.getUint32(this.pos,!0);return this.pos+=4,t}readSFixed32(){const t=this.dataView.getInt32(this.pos,!0);return this.pos+=4,t}readFixed64(){const t=this.dataView.getUint32(this.pos,!0)+this.dataView.getUint32(this.pos+4,!0)*Lh;return this.pos+=8,t}readSFixed64(){const t=this.dataView.getUint32(this.pos,!0)+this.dataView.getInt32(this.pos+4,!0)*Lh;return this.pos+=8,t}readFloat(){const t=this.dataView.getFloat32(this.pos,!0);return this.pos+=4,t}readDouble(){const t=this.dataView.getFloat64(this.pos,!0);return this.pos+=8,t}readVarint(t){const e=this.buf;let n,r;return r=e[this.pos++],n=127&r,r<128?n:(r=e[this.pos++],n|=(127&r)<<7,r<128?n:(r=e[this.pos++],n|=(127&r)<<14,r<128?n:(r=e[this.pos++],n|=(127&r)<<21,r<128?n:(r=e[this.pos],n|=(15&r)<<28,function(t,e,n){const r=n.buf;let i,s;if(s=r[n.pos++],i=(112&s)>>4,s<128)return Nh(t,i,e);if(s=r[n.pos++],i|=(127&s)<<3,s<128)return Nh(t,i,e);if(s=r[n.pos++],i|=(127&s)<<10,s<128)return Nh(t,i,e);if(s=r[n.pos++],i|=(127&s)<<17,s<128)return Nh(t,i,e);if(s=r[n.pos++],i|=(127&s)<<24,s<128)return Nh(t,i,e);if(s=r[n.pos++],i|=(1&s)<<31,s<128)return Nh(t,i,e);throw new Error("Expected varint not more than 10 bytes")}(n,t,this)))))}readVarint64(){return this.readVarint(!0)}readSVarint(){const t=this.readVarint();return t%2==1?(t+1)/-2:t/2}readBoolean(){return Boolean(this.readVarint())}readString(){const t=this.readVarint()+this.pos,e=this.pos;return this.pos=t,t-e>=12&&$h?$h.decode(this.buf.subarray(e,t)):function(t,e,n){let r="",i=e;for(;i239?4:e>223?3:e>191?2:1;if(i+u>n)break;1===u?e<128&&(l=e):2===u?(s=t[i+1],128==(192&s)&&(l=(31&e)<<6|63&s,l<=127&&(l=null))):3===u?(s=t[i+1],o=t[i+2],128==(192&s)&&128==(192&o)&&(l=(15&e)<<12|(63&s)<<6|63&o,(l<=2047||l>=55296&&l<=57343)&&(l=null))):4===u&&(s=t[i+1],o=t[i+2],a=t[i+3],128==(192&s)&&128==(192&o)&&128==(192&a)&&(l=(15&e)<<18|(63&s)<<12|(63&o)<<6|63&a,(l<=65535||l>=1114112)&&(l=null))),null===l?(l=65533,u=1):l>65535&&(l-=65536,r+=String.fromCharCode(l>>>10&1023|55296),l=56320|1023&l),r+=String.fromCharCode(l),i+=u;}return r}(this.buf,e,t)}readBytes(){const t=this.readVarint()+this.pos,e=this.buf.subarray(this.pos,t);return this.pos=t,e}readPackedVarint(t=[],e){const n=this.readPackedEnd();for(;this.pos127;);else if(2===e)this.pos=this.readVarint()+this.pos;else if(5===e)this.pos+=4;else {if(1!==e)throw new Error(`Unimplemented type: ${e}`);this.pos+=8;}}writeTag(t,e){this.writeVarint(t<<3|e);}realloc(t){let e=this.length||16;for(;e268435455||t<0?function(t,e){let n,r;if(t>=0?(n=t%4294967296|0,r=t/4294967296|0):(n=~(-t%4294967296),r=~(-t/4294967296),4294967295^n?n=n+1|0:(n=0,r=r+1|0)),t>=0x10000000000000000||t<-0x10000000000000000)throw new Error("Given varint doesn't fit into 10 bytes");e.realloc(10),function(t,e,n){n.buf[n.pos++]=127&t|128,t>>>=7,n.buf[n.pos++]=127&t|128,t>>>=7,n.buf[n.pos++]=127&t|128,t>>>=7,n.buf[n.pos++]=127&t|128,n.buf[n.pos]=127&(t>>>=7);}(n,0,e),function(t,e){const n=(7&t)<<4;e.buf[e.pos++]|=n|((t>>>=3)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t)))));}(r,e);}(t,this):(this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127))));}writeSVarint(t){this.writeVarint(t<0?2*-t-1:2*t);}writeBoolean(t){this.writeVarint(+t);}writeString(t){t=String(t),this.realloc(4*t.length),this.pos++;const e=this.pos;this.pos=function(t,e,n){for(let r,i,s=0;s55295&&r<57344){if(!i){r>56319||s+1===e.length?(t[n++]=239,t[n++]=191,t[n++]=189):i=r;continue}if(r<56320){t[n++]=239,t[n++]=191,t[n++]=189,i=r;continue}r=i-55296<<10|r-56320|65536,i=null;}else i&&(t[n++]=239,t[n++]=191,t[n++]=189,i=null);r<128?t[n++]=r:(r<2048?t[n++]=r>>6|192:(r<65536?t[n++]=r>>12|224:(t[n++]=r>>18|240,t[n++]=r>>12&63|128),t[n++]=r>>6&63|128),t[n++]=63&r|128);}return n}(this.buf,t,this.pos);const n=this.pos-e;n>=128&&Uh(e,n,this),this.pos=e-1,this.writeVarint(n),this.pos+=n;}writeFloat(t){this.realloc(4),this.dataView.setFloat32(this.pos,t,!0),this.pos+=4;}writeDouble(t){this.realloc(8),this.dataView.setFloat64(this.pos,t,!0),this.pos+=8;}writeBytes(t){const e=t.length;this.writeVarint(e),this.realloc(e);for(let n=0;n=128&&Uh(n,r,this),this.pos=n-1,this.writeVarint(r),this.pos+=r;}writeMessage(t,e,n){this.writeTag(t,2),this.writeRawMessage(e,n);}writePackedVarint(t,e){e.length&&this.writeMessage(t,jh,e);}writePackedSVarint(t,e){e.length&&this.writeMessage(t,qh,e);}writePackedBoolean(t,e){e.length&&this.writeMessage(t,Yh,e);}writePackedFloat(t,e){e.length&&this.writeMessage(t,Gh,e);}writePackedDouble(t,e){e.length&&this.writeMessage(t,Xh,e);}writePackedFixed32(t,e){e.length&&this.writeMessage(t,Zh,e);}writePackedSFixed32(t,e){e.length&&this.writeMessage(t,Hh,e);}writePackedFixed64(t,e){e.length&&this.writeMessage(t,Wh,e);}writePackedSFixed64(t,e){e.length&&this.writeMessage(t,Kh,e);}writeBytesField(t,e){this.writeTag(t,2),this.writeBytes(e);}writeFixed32Field(t,e){this.writeTag(t,5),this.writeFixed32(e);}writeSFixed32Field(t,e){this.writeTag(t,5),this.writeSFixed32(e);}writeFixed64Field(t,e){this.writeTag(t,1),this.writeFixed64(e);}writeSFixed64Field(t,e){this.writeTag(t,1),this.writeSFixed64(e);}writeVarintField(t,e){this.writeTag(t,0),this.writeVarint(e);}writeSVarintField(t,e){this.writeTag(t,0),this.writeSVarint(e);}writeStringField(t,e){this.writeTag(t,2),this.writeString(e);}writeFloatField(t,e){this.writeTag(t,5),this.writeFloat(e);}writeDoubleField(t,e){this.writeTag(t,1),this.writeDouble(e);}writeBooleanField(t,e){this.writeVarintField(t,+e);}}function Nh(t,e,n){return n?4294967296*e+(t>>>0):4294967296*(e>>>0)+(t>>>0)}function Uh(t,e,n){const r=e<=16383?1:e<=2097151?2:e<=268435455?3:Math.floor(Math.log(e)/(7*Math.LN2));n.realloc(r);for(let e=n.pos-1;e>=t;e--)n.buf[e+r]=n.buf[e];}function jh(t,e){for(let n=0;ne.h-t.h));const r=[{x:0,y:0,w:Math.max(Math.ceil(Math.sqrt(e/.95)),n),h:1/0}];let i=0,s=0;for(const e of t)for(let t=r.length-1;t>=0;t--){const n=r[t];if(!(e.w>n.w||e.h>n.h)){if(e.x=n.x,e.y=n.y,s=Math.max(s,e.y+e.h),i=Math.max(i,e.x+e.w),e.w===n.w&&e.h===n.h){const e=r.pop();e&&tm.toCodeUnitIndex(t)));const t=v(m.toString(),x);for(const e of t){const t=[...e].map((()=>0));g.push(new Vh(e,m.sections,t));}}else if(b){g=[],x=x.map((t=>m.toCodeUnitIndex(t)));let t=0;const e=[];for(const n of m.text)e.push(...Array(n.length).fill(m.sectionIndex[t])),t++;const n=b(m.text,e,x);for(const t of n){const e=[];let n="";for(const r of t[0])e.push(t[1][n.length]),n+=r;g.push(new Vh(t[0],m.sections,e));}}else g=function(t,e){const n=[];let r=0;for(const i of e)n.push(t.substring(r,i)),r=i;return ru){const t=Math.ceil(s/u);i*=t/o,o=t;}return {x1:r,y1:i,x2:r+s,y2:i+o}}function yp(t,e,n,r,i,s){const o=t.image;let a;if(o.content){const t=o.content,e=o.pixelRatio||1;a=[t[0]/e,t[1]/e,o.displaySize[0]-t[2]/e,o.displaySize[1]-t[3]/e];}const l=e.left*s,u=e.right*s;let c,h,p,f;"width"===n||"both"===n?(f=i[0]+l-r[3],h=i[0]+u+r[1]):(f=i[0]+(l+u-o.displaySize[0])/2,h=f+o.displaySize[0]);const d=e.top*s,y=e.bottom*s;return "height"===n||"both"===n?(c=i[1]+d-r[0],p=i[1]+y+r[2]):(c=i[1]+(d+y-o.displaySize[1])/2,p=c+o.displaySize[1]),{image:o,top:c,right:h,bottom:p,left:f,collisionPadding:a}}ds("ImagePosition",np),ds("ImageAtlas",rp),t.ax=void 0,(ip=t.ax||(t.ax={}))[ip.none=0]="none",ip[ip.horizontal=1]="horizontal",ip[ip.vertical=2]="vertical",ip[ip.horizontalOnly=3]="horizontalOnly";const mp=128,gp=32640;function xp(t,e){const{expression:n}=e;if("constant"===n.kind)return {kind:"constant",layoutSize:n.evaluate(new Cs(t+1))};if("source"===n.kind)return {kind:"source"};{const{zoomStops:e,interpolationType:r}=n;let i=0;for(;it.id)),this.index=e.index,this.pixelRatio=e.pixelRatio,this.sourceLayerIndex=e.sourceLayerIndex,this.hasDependencies=!1,this.hasRTLText=!1,this.sortKeyRanges=[],this.collisionCircleArray=[];const n=this.layers[0]._unevaluatedLayout._values;this.textSizeData=xp(this.zoom,n["text-size"]),this.iconSizeData=xp(this.zoom,n["icon-size"]);const r=this.layers[0].layout,i=r.get("symbol-sort-key"),s=r.get("symbol-z-order");this.canOverlap="never"!==vp(r,"text-overlap","text-allow-overlap")||"never"!==vp(r,"icon-overlap","icon-allow-overlap")||r.get("text-ignore-placement")||r.get("icon-ignore-placement"),this.sortFeaturesByKey="viewport-y"!==s&&!i.isConstant(),this.sortFeaturesByY=("viewport-y"===s||"auto"===s&&!this.sortFeaturesByKey)&&this.canOverlap,"point"===r.get("symbol-placement")&&(this.writingModes=r.get("text-writing-mode").map((e=>t.ax[e]))),this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id)),this.sourceID=e.sourceID;}createArrays(){this.text=new Ap(new $a(this.layers,this.zoom,(t=>t.startsWith("text")))),this.icon=new Ap(new $a(this.layers,this.zoom,(t=>t.startsWith("icon")))),this.glyphOffsetArray=new Ro,this.lineVertexArray=new No,this.symbolInstances=new $o,this.textAnchorOffsets=new jo;}calculateGlyphDependencies(t,e,n,r,i){for(const s of t)if(e[s.codePointAt(0)]=!0,(n||r)&&i){const t=Eh[s];t&&(e[t.codePointAt(0)]=!0);}}populate(e,n,r){var i;const s=this.layers[0],o=s.layout,a=o.get("text-font"),l=o.get("text-field"),u=o.get("icon-image"),c=("constant"!==l.value.kind||l.value.value instanceof ze&&!l.value.value.isEmpty()||l.value.value.toString().length>0)&&("constant"!==a.value.kind||a.value.value.length>0),h="constant"!==u.value.kind||!!u.value.value||Object.keys(u.parameters).length>0,p=o.get("symbol-sort-key");if(this.features=[],!c&&!h)return;const f=n.iconDependencies,d=n.glyphDependencies,y=n.availableImages,m=new Cs(this.zoom);for(const{feature:n,id:l,index:u,sourceLayerIndex:g}of e){const e=s._featureFilter.needGeometry,x=Ga(n,e);if(!s._featureFilter.filter(m,x,r))continue;let v,b;if(e||(x.geometry=qa(n)),c){const t=s.getValueAndResolveTokens("text-field",x,r,y),e=ze.factory(t);this.hasRTLText||(this.hasRTLText=Sp(e)),(!this.hasRTLText||"unavailable"===Bs.getRTLTextPluginStatus()||this.hasRTLText&&Bs.isParsed())&&(v=kh(e,s,x));}if(h){const t=s.getValueAndResolveTokens("icon-image",x,r,y);b=t instanceof Re?t:Re.fromString(t);}if(!v&&!b)continue;const w=this.sortFeaturesByKey?p.evaluate(x,{},r):void 0;if(this.features.push({id:l,text:v,icon:b,index:u,sourceLayerIndex:g,geometry:x.geometry,properties:n.properties,type:Cu.types[n.type],sortKey:w}),b&&(f[b.name]=!0),v){const e=a.evaluate(x,{},r).join(","),n="viewport"!==o.get("text-rotation-alignment")&&"point"!==o.get("symbol-placement");this.allowVerticalPlacement=null===(i=this.writingModes)||void 0===i?void 0:i.includes(t.ax.vertical);for(const t of v.sections)if(t.image)f[t.image.name]=!0;else {const r=As(v.toString()),i=t.fontStack||e;d[i]||(d[i]={}),this.calculateGlyphDependencies(t.text,d[i],n,this.allowVerticalPlacement,r);}}}"line"===o.get("symbol-placement")&&(this.features=function(t){const e={},n={},r=[];let i=0;function s(e){r.push(t[e]),i++;}function o(t,e,i){const s=n[t];return delete n[t],n[e]=s,r[s].geometry[0].pop(),r[s].geometry[0]=r[s].geometry[0].concat(i[0]),s}function a(t,n,i){const s=e[n];return delete e[n],e[t]=s,r[s].geometry[0].shift(),r[s].geometry[0]=i[0].concat(r[s].geometry[0]),s}function l(t,e,n){const r=n?e[0][e[0].length-1]:e[0][0];return `${t}:${r.x}:${r.y}`}for(let u=0;ut.geometry))}(this.features)),this.sortFeaturesByKey&&this.features.sort(((t,e)=>t.sortKey-e.sortKey));}update(t,e,n){this.stateDependentLayers.length&&(this.text.programConfigurations.updatePaintArrays(t,e,this.layers,{imagePositions:n}),this.icon.programConfigurations.updatePaintArrays(t,e,this.layers,{imagePositions:n}));}isEmpty(){return 0===this.symbolInstances.length&&!this.hasRTLText}uploadPending(){return !this.uploaded||this.text.programConfigurations.needsUpload||this.icon.programConfigurations.needsUpload}upload(t){!this.uploaded&&this.hasDebugData()&&(this.textCollisionBox.upload(t),this.iconCollisionBox.upload(t)),this.text.upload(t,this.sortFeaturesByY,!this.uploaded,this.text.programConfigurations.needsUpload),this.icon.upload(t,this.sortFeaturesByY,!this.uploaded,this.icon.programConfigurations.needsUpload),this.uploaded=!0;}destroyDebugData(){this.textCollisionBox.destroy(),this.iconCollisionBox.destroy();}destroy(){this.text.destroy(),this.icon.destroy(),this.hasDebugData()&&this.destroyDebugData();}addToLineVertexArray(t,e){const n=this.lineVertexArray.length;if(void 0!==t.segment){let n=t.dist(e[t.segment+1]),r=t.dist(e[t.segment]);const i={};for(let r=t.segment+1;r=0;n--)i[n]={x:e[n].x,y:e[n].y,tileUnitDistanceFromAnchor:r},n>0&&(r+=e[n-1].dist(e[n]));for(let t=0;t0}hasIconData(){return this.icon.segments.get().length>0}hasDebugData(){return this.textCollisionBox&&this.iconCollisionBox}hasTextCollisionBoxData(){return this.hasDebugData()&&this.textCollisionBox.segments.get().length>0}hasIconCollisionBoxData(){return this.hasDebugData()&&this.iconCollisionBox.segments.get().length>0}addIndicesForPlacedSymbol(t,e){const n=t.placedSymbolArray.get(e),r=n.vertexStartIndex+4*n.numGlyphs;for(let e=n.vertexStartIndex;er[t]-r[e]||i[e]-i[t])),s}addToSortKeyRanges(t,e){const n=this.sortKeyRanges[this.sortKeyRanges.length-1];(null==n?void 0:n.sortKey)===e?n.symbolInstanceEnd=t+1:this.sortKeyRanges.push({sortKey:e,symbolInstanceStart:t,symbolInstanceEnd:t+1});}sortFeatures(t){if(this.sortFeaturesByY&&this.sortedAngle!==t&&!(this.text.segments.get().length>1||this.icon.segments.get().length>1)){this.symbolInstanceIndexes=this.getSortedSymbolIndexes(t),this.sortedAngle=t,this.text.indexArray.clear(),this.icon.indexArray.clear(),this.featureSortOrder=[];for(const t of this.symbolInstanceIndexes){const e=this.symbolInstances.get(t);this.featureSortOrder.push(e.featureIndex);const n=[e.rightJustifiedTextSymbolIndex,e.centerJustifiedTextSymbolIndex,e.leftJustifiedTextSymbolIndex];for(let t=0;t=0&&n.indexOf(e)===t&&this.addIndicesForPlacedSymbol(this.text,e);}e.verticalPlacedTextSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.text,e.verticalPlacedTextSymbolIndex),e.placedIconSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.icon,e.placedIconSymbolIndex),e.verticalPlacedIconSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.icon,e.verticalPlacedIconSymbolIndex);}this.text.indexBuffer&&this.text.indexBuffer.updateData(this.text.indexArray),this.icon.indexBuffer&&this.icon.indexBuffer.updateData(this.icon.indexArray);}}}let Ip,Ep;ds("SymbolBucket",kp,{omit:["layers","collisionBoxArray","features","compareText"]}),kp.MAX_GLYPHS=65535,kp.addDynamicAttributes=_p;var Tp={get paint(){return Ep=Ep||new Ks({"icon-opacity":new Ys(wt.paint_symbol["icon-opacity"]),"icon-color":new Ys(wt.paint_symbol["icon-color"]),"icon-halo-color":new Ys(wt.paint_symbol["icon-halo-color"]),"icon-halo-width":new Ys(wt.paint_symbol["icon-halo-width"]),"icon-halo-blur":new Ys(wt.paint_symbol["icon-halo-blur"]),"icon-translate":new Xs(wt.paint_symbol["icon-translate"]),"icon-translate-anchor":new Xs(wt.paint_symbol["icon-translate-anchor"]),"text-opacity":new Ys(wt.paint_symbol["text-opacity"]),"text-color":new Ys(wt.paint_symbol["text-color"],{runtimeType:Nt,getOverride:t=>t.textColor,hasOverride:t=>!!t.textColor}),"text-halo-color":new Ys(wt.paint_symbol["text-halo-color"]),"text-halo-width":new Ys(wt.paint_symbol["text-halo-width"]),"text-halo-blur":new Ys(wt.paint_symbol["text-halo-blur"]),"text-translate":new Xs(wt.paint_symbol["text-translate"]),"text-translate-anchor":new Xs(wt.paint_symbol["text-translate-anchor"])})},get layout(){return Ip=Ip||new Ks({"symbol-placement":new Xs(wt.layout_symbol["symbol-placement"]),"symbol-spacing":new Xs(wt.layout_symbol["symbol-spacing"]),"symbol-avoid-edges":new Xs(wt.layout_symbol["symbol-avoid-edges"]),"symbol-sort-key":new Ys(wt.layout_symbol["symbol-sort-key"]),"symbol-z-order":new Xs(wt.layout_symbol["symbol-z-order"]),"icon-allow-overlap":new Xs(wt.layout_symbol["icon-allow-overlap"]),"icon-overlap":new Xs(wt.layout_symbol["icon-overlap"]),"icon-ignore-placement":new Xs(wt.layout_symbol["icon-ignore-placement"]),"icon-optional":new Xs(wt.layout_symbol["icon-optional"]),"icon-rotation-alignment":new Xs(wt.layout_symbol["icon-rotation-alignment"]),"icon-size":new Ys(wt.layout_symbol["icon-size"]),"icon-text-fit":new Xs(wt.layout_symbol["icon-text-fit"]),"icon-text-fit-padding":new Xs(wt.layout_symbol["icon-text-fit-padding"]),"icon-image":new Ys(wt.layout_symbol["icon-image"]),"icon-rotate":new Ys(wt.layout_symbol["icon-rotate"]),"icon-padding":new Ys(wt.layout_symbol["icon-padding"]),"icon-keep-upright":new Xs(wt.layout_symbol["icon-keep-upright"]),"icon-offset":new Ys(wt.layout_symbol["icon-offset"]),"icon-anchor":new Ys(wt.layout_symbol["icon-anchor"]),"icon-pitch-alignment":new Xs(wt.layout_symbol["icon-pitch-alignment"]),"text-pitch-alignment":new Xs(wt.layout_symbol["text-pitch-alignment"]),"text-rotation-alignment":new Xs(wt.layout_symbol["text-rotation-alignment"]),"text-field":new Ys(wt.layout_symbol["text-field"]),"text-font":new Ys(wt.layout_symbol["text-font"]),"text-size":new Ys(wt.layout_symbol["text-size"]),"text-max-width":new Ys(wt.layout_symbol["text-max-width"]),"text-line-height":new Xs(wt.layout_symbol["text-line-height"]),"text-letter-spacing":new Ys(wt.layout_symbol["text-letter-spacing"]),"text-justify":new Ys(wt.layout_symbol["text-justify"]),"text-radial-offset":new Ys(wt.layout_symbol["text-radial-offset"]),"text-variable-anchor":new Xs(wt.layout_symbol["text-variable-anchor"]),"text-variable-anchor-offset":new Ys(wt.layout_symbol["text-variable-anchor-offset"]),"text-anchor":new Ys(wt.layout_symbol["text-anchor"]),"text-max-angle":new Xs(wt.layout_symbol["text-max-angle"]),"text-writing-mode":new Xs(wt.layout_symbol["text-writing-mode"]),"text-rotate":new Ys(wt.layout_symbol["text-rotate"]),"text-padding":new Xs(wt.layout_symbol["text-padding"]),"text-keep-upright":new Xs(wt.layout_symbol["text-keep-upright"]),"text-transform":new Ys(wt.layout_symbol["text-transform"]),"text-offset":new Ys(wt.layout_symbol["text-offset"]),"text-allow-overlap":new Xs(wt.layout_symbol["text-allow-overlap"]),"text-overlap":new Xs(wt.layout_symbol["text-overlap"]),"text-ignore-placement":new Xs(wt.layout_symbol["text-ignore-placement"]),"text-optional":new Xs(wt.layout_symbol["text-optional"])})}};class Fp{constructor(t){if(void 0===t.property.overrides)throw new Error("overrides must be provided to instantiate FormatSectionOverride class");this.type=t.property.overrides?t.property.overrides.runtimeType:Lt,this.defaultValue=t;}evaluate(t){if(t.formattedSection){const e=this.defaultValue.property.overrides;if(null==e?void 0:e.hasOverride(t.formattedSection))return e.getOverride(t.formattedSection)}return t.feature&&t.featureState?this.defaultValue.evaluate(t.feature,t.featureState):this.defaultValue.property.specification.default}eachChild(t){this.defaultValue.isConstant()||t(this.defaultValue.value._styleExpression.expression);}outputDefined(){return !1}serialize(){return null}}ds("FormatSectionOverride",Fp,{omit:["defaultValue"]});class Pp extends to{constructor(t,e){super(t,Tp,e);}recalculate(t,e){if(super.recalculate(t,e),"auto"===this.layout.get("icon-rotation-alignment")&&(this.layout._values["icon-rotation-alignment"]="point"!==this.layout.get("symbol-placement")?"map":"viewport"),"auto"===this.layout.get("text-rotation-alignment")&&(this.layout._values["text-rotation-alignment"]="point"!==this.layout.get("symbol-placement")?"map":"viewport"),"auto"===this.layout.get("text-pitch-alignment")&&(this.layout._values["text-pitch-alignment"]="map"===this.layout.get("text-rotation-alignment")?"map":"viewport"),"auto"===this.layout.get("icon-pitch-alignment")&&(this.layout._values["icon-pitch-alignment"]=this.layout.get("icon-rotation-alignment")),"point"===this.layout.get("symbol-placement")){const t=this.layout.get("text-writing-mode");if(t){const e=[];for(const n of t)e.includes(n)||e.push(n);this.layout._values["text-writing-mode"]=e;}else this.layout._values["text-writing-mode"]=["horizontal"];}this._setPaintOverrides();}getValueAndResolveTokens(t,e,n,r){const i=this.layout.get(t).evaluate(e,{},n,r),s=this._unevaluatedLayout._values[t];return s.isDataDriven()||ii(s.value)||!i?i:function(t,e){return e.replace(/{([^{}]+)}/g,((e,n)=>t&&n in t?String(t[n]):""))}(e.properties,i)}createBucket(t){return new kp(t)}queryRadius(){return 0}queryIntersectsFeature(){throw new Error("Should take a different path in FeatureIndex")}_setPaintOverrides(){for(const t of Tp.paint.overridableProperties){if(!Pp.hasPaintOverride(this.layout,t))continue;const e=this.paint.get(t),n=new Fp(e),r=new ri(n,e.property.specification);let i=null;i="constant"===e.value.kind||"source"===e.value.kind?new oi("source",r):new ai("composite",r,e.value.zoomStops),this.paint._values[t]=new qs(e.property,i,e.parameters);}}_handleOverridablePaintPropertyUpdate(t,e,n){return !(!this.layout||e.isDataDriven()||n.isDataDriven())&&Pp.hasPaintOverride(this.layout,t)}static hasPaintOverride(t,e){const n=t.get("text-field"),r=Tp.paint.properties[e];let i=!1;const s=t=>{var e;for(const n of t)if(null===(e=r.overrides)||void 0===e?void 0:e.hasOverride(n))return void(i=!0)};if("constant"===n.value.kind&&n.value.value instanceof ze)s(n.value.value.sections);else if("source"===n.value.kind||"composite"===n.value.kind){const t=e=>{i||(e instanceof Xe&&qe(e.value)===Xt?s(e.value.sections):e instanceof Pn?s(e.sections):e.eachChild(t));},e=n.value;e._styleExpression&&t(e._styleExpression.expression);}return i}}let Dp;var zp={get paint(){return Dp=Dp||new Ks({"background-color":new Xs(wt.paint_background["background-color"]),"background-pattern":new Hs(wt.paint_background["background-pattern"]),"background-opacity":new Xs(wt.paint_background["background-opacity"])})}};class Bp extends to{constructor(t,e){super(t,zp,e);}}class Cp extends to{constructor(t,e){super(t,{},e),this.onAdd=t=>{this.implementation.onAdd&&this.implementation.onAdd(t,t.painter.context.gl);},this.onRemove=t=>{this.implementation.onRemove&&this.implementation.onRemove(t,t.painter.context.gl);},this.implementation=t;}is3D(){return "3d"===this.implementation.renderingMode}hasOffscreenPass(){return void 0!==this.implementation.prerender}recalculate(){}updateTransitions(){}hasTransition(){return !1}serialize(){throw new Error("Custom layers cannot be serialized")}}class Vp{constructor(t){this._methodToThrottle=t,this._triggered=!1,this._channel=new MessageChannel,this._channel.port2.onmessage=()=>{this._triggered=!1,this._methodToThrottle();};}trigger(){var t;this._triggered||(this._triggered=!0,null===(t=this._channel)||void 0===t||t.port1.postMessage(!0));}remove(){delete this._channel,this._methodToThrottle=()=>{};}}const Lp={once:!0},Op=6371008.8;class $p{constructor(t,e){if(isNaN(t)||isNaN(e))throw new Error(`Invalid LngLat object: (${t}, ${e})`);if(this.lng=+t,this.lat=+e,this.lat>90||this.lat<-90)throw new Error("Invalid LngLat latitude value: must be between -90 and 90")}wrap(){return new $p(O(this.lng,-180,180),this.lat)}toArray(){return [this.lng,this.lat]}toString(){return `LngLat(${this.lng}, ${this.lat})`}distanceTo(t){const e=Math.PI/180,n=this.lat*e,r=t.lat*e,i=Math.sin(n)*Math.sin(r)+Math.cos(n)*Math.cos(r)*Math.cos((t.lng-this.lng)*e);return Op*Math.acos(Math.min(i,1))}static convert(t){if(t instanceof $p)return t;if(Array.isArray(t)&&(2===t.length||3===t.length))return new $p(Number(t[0]),Number(t[1]));if(!Array.isArray(t)&&"object"==typeof t&&null!==t)return new $p(Number("lng"in t?t.lng:t.lon),Number(t.lat));throw new Error("`LngLatLike` argument must be specified as a LngLat instance, an object {lng: , lat: }, an object {lon: , lat: }, or an array of [, ]")}}const Rp=2*Math.PI*Op;function Np(t){return Rp*Math.cos(t*Math.PI/180)}function Up(t){return (180+t)/360}function jp(t){return (180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t*Math.PI/360)))/360}function qp(t,e){return t/Np(e)}function Gp(t){return 360*t-180}function Xp(t){return 360/Math.PI*Math.atan(Math.exp((180-360*t)*Math.PI/180))-90}function Yp(t,e){return t*Np(Xp(e))}class Zp{constructor(t,e,n=0){this.x=+t,this.y=+e,this.z=+n;}static fromLngLat(t,e=0){const n=$p.convert(t);return new Zp(Up(n.lng),jp(n.lat),qp(e,n.lat))}toLngLat(){return new $p(Gp(this.x),Xp(this.y))}toAltitude(){return Yp(this.z,this.y)}meterInMercatorCoordinateUnits(){return 1/Rp*(t=Xp(this.y),1/Math.cos(t*Math.PI/180));var t;}}function Hp(t,e,n){var r=2*Math.PI*6378137/256/Math.pow(2,n);return [t*r-2*Math.PI*6378137/2,e*r-2*Math.PI*6378137/2]}class Wp{constructor(t,e,n){if(!function(t,e,n){return !(t<0||t>25||n<0||n>=Math.pow(2,t)||e<0||e>=Math.pow(2,t))}(t,e,n))throw new Error(`x=${e}, y=${n}, z=${t} outside of bounds. 0<=x<${Math.pow(2,t)}, 0<=y<${Math.pow(2,t)} 0<=z<=25 `);this.z=t,this.x=e,this.y=n,this.key=Qp(0,t,t,e,n);}equals(t){return this.z===t.z&&this.x===t.x&&this.y===t.y}url(t,e,n){const r=(s=this.y,o=this.z,a=Hp(256*(i=this.x),256*(s=Math.pow(2,o)-s-1),o),l=Hp(256*(i+1),256*(s+1),o),a[0]+","+a[1]+","+l[0]+","+l[1]);var i,s,o,a,l;const u=function(t,e,n){let r="";for(let i=t;i>0;i--){const t=1<1?"@2x":"").replace(/{quadkey}/g,u).replace(/{bbox-epsg-3857}/g,r)}isChildOf(t){const e=this.z-t.z;return e>0&&t.x===this.x>>e&&t.y===this.y>>e}getTilePoint(t){const e=Math.pow(2,this.z);return new n((t.x*e-this.x)*T,(t.y*e-this.y)*T)}toString(){return `${this.z}/${this.x}/${this.y}`}}class Kp{constructor(t,e){this.wrap=t,this.canonical=e,this.key=Qp(t,e.z,e.z,e.x,e.y);}}class Jp{constructor(t,e,n,r,i){if(this.terrainRttPosMatrix32f=null,t= z; overscaledZ = ${t}; z = ${n}`);this.overscaledZ=t,this.wrap=e,this.canonical=new Wp(n,+r,+i),this.key=Qp(e,t,n,r,i);}clone(){return new Jp(this.overscaledZ,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)}equals(t){return this.overscaledZ===t.overscaledZ&&this.wrap===t.wrap&&this.canonical.equals(t.canonical)}scaledTo(t){if(t>this.overscaledZ)throw new Error(`targetZ > this.overscaledZ; targetZ = ${t}; overscaledZ = ${this.overscaledZ}`);const e=this.canonical.z-t;return t>this.canonical.z?new Jp(t,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y):new Jp(t,this.wrap,t,this.canonical.x>>e,this.canonical.y>>e)}isOverscaled(){return this.overscaledZ>this.canonical.z}calculateScaledKey(t,e){if(t>this.overscaledZ)throw new Error(`targetZ > this.overscaledZ; targetZ = ${t}; overscaledZ = ${this.overscaledZ}`);const n=this.canonical.z-t;return t>this.canonical.z?Qp(this.wrap*+e,t,this.canonical.z,this.canonical.x,this.canonical.y):Qp(this.wrap*+e,t,t,this.canonical.x>>n,this.canonical.y>>n)}isChildOf(t){if(t.wrap!==this.wrap)return !1;if(this.overscaledZ-t.overscaledZ<=0)return !1;if(0===t.overscaledZ)return this.overscaledZ>0;const e=this.canonical.z-t.canonical.z;return !(e<0)&&t.canonical.x===this.canonical.x>>e&&t.canonical.y===this.canonical.y>>e}children(t){if(this.overscaledZ>=t)return [new Jp(this.overscaledZ+1,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)];const e=this.canonical.z+1,n=2*this.canonical.x,r=2*this.canonical.y;return [new Jp(e,this.wrap,e,n,r),new Jp(e,this.wrap,e,n+1,r),new Jp(e,this.wrap,e,n,r+1),new Jp(e,this.wrap,e,n+1,r+1)]}isLessThan(t){return this.wrapt.wrap)&&(this.overscaledZt.overscaledZ)&&(this.canonical.xt.canonical.x)&&this.canonical.y=0&&t=0&&e=l)return null;let c=this.canonical.x+r,h=this.wrap;return c<0?(h-=Math.ceil(-c/l),c=(c%l+l)%l):c>=l&&(h+=Math.floor(c/l),c%=l),{tileID:new Jp(this.overscaledZ,h,a,c,u),x:s,y:o}}}function Qp(t,e,n,r,i){(t*=2)<0&&(t=-1*t-1);const s=1<this.maxX||this.minY>this.maxY)&&(this.minX=1/0,this.maxX=-1/0,this.minY=1/0,this.maxY=-1/0),this}shrinkBy(t){return this.expandBy(-t)}map(t){const e=new tf;return e.extend(t(new n(this.minX,this.minY))),e.extend(t(new n(this.maxX,this.minY))),e.extend(t(new n(this.minX,this.maxY))),e.extend(t(new n(this.maxX,this.maxY))),e}static fromPoints(t){const e=new tf;for(const n of t)e.extend(n);return e}contains(t){return t.x>=this.minX&&t.x<=this.maxX&&t.y>=this.minY&&t.y<=this.maxY}empty(){return this.minX>this.maxX}width(){return this.maxX-this.minX}height(){return this.maxY-this.minY}covers(t){return !this.empty()&&!t.empty()&&t.minX>=this.minX&&t.maxX<=this.maxX&&t.minY>=this.minY&&t.maxY<=this.maxY}intersects(t){return !this.empty()&&!t.empty()&&t.minX<=this.maxX&&t.maxX>=this.minX&&t.minY<=this.maxY&&t.maxY>=this.minY}}class ef{constructor(t){this._stringToNumber={},this._numberToString=[];for(let e=0;e=this._numberToString.length)throw new Error(`Out of bounds. Index requested n=${t} can't be >= this._numberToString.length ${this._numberToString.length}`);return this._numberToString[t]}}class nf{constructor(t,e,n,r,i){this.type="Feature",this._vectorTileFeature=t,this._x=n,this._y=r,this._z=e,this.properties=t.properties,this.id=i;}projectPoint(t,e,n,r){return [360*(t.x+e)/r-180,360/Math.PI*Math.atan(Math.exp((1-2*(t.y+n)/r)*Math.PI))-90]}projectLine(t,e,n,r){return t.map((t=>this.projectPoint(t,e,n,r)))}get geometry(){if(this._geometry)return this._geometry;const t=this._vectorTileFeature,e=t.extent*Math.pow(2,this._z),n=t.extent*this._x,r=t.extent*this._y,i=t.loadGeometry();switch(t.type){case 1:{const t=[];for(const e of i)t.push(e[0]);const s=this.projectLine(t,n,r,e);this._geometry=1===t.length?{type:"Point",coordinates:s[0]}:{type:"MultiPoint",coordinates:s};break}case 2:{const t=i.map((t=>this.projectLine(t,n,r,e)));this._geometry=1===t.length?{type:"LineString",coordinates:t[0]}:{type:"MultiLineString",coordinates:t};break}case 3:{const t=Lu(i),s=[];for(const i of t)s.push(i.map((t=>this.projectLine(t,n,r,e))));this._geometry=1===s.length?{type:"Polygon",coordinates:s[0]}:{type:"MultiPolygon",coordinates:s};break}default:throw new Error(`unknown feature type: ${t.type}`)}return this._geometry}set geometry(t){this._geometry=t;}toJSON(){const t={geometry:this.geometry};for(const e in this)"_geometry"!==e&&"_vectorTileFeature"!==e&&"_x"!==e&&"_y"!==e&&"_z"!==e&&(t[e]=this[e]);return t}}class rf{constructor(t,e,n){this._name=t,this.dataBuffer=e,"number"==typeof n?this._size=n:(this.nullabilityBuffer=n,this._size=n.size());}getValue(t){return this.nullabilityBuffer&&!this.nullabilityBuffer.get(t)?null:this.getValueFromBuffer(t)}has(t){return this.nullabilityBuffer?.get(t)||!this.nullabilityBuffer}get name(){return this._name}get size(){return this._size}}class sf extends rf{}class of extends sf{getValueFromBuffer(t){return this.dataBuffer[t]}}class af extends sf{getValueFromBuffer(t){return this.dataBuffer[t]}}class lf extends rf{constructor(t,e,n,r){super(t,e,r),this.delta=n;}}class uf extends lf{constructor(t,e,n,r){super(t,Int32Array.of(e),n,r);}getValueFromBuffer(t){return this.dataBuffer[0]+t*this.delta}}class cf extends rf{constructor(t,e,n,r){super(t,r?Int32Array.of(e):Uint32Array.of(e),n);}getValueFromBuffer(t){return this.dataBuffer[0]}}class hf{constructor(t,e,n,r,i=4096){this._name=t,this._geometryVector=e,this._idVector=n,this._propertyVectors=r,this._extent=i;}get name(){return this._name}get idVector(){return this._idVector}get geometryVector(){return this._geometryVector}get propertyVectors(){return this._propertyVectors}getPropertyVector(t){return this.propertyVectorsMap||(this.propertyVectorsMap=new Map(this._propertyVectors.map((t=>[t.name,t])))),this.propertyVectorsMap.get(t)}get numFeatures(){return this.geometryVector.numGeometries}get extent(){return this._extent}getFeatures(){const t=[],e=this.geometryVector.getGeometries();for(let n=0;n>>32-t;const mf=yf,gf=256;function xf(t,e){return t-t%e}function vf(t){const e=t>>>0;return ((255&e)<<24|(65280&e)<<8|e>>>8&65280|e>>>24&255)>>>0}const bf=function(){if(!Number.isFinite(65536))return 65536;const t=xf(Math.floor(65536),gf);return 0===t?gf:t}(),wf=3*bf/gf+bf|0;function _f(){const t=new Uint8Array(wf);return {dataToBePacked:new Array(33),dataPointers:new Int32Array(33),byteContainer:t,byteContainerI32:new Int32Array(t.buffer,t.byteOffset,t.byteLength>>>2),exceptionSizes:new Int32Array(33)}}function Sf(t,e,n,r,i){switch(i){case 1:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0;n[i++]=e>>>0&1,n[i++]=e>>>1&1,n[i++]=e>>>2&1,n[i++]=e>>>3&1,n[i++]=e>>>4&1,n[i++]=e>>>5&1,n[i++]=e>>>6&1,n[i++]=e>>>7&1,n[i++]=e>>>8&1,n[i++]=e>>>9&1,n[i++]=e>>>10&1,n[i++]=e>>>11&1,n[i++]=e>>>12&1,n[i++]=e>>>13&1,n[i++]=e>>>14&1,n[i++]=e>>>15&1,n[i++]=e>>>16&1,n[i++]=e>>>17&1,n[i++]=e>>>18&1,n[i++]=e>>>19&1,n[i++]=e>>>20&1,n[i++]=e>>>21&1,n[i++]=e>>>22&1,n[i++]=e>>>23&1,n[i++]=e>>>24&1,n[i++]=e>>>25&1,n[i++]=e>>>26&1,n[i++]=e>>>27&1,n[i++]=e>>>28&1,n[i++]=e>>>29&1,n[i++]=e>>>30&1,n[i++]=e>>>31&1;}}(t,e,n,r);break;case 2:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0;n[i++]=e>>>0&3,n[i++]=e>>>2&3,n[i++]=e>>>4&3,n[i++]=e>>>6&3,n[i++]=e>>>8&3,n[i++]=e>>>10&3,n[i++]=e>>>12&3,n[i++]=e>>>14&3,n[i++]=e>>>16&3,n[i++]=e>>>18&3,n[i++]=e>>>20&3,n[i++]=e>>>22&3,n[i++]=e>>>24&3,n[i++]=e>>>26&3,n[i++]=e>>>28&3,n[i++]=e>>>30&3,n[i++]=r>>>0&3,n[i++]=r>>>2&3,n[i++]=r>>>4&3,n[i++]=r>>>6&3,n[i++]=r>>>8&3,n[i++]=r>>>10&3,n[i++]=r>>>12&3,n[i++]=r>>>14&3,n[i++]=r>>>16&3,n[i++]=r>>>18&3,n[i++]=r>>>20&3,n[i++]=r>>>22&3,n[i++]=r>>>24&3,n[i++]=r>>>26&3,n[i++]=r>>>28&3,n[i++]=r>>>30&3;}}(t,e,n,r);break;case 3:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0,o=t[s++]>>>0;n[i++]=e>>>0&7,n[i++]=e>>>3&7,n[i++]=e>>>6&7,n[i++]=e>>>9&7,n[i++]=e>>>12&7,n[i++]=e>>>15&7,n[i++]=e>>>18&7,n[i++]=e>>>21&7,n[i++]=e>>>24&7,n[i++]=e>>>27&7,n[i++]=7&(e>>>30|(1&r)<<2),n[i++]=r>>>1&7,n[i++]=r>>>4&7,n[i++]=r>>>7&7,n[i++]=r>>>10&7,n[i++]=r>>>13&7,n[i++]=r>>>16&7,n[i++]=r>>>19&7,n[i++]=r>>>22&7,n[i++]=r>>>25&7,n[i++]=r>>>28&7,n[i++]=7&(r>>>31|(3&o)<<1),n[i++]=o>>>2&7,n[i++]=o>>>5&7,n[i++]=o>>>8&7,n[i++]=o>>>11&7,n[i++]=o>>>14&7,n[i++]=o>>>17&7,n[i++]=o>>>20&7,n[i++]=o>>>23&7,n[i++]=o>>>26&7,n[i++]=o>>>29&7;}}(t,e,n,r);break;case 4:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0,o=t[s++]>>>0,a=t[s++]>>>0;n[i++]=e>>>0&15,n[i++]=e>>>4&15,n[i++]=e>>>8&15,n[i++]=e>>>12&15,n[i++]=e>>>16&15,n[i++]=e>>>20&15,n[i++]=e>>>24&15,n[i++]=e>>>28&15,n[i++]=r>>>0&15,n[i++]=r>>>4&15,n[i++]=r>>>8&15,n[i++]=r>>>12&15,n[i++]=r>>>16&15,n[i++]=r>>>20&15,n[i++]=r>>>24&15,n[i++]=r>>>28&15,n[i++]=o>>>0&15,n[i++]=o>>>4&15,n[i++]=o>>>8&15,n[i++]=o>>>12&15,n[i++]=o>>>16&15,n[i++]=o>>>20&15,n[i++]=o>>>24&15,n[i++]=o>>>28&15,n[i++]=a>>>0&15,n[i++]=a>>>4&15,n[i++]=a>>>8&15,n[i++]=a>>>12&15,n[i++]=a>>>16&15,n[i++]=a>>>20&15,n[i++]=a>>>24&15,n[i++]=a>>>28&15;}}(t,e,n,r);break;case 5:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0,o=t[s++]>>>0,a=t[s++]>>>0,l=t[s++]>>>0;n[i++]=e>>>0&31,n[i++]=e>>>5&31,n[i++]=e>>>10&31,n[i++]=e>>>15&31,n[i++]=e>>>20&31,n[i++]=e>>>25&31,n[i++]=31&(e>>>30|(7&r)<<2),n[i++]=r>>>3&31,n[i++]=r>>>8&31,n[i++]=r>>>13&31,n[i++]=r>>>18&31,n[i++]=r>>>23&31,n[i++]=31&(r>>>28|(1&o)<<4),n[i++]=o>>>1&31,n[i++]=o>>>6&31,n[i++]=o>>>11&31,n[i++]=o>>>16&31,n[i++]=o>>>21&31,n[i++]=o>>>26&31,n[i++]=31&(o>>>31|(15&a)<<1),n[i++]=a>>>4&31,n[i++]=a>>>9&31,n[i++]=a>>>14&31,n[i++]=a>>>19&31,n[i++]=a>>>24&31,n[i++]=31&(a>>>29|(3&l)<<3),n[i++]=l>>>2&31,n[i++]=l>>>7&31,n[i++]=l>>>12&31,n[i++]=l>>>17&31,n[i++]=l>>>22&31,n[i++]=l>>>27&31;}}(t,e,n,r);break;case 6:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0,o=t[s++]>>>0,a=t[s++]>>>0,l=t[s++]>>>0,u=t[s++]>>>0;n[i++]=e>>>0&63,n[i++]=e>>>6&63,n[i++]=e>>>12&63,n[i++]=e>>>18&63,n[i++]=e>>>24&63,n[i++]=63&(e>>>30|(15&r)<<2),n[i++]=r>>>4&63,n[i++]=r>>>10&63,n[i++]=r>>>16&63,n[i++]=r>>>22&63,n[i++]=63&(r>>>28|(3&o)<<4),n[i++]=o>>>2&63,n[i++]=o>>>8&63,n[i++]=o>>>14&63,n[i++]=o>>>20&63,n[i++]=o>>>26&63,n[i++]=a>>>0&63,n[i++]=a>>>6&63,n[i++]=a>>>12&63,n[i++]=a>>>18&63,n[i++]=a>>>24&63,n[i++]=63&(a>>>30|(15&l)<<2),n[i++]=l>>>4&63,n[i++]=l>>>10&63,n[i++]=l>>>16&63,n[i++]=l>>>22&63,n[i++]=63&(l>>>28|(3&u)<<4),n[i++]=u>>>2&63,n[i++]=u>>>8&63,n[i++]=u>>>14&63,n[i++]=u>>>20&63,n[i++]=u>>>26&63;}}(t,e,n,r);break;case 7:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0,o=t[s++]>>>0,a=t[s++]>>>0,l=t[s++]>>>0,u=t[s++]>>>0,c=t[s++]>>>0;n[i++]=e>>>0&127,n[i++]=e>>>7&127,n[i++]=e>>>14&127,n[i++]=e>>>21&127,n[i++]=127&(e>>>28|(7&r)<<4),n[i++]=r>>>3&127,n[i++]=r>>>10&127,n[i++]=r>>>17&127,n[i++]=r>>>24&127,n[i++]=127&(r>>>31|(63&o)<<1),n[i++]=o>>>6&127,n[i++]=o>>>13&127,n[i++]=o>>>20&127,n[i++]=127&(o>>>27|(3&a)<<5),n[i++]=a>>>2&127,n[i++]=a>>>9&127,n[i++]=a>>>16&127,n[i++]=a>>>23&127,n[i++]=127&(a>>>30|(31&l)<<2),n[i++]=l>>>5&127,n[i++]=l>>>12&127,n[i++]=l>>>19&127,n[i++]=127&(l>>>26|(1&u)<<6),n[i++]=u>>>1&127,n[i++]=u>>>8&127,n[i++]=u>>>15&127,n[i++]=u>>>22&127,n[i++]=127&(u>>>29|(15&c)<<3),n[i++]=c>>>4&127,n[i++]=c>>>11&127,n[i++]=c>>>18&127,n[i++]=c>>>25&127;}}(t,e,n,r);break;case 8:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<8;e++){const e=t[s++]>>>0,r=t[s++]>>>0,o=t[s++]>>>0,a=t[s++]>>>0,l=t[s++]>>>0,u=t[s++]>>>0,c=t[s++]>>>0,h=t[s++]>>>0;n[i++]=e>>>0&255,n[i++]=e>>>8&255,n[i++]=e>>>16&255,n[i++]=e>>>24&255,n[i++]=r>>>0&255,n[i++]=r>>>8&255,n[i++]=r>>>16&255,n[i++]=r>>>24&255,n[i++]=o>>>0&255,n[i++]=o>>>8&255,n[i++]=o>>>16&255,n[i++]=o>>>24&255,n[i++]=a>>>0&255,n[i++]=a>>>8&255,n[i++]=a>>>16&255,n[i++]=a>>>24&255,n[i++]=l>>>0&255,n[i++]=l>>>8&255,n[i++]=l>>>16&255,n[i++]=l>>>24&255,n[i++]=u>>>0&255,n[i++]=u>>>8&255,n[i++]=u>>>16&255,n[i++]=u>>>24&255,n[i++]=c>>>0&255,n[i++]=c>>>8&255,n[i++]=c>>>16&255,n[i++]=c>>>24&255,n[i++]=h>>>0&255,n[i++]=h>>>8&255,n[i++]=h>>>16&255,n[i++]=h>>>24&255;}}(t,e,n,r);break;case 16:!function(t,e,n,r){let i=r,s=e;for(let e=0;e<128;e++){const e=t[s++]>>>0;n[i++]=65535&e,n[i++]=e>>>16&65535;}}(t,e,n,r);break;default:!function(t,e,n,r,i){const s=mf[i]>>>0;let o=e,a=0,l=t[o]>>>0,u=r;for(let e=0;e<8;e++){for(let e=0;e<32;e++)if(a+i<=32)n[u+e]=l>>>a&s,a+=i,32===a&&(a=0,o++,31!==e&&(l=t[o]>>>0));else {const r=32-a,c=l>>>a;o++,l=t[o]>>>0;const h=i-r;n[u+e]=(c|(l&-1>>>32-h>>>0)<>>0);}}(t,e,n,r,i);}return e+(i<<3)|0}function Af(t,e,n,r){if(n+2>e)throw new Error(`FastPFOR decode: byteContainer underflow at block=${r} (need 2 bytes for [bitWidth, exceptionCount], bytePos=${n}, byteSize=${e})`);const i=t[n++],s=t[n++];if(i>32)throw new Error(`FastPFOR decode: invalid bitWidth=${i} at block=${r} (expected 0..32). This likely indicates corrupted or truncated input.`);return {bitWidth:i,exceptionCount:s,bytePosIn:n}}function Mf(t,e,n,r,i,s,o,a,l){const{maxBits:u,exceptionBitWidth:c,bytePosIn:h}=function(t,e,n,r,i,s){if(n+1>e)throw new Error(`FastPFOR decode: exception header underflow at block=${s} (need 1 byte for maxBits, bytePos=${n}, byteSize=${e})`);const o=t[n++];if(o32)throw new Error(`FastPFOR decode: invalid maxBits=${o} at block=${s} (bitWidth=${r}, expected ${r}..32)`);const a=o-r|0;if(a<1||a>32)throw new Error(`FastPFOR decode: invalid exceptionBitWidth=${a} at block=${s} (bitWidth=${r}, maxBits=${o})`);if(n+i>e)throw new Error(`FastPFOR decode: exception positions underflow at block=${s} (need=${i}, have=${e-n})`);return {maxBits:o,exceptionBitWidth:a,bytePosIn:n}}(i,s,o,n,r,l);if(o=h,1===c){const s=1<y)throw new Error(`FastPFOR decode: exception stream overflow for exceptionBitWidth=${c} (ptr=${d}, need ${r}, size=${y}) at block ${l}`);for(let s=0;st.length-1)throw new Error(`FastPFOR decode: invalid whereMeta=${a} at pageStart=${o} (expected > 0 and pageStart+whereMeta < encoded.length=${t.length})`);const l=o+1|0,u=o+a|0,c=t[u]>>>0,h=c+3>>>2,p=u+1,f=p+h;if(f>=t.length)throw new Error(`FastPFOR decode: invalid byteSize=${c} (metaInts=${h}, pageStart=${o}, packedEnd=${u}, byteContainerStart=${p}) causes bitmapPos=${f} out of bounds (encoded.length=${t.length})`);const d=function(t,e,n,r){r.byteContainer.length>>2;if(3&i.byteOffset)for(let n=0;n>>8&255,i[s+2|0]=r>>>16&255,i[s+3|0]=r>>>24&255;}else {let n=r.byteContainerI32;(!n||n.buffer!==i.buffer||n.byteOffset!==i.byteOffset||n.length>>2)),n.set(t.subarray(e,e+s));}const o=3&n;if(o>0){const n=0|t[e+s|0],r=s<<2;for(let t=0;t>>(t<<3)&255;}return i}(t,p,c,s),y=c,m=function(t,e,n){const r=0|t[e++],i=n.dataToBePacked;for(let s=2;s<=32;s=s+1|0){if(!(r>>>s-1&1))continue;if(e>=t.length)throw new Error(`FastPFOR decode: truncated exception stream header (bitWidth=${s}, streamWordIndex=${e}, needWords=1, availableWords=${t.length-e}, encodedWords=${t.length})`);const o=t[e++]>>>0,a=xf(o+31,32),l=o*s+31>>>5;if(e+l>t.length)throw new Error(`FastPFOR decode: truncated exception stream (bitWidth=${s}, size=${o}, streamWordIndex=${e}, needWords=${l}, availableWords=${t.length-e}, encodedWords=${t.length})`);let u=i[s];(!u||u.length>>5)|0,n.exceptionSizes[s]=o;}return e}(t,f,s);return s.dataPointers.fill(0),function(t,e,n,r,i,s,o,a,l,u){let c=0|n,h=0;for(let e=0;e0&&(h=Mf(i,p,r,o,a,l,h,u,e));}if(c!==r)throw new Error(`FastPFOR decode: packed region mismatch (pageStart=${e}, packedStart=${n}, consumedPackedEnd=${c}, expectedPackedEnd=${r}, packedWords=${r-n}, encoded.length=${t.length})`)}(t,o,l,u,e,0|r,i/gf|0,d,y,s),m}function If(t,e,n,r,i){switch(i){case 2:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0;n[i++]=s>>>0&3,n[i++]=s>>>2&3,n[i++]=s>>>4&3,n[i++]=s>>>6&3,n[i++]=s>>>8&3,n[i++]=s>>>10&3,n[i++]=s>>>12&3,n[i++]=s>>>14&3,n[i++]=s>>>16&3,n[i++]=s>>>18&3,n[i++]=s>>>20&3,n[i++]=s>>>22&3,n[i++]=s>>>24&3,n[i++]=s>>>26&3,n[i++]=s>>>28&3,n[i++]=s>>>30&3,n[i++]=o>>>0&3,n[i++]=o>>>2&3,n[i++]=o>>>4&3,n[i++]=o>>>6&3,n[i++]=o>>>8&3,n[i++]=o>>>10&3,n[i++]=o>>>12&3,n[i++]=o>>>14&3,n[i++]=o>>>16&3,n[i++]=o>>>18&3,n[i++]=o>>>20&3,n[i++]=o>>>22&3,n[i++]=o>>>24&3,n[i++]=o>>>26&3,n[i++]=o>>>28&3,n[i]=o>>>30&3;}(t,e,n,r);case 3:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0;n[i++]=s>>>0&7,n[i++]=s>>>3&7,n[i++]=s>>>6&7,n[i++]=s>>>9&7,n[i++]=s>>>12&7,n[i++]=s>>>15&7,n[i++]=s>>>18&7,n[i++]=s>>>21&7,n[i++]=s>>>24&7,n[i++]=s>>>27&7,n[i++]=7&(s>>>30|(1&o)<<2),n[i++]=o>>>1&7,n[i++]=o>>>4&7,n[i++]=o>>>7&7,n[i++]=o>>>10&7,n[i++]=o>>>13&7,n[i++]=o>>>16&7,n[i++]=o>>>19&7,n[i++]=o>>>22&7,n[i++]=o>>>25&7,n[i++]=o>>>28&7,n[i++]=7&(o>>>31|(3&a)<<1),n[i++]=a>>>2&7,n[i++]=a>>>5&7,n[i++]=a>>>8&7,n[i++]=a>>>11&7,n[i++]=a>>>14&7,n[i++]=a>>>17&7,n[i++]=a>>>20&7,n[i++]=a>>>23&7,n[i++]=a>>>26&7,n[i]=a>>>29&7;}(t,e,n,r);case 4:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0;n[i++]=s>>>0&15,n[i++]=s>>>4&15,n[i++]=s>>>8&15,n[i++]=s>>>12&15,n[i++]=s>>>16&15,n[i++]=s>>>20&15,n[i++]=s>>>24&15,n[i++]=s>>>28&15,n[i++]=o>>>0&15,n[i++]=o>>>4&15,n[i++]=o>>>8&15,n[i++]=o>>>12&15,n[i++]=o>>>16&15,n[i++]=o>>>20&15,n[i++]=o>>>24&15,n[i++]=o>>>28&15,n[i++]=a>>>0&15,n[i++]=a>>>4&15,n[i++]=a>>>8&15,n[i++]=a>>>12&15,n[i++]=a>>>16&15,n[i++]=a>>>20&15,n[i++]=a>>>24&15,n[i++]=a>>>28&15,n[i++]=l>>>0&15,n[i++]=l>>>4&15,n[i++]=l>>>8&15,n[i++]=l>>>12&15,n[i++]=l>>>16&15,n[i++]=l>>>20&15,n[i++]=l>>>24&15,n[i]=l>>>28&15;}(t,e,n,r);case 5:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0;n[i++]=s>>>0&31,n[i++]=s>>>5&31,n[i++]=s>>>10&31,n[i++]=s>>>15&31,n[i++]=s>>>20&31,n[i++]=s>>>25&31,n[i++]=31&(s>>>30|(7&o)<<2),n[i++]=o>>>3&31,n[i++]=o>>>8&31,n[i++]=o>>>13&31,n[i++]=o>>>18&31,n[i++]=o>>>23&31,n[i++]=31&(o>>>28|(1&a)<<4),n[i++]=a>>>1&31,n[i++]=a>>>6&31,n[i++]=a>>>11&31,n[i++]=a>>>16&31,n[i++]=a>>>21&31,n[i++]=a>>>26&31,n[i++]=31&(a>>>31|(15&l)<<1),n[i++]=l>>>4&31,n[i++]=l>>>9&31,n[i++]=l>>>14&31,n[i++]=l>>>19&31,n[i++]=l>>>24&31,n[i++]=31&(l>>>29|(3&u)<<3),n[i++]=u>>>2&31,n[i++]=u>>>7&31,n[i++]=u>>>12&31,n[i++]=u>>>17&31,n[i++]=u>>>22&31,n[i]=u>>>27&31;}(t,e,n,r);case 6:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0;n[i++]=s>>>0&63,n[i++]=s>>>6&63,n[i++]=s>>>12&63,n[i++]=s>>>18&63,n[i++]=s>>>24&63,n[i++]=63&(s>>>30|(15&o)<<2),n[i++]=o>>>4&63,n[i++]=o>>>10&63,n[i++]=o>>>16&63,n[i++]=o>>>22&63,n[i++]=63&(o>>>28|(3&a)<<4),n[i++]=a>>>2&63,n[i++]=a>>>8&63,n[i++]=a>>>14&63,n[i++]=a>>>20&63,n[i++]=a>>>26&63,n[i++]=l>>>0&63,n[i++]=l>>>6&63,n[i++]=l>>>12&63,n[i++]=l>>>18&63,n[i++]=l>>>24&63,n[i++]=63&(l>>>30|(15&u)<<2),n[i++]=u>>>4&63,n[i++]=u>>>10&63,n[i++]=u>>>16&63,n[i++]=u>>>22&63,n[i++]=63&(u>>>28|(3&c)<<4),n[i++]=c>>>2&63,n[i++]=c>>>8&63,n[i++]=c>>>14&63,n[i++]=c>>>20&63,n[i]=c>>>26&63;}(t,e,n,r);case 7:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0;n[i++]=s>>>0&127,n[i++]=s>>>7&127,n[i++]=s>>>14&127,n[i++]=s>>>21&127,n[i++]=127&(s>>>28|(7&o)<<4),n[i++]=o>>>3&127,n[i++]=o>>>10&127,n[i++]=o>>>17&127,n[i++]=o>>>24&127,n[i++]=127&(o>>>31|(63&a)<<1),n[i++]=a>>>6&127,n[i++]=a>>>13&127,n[i++]=a>>>20&127,n[i++]=127&(a>>>27|(3&l)<<5),n[i++]=l>>>2&127,n[i++]=l>>>9&127,n[i++]=l>>>16&127,n[i++]=l>>>23&127,n[i++]=127&(l>>>30|(31&u)<<2),n[i++]=u>>>5&127,n[i++]=u>>>12&127,n[i++]=u>>>19&127,n[i++]=127&(u>>>26|(1&c)<<6),n[i++]=c>>>1&127,n[i++]=c>>>8&127,n[i++]=c>>>15&127,n[i++]=c>>>22&127,n[i++]=127&(c>>>29|(15&h)<<3),n[i++]=h>>>4&127,n[i++]=h>>>11&127,n[i++]=h>>>18&127,n[i]=h>>>25&127;}(t,e,n,r);case 8:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0,p=t[e+7]>>>0;n[i++]=s>>>0&255,n[i++]=s>>>8&255,n[i++]=s>>>16&255,n[i++]=s>>>24&255,n[i++]=o>>>0&255,n[i++]=o>>>8&255,n[i++]=o>>>16&255,n[i++]=o>>>24&255,n[i++]=a>>>0&255,n[i++]=a>>>8&255,n[i++]=a>>>16&255,n[i++]=a>>>24&255,n[i++]=l>>>0&255,n[i++]=l>>>8&255,n[i++]=l>>>16&255,n[i++]=l>>>24&255,n[i++]=u>>>0&255,n[i++]=u>>>8&255,n[i++]=u>>>16&255,n[i++]=u>>>24&255,n[i++]=c>>>0&255,n[i++]=c>>>8&255,n[i++]=c>>>16&255,n[i++]=c>>>24&255,n[i++]=h>>>0&255,n[i++]=h>>>8&255,n[i++]=h>>>16&255,n[i++]=h>>>24&255,n[i++]=p>>>0&255,n[i++]=p>>>8&255,n[i++]=p>>>16&255,n[i]=p>>>24&255;}(t,e,n,r);case 9:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0,p=t[e+7]>>>0,f=t[e+8]>>>0;n[i++]=s>>>0&511,n[i++]=s>>>9&511,n[i++]=s>>>18&511,n[i++]=511&(s>>>27|(15&o)<<5),n[i++]=o>>>4&511,n[i++]=o>>>13&511,n[i++]=o>>>22&511,n[i++]=511&(o>>>31|(255&a)<<1),n[i++]=a>>>8&511,n[i++]=a>>>17&511,n[i++]=511&(a>>>26|(7&l)<<6),n[i++]=l>>>3&511,n[i++]=l>>>12&511,n[i++]=l>>>21&511,n[i++]=511&(l>>>30|(127&u)<<2),n[i++]=u>>>7&511,n[i++]=u>>>16&511,n[i++]=511&(u>>>25|(3&c)<<7),n[i++]=c>>>2&511,n[i++]=c>>>11&511,n[i++]=c>>>20&511,n[i++]=511&(c>>>29|(63&h)<<3),n[i++]=h>>>6&511,n[i++]=h>>>15&511,n[i++]=511&(h>>>24|(1&p)<<8),n[i++]=p>>>1&511,n[i++]=p>>>10&511,n[i++]=p>>>19&511,n[i++]=511&(p>>>28|(31&f)<<4),n[i++]=f>>>5&511,n[i++]=f>>>14&511,n[i]=f>>>23&511;}(t,e,n,r);case 10:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0,p=t[e+7]>>>0,f=t[e+8]>>>0,d=t[e+9]>>>0;n[i++]=s>>>0&1023,n[i++]=s>>>10&1023,n[i++]=s>>>20&1023,n[i++]=1023&(s>>>30|(255&o)<<2),n[i++]=o>>>8&1023,n[i++]=o>>>18&1023,n[i++]=1023&(o>>>28|(63&a)<<4),n[i++]=a>>>6&1023,n[i++]=a>>>16&1023,n[i++]=1023&(a>>>26|(15&l)<<6),n[i++]=l>>>4&1023,n[i++]=l>>>14&1023,n[i++]=1023&(l>>>24|(3&u)<<8),n[i++]=u>>>2&1023,n[i++]=u>>>12&1023,n[i++]=u>>>22&1023,n[i++]=c>>>0&1023,n[i++]=c>>>10&1023,n[i++]=c>>>20&1023,n[i++]=1023&(c>>>30|(255&h)<<2),n[i++]=h>>>8&1023,n[i++]=h>>>18&1023,n[i++]=1023&(h>>>28|(63&p)<<4),n[i++]=p>>>6&1023,n[i++]=p>>>16&1023,n[i++]=1023&(p>>>26|(15&f)<<6),n[i++]=f>>>4&1023,n[i++]=f>>>14&1023,n[i++]=1023&(f>>>24|(3&d)<<8),n[i++]=d>>>2&1023,n[i++]=d>>>12&1023,n[i]=d>>>22&1023;}(t,e,n,r);case 11:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0,p=t[e+7]>>>0,f=t[e+8]>>>0,d=t[e+9]>>>0,y=t[e+10]>>>0;n[i++]=s>>>0&2047,n[i++]=s>>>11&2047,n[i++]=2047&(s>>>22|(1&o)<<10),n[i++]=o>>>1&2047,n[i++]=o>>>12&2047,n[i++]=2047&(o>>>23|(3&a)<<9),n[i++]=a>>>2&2047,n[i++]=a>>>13&2047,n[i++]=2047&(a>>>24|(7&l)<<8),n[i++]=l>>>3&2047,n[i++]=l>>>14&2047,n[i++]=2047&(l>>>25|(15&u)<<7),n[i++]=u>>>4&2047,n[i++]=u>>>15&2047,n[i++]=2047&(u>>>26|(31&c)<<6),n[i++]=c>>>5&2047,n[i++]=c>>>16&2047,n[i++]=2047&(c>>>27|(63&h)<<5),n[i++]=h>>>6&2047,n[i++]=h>>>17&2047,n[i++]=2047&(h>>>28|(127&p)<<4),n[i++]=p>>>7&2047,n[i++]=p>>>18&2047,n[i++]=2047&(p>>>29|(255&f)<<3),n[i++]=f>>>8&2047,n[i++]=f>>>19&2047,n[i++]=2047&(f>>>30|(511&d)<<2),n[i++]=d>>>9&2047,n[i++]=d>>>20&2047,n[i++]=2047&(d>>>31|(1023&y)<<1),n[i++]=y>>>10&2047,n[i]=y>>>21&2047;}(t,e,n,r);case 12:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0,p=t[e+7]>>>0,f=t[e+8]>>>0,d=t[e+9]>>>0,y=t[e+10]>>>0,m=t[e+11]>>>0;n[i++]=s>>>0&4095,n[i++]=s>>>12&4095,n[i++]=4095&(s>>>24|(15&o)<<8),n[i++]=o>>>4&4095,n[i++]=o>>>16&4095,n[i++]=4095&(o>>>28|(255&a)<<4),n[i++]=a>>>8&4095,n[i++]=a>>>20&4095,n[i++]=l>>>0&4095,n[i++]=l>>>12&4095,n[i++]=4095&(l>>>24|(15&u)<<8),n[i++]=u>>>4&4095,n[i++]=u>>>16&4095,n[i++]=4095&(u>>>28|(255&c)<<4),n[i++]=c>>>8&4095,n[i++]=c>>>20&4095,n[i++]=h>>>0&4095,n[i++]=h>>>12&4095,n[i++]=4095&(h>>>24|(15&p)<<8),n[i++]=p>>>4&4095,n[i++]=p>>>16&4095,n[i++]=4095&(p>>>28|(255&f)<<4),n[i++]=f>>>8&4095,n[i++]=f>>>20&4095,n[i++]=d>>>0&4095,n[i++]=d>>>12&4095,n[i++]=4095&(d>>>24|(15&y)<<8),n[i++]=y>>>4&4095,n[i++]=y>>>16&4095,n[i++]=4095&(y>>>28|(255&m)<<4),n[i++]=m>>>8&4095,n[i]=m>>>20&4095;}(t,e,n,r);case 16:return void function(t,e,n,r){let i=r;const s=t[e]>>>0,o=t[e+1]>>>0,a=t[e+2]>>>0,l=t[e+3]>>>0,u=t[e+4]>>>0,c=t[e+5]>>>0,h=t[e+6]>>>0,p=t[e+7]>>>0,f=t[e+8]>>>0,d=t[e+9]>>>0,y=t[e+10]>>>0,m=t[e+11]>>>0,g=t[e+12]>>>0,x=t[e+13]>>>0,v=t[e+14]>>>0,b=t[e+15]>>>0;n[i++]=s>>>0&65535,n[i++]=s>>>16&65535,n[i++]=o>>>0&65535,n[i++]=o>>>16&65535,n[i++]=a>>>0&65535,n[i++]=a>>>16&65535,n[i++]=l>>>0&65535,n[i++]=l>>>16&65535,n[i++]=u>>>0&65535,n[i++]=u>>>16&65535,n[i++]=c>>>0&65535,n[i++]=c>>>16&65535,n[i++]=h>>>0&65535,n[i++]=h>>>16&65535,n[i++]=p>>>0&65535,n[i++]=p>>>16&65535,n[i++]=f>>>0&65535,n[i++]=f>>>16&65535,n[i++]=d>>>0&65535,n[i++]=d>>>16&65535,n[i++]=y>>>0&65535,n[i++]=y>>>16&65535,n[i++]=m>>>0&65535,n[i++]=m>>>16&65535,n[i++]=g>>>0&65535,n[i++]=g>>>16&65535,n[i++]=x>>>0&65535,n[i++]=x>>>16&65535,n[i++]=v>>>0&65535,n[i++]=v>>>16&65535,n[i++]=b>>>0&65535,n[i]=b>>>16&65535;}(t,e,n,r);case 32:for(let i=0;i<32;i=i+1|0)n[r+i|0]=0|t[e+i|0];return}const s=mf[i]>>>0;let o=e,a=0,l=t[o]>>>0;for(let e=0;e<32;e++)if(a+i<=32)n[r+e]=l>>>a&s,a+=i,32===a&&(a=0,o++,31!==e&&(l=t[o]>>>0));else {const u=32-a,c=l>>>a;o++,l=t[o]>>>0,n[r+e]=(c|(l&mf[i-u]>>>0)<=64)throw new Error("Varint too long")}return e.set(i),n}function Pf(t,e){let n,r;return r=t[e.get()],e.increment(),n=127&r,r<128?n:(r=t[e.get()],e.increment(),n|=(127&r)<<7,r<128?n:(r=t[e.get()],e.increment(),n|=(127&r)<<14,r<128?n:(r=t[e.get()],e.increment(),n|=(127&r)<<21,r<128?n:(r=t[e.get()],n|=(15&r)<<28,function(t,e,n){let r,i;if(i=e[n.get()],n.increment(),r=(112&i)>>4,i<128)return 4294967296*r+(t>>>0);if(i=e[n.get()],n.increment(),r|=(127&i)<<3,i<128)return 4294967296*r+(t>>>0);if(i=e[n.get()],n.increment(),r|=(127&i)<<10,i<128)return 4294967296*r+(t>>>0);if(i=e[n.get()],n.increment(),r|=(127&i)<<17,i<128)return 4294967296*r+(t>>>0);if(i=e[n.get()],n.increment(),r|=(127&i)<<24,i<128)return 4294967296*r+(t>>>0);if(i=e[n.get()],n.increment(),r|=(1&i)<<31,i<128)return 4294967296*r+(t>>>0);throw new Error("Expected varint not more than 10 bytes")}(n,t,e)))))}function Df(t){return t>>>1^-(1&t)}function zf(t){return t>>1n^-(1n&t)}function Bf(t){return t%2==1?(t+1)/-2:t/2}function Cf(t,e,n){if(void 0===n){n=0;for(let r=0;r=4)for(;r=4)for(;r=4)for(let r=t[0];n>4];let i=null;switch(r){case Nf.DATA:i={dictionaryType:Object.values(Uf)[15&n]};break;case Nf.OFFSET:i={offsetType:Object.values(jf)[15&n]};break;case Nf.LENGTH:i={lengthType:Object.values(qf)[15&n]};}e.increment();const s=t[e.get()],o=Object.values(ff)[s>>5],a=Object.values(ff)[s>>2&7],l=Object.values(df)[3&s];e.increment();const u=Ef(t,e,2),c=u[0];return {physicalStreamType:r,logicalStreamType:i,logicalLevelTechnique1:o,logicalLevelTechnique2:a,physicalLevelTechnique:l,numValues:c,byteLength:u[1],decompressedCount:c}}(t,e);return n.logicalLevelTechnique1===ff.MORTON?function(t,e,n){const r=Ef(e,n,2);return {physicalStreamType:t.physicalStreamType,logicalStreamType:t.logicalStreamType,logicalLevelTechnique1:t.logicalLevelTechnique1,logicalLevelTechnique2:t.logicalLevelTechnique2,physicalLevelTechnique:t.physicalLevelTechnique,numValues:t.numValues,byteLength:t.byteLength,decompressedCount:t.decompressedCount,numBits:r[0],coordinateShift:r[1]}}(n,t,e):ff.RLE!==n.logicalLevelTechnique1&&ff.RLE!==n.logicalLevelTechnique2||df.NONE===n.physicalLevelTechnique?n:function(t,e,n){const r=Ef(e,n,2);return {physicalStreamType:t.physicalStreamType,logicalStreamType:t.logicalStreamType,logicalLevelTechnique1:t.logicalLevelTechnique1,logicalLevelTechnique2:t.logicalLevelTechnique2,physicalLevelTechnique:t.physicalLevelTechnique,numValues:t.numValues,byteLength:t.byteLength,decompressedCount:r[1],runs:r[0],numRleValues:r[1]}}(n,t,e)}!function(t){t.PRESENT="PRESENT",t.DATA="DATA",t.OFFSET="OFFSET",t.LENGTH="LENGTH";}(Nf||(Nf={})),function(t){t.NONE="NONE",t.SINGLE="SINGLE",t.SHARED="SHARED",t.VERTEX="VERTEX",t.MORTON="MORTON",t.FSST="FSST";}(Uf||(Uf={})),function(t){t.VERTEX="VERTEX",t.INDEX="INDEX",t.STRING="STRING",t.KEY="KEY";}(jf||(jf={})),function(t){t.VAR_BINARY="VAR_BINARY",t.GEOMETRIES="GEOMETRIES",t.PARTS="PARTS",t.RINGS="RINGS",t.TRIANGLES="TRIANGLES",t.SYMBOL="SYMBOL",t.DICTIONARY="DICTIONARY";}(qf||(qf={})),function(t){t[t.FLAT=0]="FLAT",t[t.CONST=1]="CONST",t[t.SEQUENCE=2]="SEQUENCE",t[t.DICTIONARY=3]="DICTIONARY",t[t.FSST_DICTIONARY=4]="FSST_DICTIONARY";}(Gf||(Gf={}));class Wf{constructor(t,e){this.values=t,this._size=e;}get(t){const e=Math.floor(t/8);return 1==(this.values[e]>>t%8&1)}set(t,e){const n=Math.floor(t/8);this.values[n]=this.values[n]|(e?1:0)<>t%8&1}size(){return this._size}getBuffer(){return this.values}}function Kf(t,e,n){if(!e)return t;const r=e.size(),i=new(0,t.constructor)(r);let s=0;for(let o=0;o=4)for(;r>>0;for(let n=1;n>>0;return e}(e.logicalLevelTechnique2===ff.RLE?Cf(t,e.runs,e.numRleValues):t);break;case ff.RLE:i=Cf(t,e.runs,e.numRleValues);break;case ff.MORTON:Rf(t),i=t;break;case ff.COMPONENTWISE_DELTA:i=function(t){if(t.length<2)return new Uint32Array(t);const e=new Uint32Array(t.length);e[0]=Df(t[0])>>>0,e[1]=Df(t[1])>>>0;for(let n=2;n>>0,e[n+1]=e[n-1]+Df(t[n+1])>>>0;return e}(t);break;case ff.NONE:i=t;break;default:throw new Error(`The specified Logical level technique is not supported: ${e.logicalLevelTechnique1}`)}return r?Kf(i,r,0):i}(ed(t,e,n),n,0,i)}function td(t,e,n){return function(t,e){if(e.logicalLevelTechnique1===ff.DELTA&&e.logicalLevelTechnique2===ff.NONE)return function(t){const e=new Int32Array(t.length+1);e[0]=0,e[1]=Df(t[0]);let n=e[1];for(let r=2;r!==e.length;++r)n+=Df(t[r-1]),e[r]=e[r-1]+n;return new Uint32Array(e)}(t);if(e.logicalLevelTechnique1===ff.RLE&&e.logicalLevelTechnique2===ff.NONE)return function(t,e,n){const r=new Uint32Array(n+1);r[0]=0;let i=1,s=r[0];for(let n=0;n>>2,a=function(t,e){if(e<=t.encodedWords.length)return t.encodedWords;const n=new Uint32Array(Math.max(16,2*e));return t.encodedWords=n,n}(i,o);!function(t,e,n,r){if(e<0||n<0||e+n>t.length)throw new RangeError(`decodeBigEndianInt32sInto: out of bounds (offset=${e}, byteLength=${n}, bytes.length=${t.length})`);const i=Math.floor(n/4),s=n%4!=0,o=s?i+1:i;if(r.length0){const n=t.byteOffset+e;if(3&n)for(let n=0;n0){const e=0|t[r];if(r=r+1|0,255&e)throw new Error(`FastPFOR decode: invalid alignedLength=${e} (expected multiple of 256)`);if(i+e>s.length)throw new Error(`FastPFOR decode: output buffer too small (outPos=${i}, alignedLength=${e}, out.length=${s.length})`);r=function(t,e,n,r,i,s){const o=r+xf(i,gf);let a=r,l=n;for(;a!==o;){const n=Math.min(bf,o-a);l=kf(t,e,l,a,n,s),a=a+n|0;}return l}(t,s,r,i,e,o),i=i+e|0;}return function(t,e,n,r,i,s){if(0===s)return e;let o=0,a=e;const l=e+n,u=i;let c=i;const h=i+s;let p=0,f=0;for(;a>>o&255;if(o+=8,a+=o>>>5,o&=31,p|=(127&e)<28)throw new Error(`FastPFOR VByte: unterminated value (expected MSB=1 terminator within 5 bytes; shift=${f}, partial=${p}, decoded=${c-u}/${s}, inPos=${a}, inEnd=${l})`)}if(c!==h)throw new Error(`FastPFOR VByte: truncated stream (decoded=${c-u}, expected=${s}, consumedWords=${a-e}/${n}, vbyteStart=${e}, vbyteEnd=${l})`)}(t,r,t.length-r|0,s,i,e-i|0),s}(a.subarray(0,o),e,i.decoderWorkspace);return r.add(n),l}(t,e,n,r,function(t=16){if(t<0)throw new RangeError(`initialEncodedWordCapacity must be >= 0, got ${t}`);const e=Math.max(16,0|t);return {encodedWords:new Uint32Array(e),decoderWorkspace:_f()}}(n>>>2))}(t,n.numValues,n.byteLength,e);case df.VARINT:return Ef(t,e,n.numValues);case df.NONE:{const r=e.get();e.add(n.byteLength);const i=t.subarray(r,e.get());return new Uint32Array(i)}default:throw new Error(`Specified physicalLevelTechnique ${r} is not supported (yet).`)}}function nd(t,e,n){const r=ed(t,e,n);return 1===r.length?r[0]:function(t){return t[1]}(r)}function rd(t,e,n){return function(t){if(2===t.length){const e=Df(t[1]);return [e,e]}return [Df(t[2]),Df(t[3])]}(ed(t,e,n))}function id(t,e,n){return function(t){if(2===t.length){const e=zf(t[1]);return [e,e]}return [zf(t[2]),zf(t[3])]}(Tf(t,e,n.numValues))}function sd(t,e,n,r){return function(t,e,n){let r;switch(e.logicalLevelTechnique1){case ff.DELTA:r=function(t){const e=new BigUint64Array(t.length);e[0]=BigInt.asUintN(64,zf(t[0]));for(let n=1;n>1,e)-n}}function hd(t,e){let n=0;for(let r=0;r>r;return n}function pd(t,e,r,i,s,o,a){return t===Zf.MORTON?function(t,e,r,i,s,o){const a=new Array(s?i+1:i);for(let s=0;s[t])),r+=t,i+=t;}break;case Xf.LINESTRING:{let n,c;m?(n=f[i]-f[i-1],i++):n=p[r]-p[r-1],r++,y?(c=fd(g,a,n,!1),a+=2*n):(c=pd(t.vertexBufferType,g,d,l,n,!1,u),l+=n),e[o++]=[c],h&&s++;}break;case Xf.POLYGON:{const n=p[r]-p[r-1];r++;const c=new Array(n-1);let m,x=f[i]-f[i-1];if(i++,y){m=fd(g,a,x,!0),a+=2*x;for(let t=0;t0&&e.push(e[0]),h.push(e);}t[e]=h,s&&u++;}break;case Xf.MULTIPOLYGON:{const c=s[u]-s[u-1];u++;const h=[];for(let t=0;t0&&e.push(e[0]),h.push(e);}}t[e]=h;}}return t}[Symbol.iterator](){return null}}function xd(t,e,n,r,i,s){return new vd(t,e,n,r,i,s)}class vd extends gd{constructor(t,e,n,r,i,s){super(n,r,i,s),this._numGeometries=t,this._geometryType=e;}geometryType(t){return this._geometryType}get numGeometries(){return this._numGeometries}containsSingleGeometryType(){return !0}}function bd(t,e,n,r,i){return new wd(t,e,n,r,i)}class wd extends gd{constructor(t,e,n,r,i){super(e,n,r,i),this._geometryTypes=t;}geometryType(t){return this._geometryTypes[t]}get numGeometries(){return this._geometryTypes.length}containsSingleGeometryType(){return !1}}function _d(t,e,n,r,i){const s=Hf(t,n);let o,a,l,u;if(ad(s,r,t,n)===Gf.CONST){const i=nd(t,n,s);let c,h,p,f;for(let r=0;rn?e[s++]:1);return r}function Ad(t,e,n,r){const i=new Uint32Array(e[e.length-1]+1);let s=0;i[0]=s;let o=1,a=0;for(let l=0;l=o);){const n=t[r.increment()];if(n<=127){const o=n+3,a=t[r.increment()],l=Math.min(s+o,e);i.fill(a,s,l),s=l;}else {const o=256-n;for(let n=0;n=12?Td.decode(t.subarray(e,n)):function(t,e,n){let r="",i=e;for(;i239?4:e>223?3:e>191?2:1;if(i+u>n)break;1===u?e<128&&(l=e):2===u?(s=t[i+1],128==(192&s)&&(l=(31&e)<<6|63&s,l<=127&&(l=null))):3===u?(s=t[i+1],o=t[i+2],128==(192&s)&&128==(192&o)&&(l=(15&e)<<12|(63&s)<<6|63&o,(l<=2047||l>=55296&&l<=57343)&&(l=null))):4===u&&(s=t[i+1],o=t[i+2],a=t[i+3],128==(192&s)&&128==(192&o)&&128==(192&a)&&(l=(15&e)<<18|(63&s)<<12|(63&o)<<6|63&a,(l<=65535||l>=1114112)&&(l=null))),null===l?(l=65533,u=1):l>65535&&(l-=65536,r+=String.fromCharCode(l>>>10&1023|55296),l=56320|1023&l),r+=String.fromCharCode(l),i+=u;}return r}(t,e,n)}class Pd extends rf{constructor(t,e,n,r){super(t,n,r),this.offsetBuffer=e;}}class Dd extends Pd{constructor(t,e,n,r){super(t,e,n,r??e.length-1);}getValueFromBuffer(t){return Fd(this.dataBuffer,this.offsetBuffer[t],this.offsetBuffer[t+1])}}class zd extends Pd{constructor(t,e,n,r,i){super(t,n,r,i??e.length),this.indexBuffer=e,this.indexBuffer=e;}getValueFromBuffer(t){const e=this.indexBuffer[t];return Fd(this.dataBuffer,this.offsetBuffer[e],this.offsetBuffer[e+1])}}class Bd extends Pd{constructor(t,e,n,r,i,s,o){super(t,n,r,o),this.indexBuffer=e,this.symbolOffsetBuffer=i,this.symbolTableBuffer=s;}getValueFromBuffer(t){null==this.decodedDictionary&&(null==this.symbolLengthBuffer&&(this.symbolLengthBuffer=this.offsetToLengthBuffer(this.symbolOffsetBuffer)),this.decodedDictionary=function(t,e,n){const r=[],i=new Array(e.length).fill(0);for(let t=1;t=10}function $d(t){return 30===t}function Rd(t){if("scalarType"===t.type){const e=t.scalarType;if("physicalType"===e.type)switch(e.physicalType){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:default:return !1;case 9:return !0}if("logicalType"===e.type)return !1}else if("complexType"===t.type){const e=t.complexType;if("physicalType"===e.type)switch(e.physicalType){case 0:case 1:return !0;default:return !1}}return console.warn("Unexpected column type in hasStreamCount",t),!1}function Nd(t){return "complexType"===t.type&&"physicalType"===t.complexType?.type&&0===t.complexType.physicalType}const Ud=new TextDecoder;function jd(t,e){const n=Ef(t,e,1)[0];if(0===n)return "";const r=e.get(),i=t.subarray(r,r+n);return e.add(n),Ud.decode(i)}function qd(t,e){const n=Ef(t,e,1)[0]>>>0;if(n<10||n>30)throw new Error(`Unsupported field type code ${n}. Supported: 10-29(scalars), 30(STRUCT)`);const r=Ld(n);if(Od(n)&&(r.name=jd(t,e)),$d(n)){const n=Ef(t,e,1)[0]>>>0;r.complexType.children=new Array(n);for(let i=0;i>>0,r=Ld(n);if(!r)throw new Error(`Unsupported column type code ${n}. Supported: 0-3(ID), 4(GEOMETRY), 10-29(scalars), 30(STRUCT)`);if(Od(n)?r.name=jd(t,e):n>=0&&n<=3?r.name="id":4===n&&(r.name="geometry"),$d(n)){const n=Ef(t,e,1)[0]>>>0,i=r.complexType;i.children=new Array(n);for(let r=0;r>>0,s=Ef(t,e,1)[0]>>>0;r.columns=new Array(s);for(let n=0;n=4)for(;n>>0,o=r.get()+e;if(o>t.length)throw new Error(`Block overruns tile: ${o} > ${t.length}`);if(1!=Ef(t,r,1)[0]>>>0){r.set(o);continue}const[a,l]=Xd(t,r),u=a.featureTables[0];let c=null,h=null;const p=[];let f=0;for(const e of u.columns){const i=e.name;if("scalarType"===(s=e).type&&"logicalType"===s.scalarType?.type&&0===s.scalarType.logicalType){let s=null;if(e.nullable){const e=Hf(t,r),n=r.get(),i=Ed(t,e.numValues,e.byteLength,r);r.set(n+e.byteLength),s=new Wf(i,e.numValues);}const o=Hf(t,r);f=s?s.size():o.decompressedCount,c=Yd(t,e,r,i,o,s??f,n);}else if(Nd(e)){const e=Ef(t,r,1)[0];if(0===f){const e=r.get();f=Hf(t,r).decompressedCount,r.set(e);}h=_d(t,e,r,f);}else {const n=Rd(e)?Ef(t,r,1)[0]:1;if(0===n)continue;const i=Cd(t,r,e,n,f);if(i)if(Array.isArray(i))for(const t of i)p.push(t);else p.push(i);}}const d=new hf(u.name,h,c,p,l);i.push(d),r.set(o);}var s;return i}(new Uint8Array(t));this.layers=e.reduce(((t,e)=>Object.assign(Object.assign({},t),{[e.name]:new Hd(e)})),{});}}class Kd{constructor(t,e){this.feature=t,this.type=t.type,this.properties=t.tags?t.tags:{},this.extent=e,"id"in t&&("string"==typeof t.id?this.id=parseInt(t.id,10):"number"!=typeof t.id||isNaN(t.id)||(this.id=t.id));}loadGeometry(){const t=[],e=1===this.feature.type?[this.feature.geometry]:this.feature.geometry;for(const r of e){const e=[];for(const t of r)e.push(new n(t[0],t[1]));t.push(e);}return t}}const Jd="_geojsonTileLayer";function Qd(t,e,n=""){e.writeVarintField(15,t.version||1),e.writeStringField(1,t.name||""),e.writeVarintField(5,t.extent||4096);const r={jsonPrefix:n,keys:[],values:[],keycache:{},valuecache:{}};for(let n=0;n>31}function iy(t,e){const n=t.loadGeometry(),r=t.type;let i=0,s=0;for(const o of n){let n=1;1===r&&(n=o.length),e.writeVarint(ny(1,n));const a=3===r?o.length-1:o.length;for(let t=0;t=0&&e[3]>=0&&a.insert(o,e[0],e[1],e[2],e[3]);}}loadVTLayers(){return this.vtLayers||(this.vtLayers="mlt"!==this.encoding?new Nu(new Rh(this.rawTileData)).layers:new Wd(this.rawTileData).layers,this.sourceLayerCoder=new ef(this.vtLayers?Object.keys(this.vtLayers).sort():[Jd])),this.vtLayers}query(t,e,r,i){this.loadVTLayers();const s=t.params,o=T/t.tileSize/t.scale,a=yi(s.filter,s.globalState),l=t.queryGeometry,u=t.queryPadding*o,c=tf.fromPoints(l),h=this.grid.query(c.minX-u,c.minY-u,c.maxX+u,c.maxY+u),p=tf.fromPoints(t.cameraQueryGeometry).expandBy(u),f=this.grid3D.query(p.minX,p.minY,p.maxX,p.maxY,((e,r,i,s)=>function(t,e,r,i,s){for(const n of t)if(e<=n.x&&r<=n.y&&i>=n.x&&s>=n.y)return !0;const o=[new n(e,r),new n(e,s),new n(i,s),new n(i,r)];if(t.length>2)for(const e of o)if(il(t,e))return !0;for(let e=0;e(c||(c=qa(e)),n.queryIntersectsFeature({queryGeometry:l,feature:e,featureState:r,geometry:c,zoom:this.z,transform:t.transform,pixelsToTileUnits:o,pixelPosMatrix:t.pixelPosMatrix,unwrappedTileID:this.tileID.toUnwrapped(),getElevation:t.getElevation}))));}return d}loadMatchingFeature(t,e,n,r,i,s,o,a,l,u,c){const h=this.bucketLayerIDs[e];if(s&&!h.some((t=>s.has(t))))return;const p=this.sourceLayerCoder.decode(n),f=this.vtLayers[p].feature(r);if(i.needGeometry){const t=Ga(f,!0);if(!i.filter(new Cs(this.tileID.overscaledZ),t,this.tileID.canonical))return}else if(!i.filter(new Cs(this.tileID.overscaledZ),f))return;const d=this.getId(f,p);for(const e of h){if(s&&!s.has(e))continue;const n=a[e];if(!n)continue;let i={};d&&u&&(i=u.getState(n.sourceLayer||Jd,d));const h=$({},l[e]);h.paint=ay(h.paint,n.paint,f,i,o),h.layout=ay(h.layout,n.layout,f,i,o);const p=!c||c(f,n,i);if(!p)continue;const y=new nf(f,this.z,this.x,this.y,d);y.layer=h;let m=t[e];void 0===m&&(m=t[e]=[]),m.push({featureIndex:r,feature:y,intersectionZ:p});}}lookupSymbolFeatures(t,e,n,r,i,s,o,a){const l={};this.loadVTLayers();const u=yi(i.filterSpec,i.globalState);for(const i of t)this.loadMatchingFeature(l,n,r,i,u,s,o,a,e);return l}hasLayer(t){for(const e of this.bucketLayerIDs)for(const n of e)if(t===n)return !0;return !1}getId(t,e){var n;let r=t.id;return this.promoteId&&(r=t.properties["string"==typeof this.promoteId?this.promoteId:this.promoteId[e]],"boolean"==typeof r&&(r=Number(r)),void 0===r&&(null===(n=t.properties)||void 0===n?void 0:n.cluster)&&this.promoteId&&(r=Number(t.properties.cluster_id))),r}}function ay(t,e,n,r,i){return N(t,((t,s)=>{const o=e instanceof Gs?e.get(s):null;return (null==o?void 0:o.evaluate)?o.evaluate(n,r,i):o}))}function ly(t,e){return e-t}function uy(t,e,r,i,s){const o=[];for(const a of t){let t;for(let l=0;l=i&&c.x>=i||(u.x>=i?u=new n(i,u.y+(i-u.x)/(c.x-u.x)*(c.y-u.y))._round():c.x>=i&&(c=new n(i,u.y+(i-u.x)/(c.x-u.x)*(c.y-u.y))._round()),u.y>=s&&c.y>=s||(u.y>=s?u=new n(u.x+(s-u.y)/(c.y-u.y)*(c.x-u.x),s)._round():c.y>=s&&(c=new n(u.x+(s-u.y)/(c.y-u.y)*(c.x-u.x),s)._round()),t&&u.equals(t[t.length-1])||(t=[u],o.push(t)),t.push(c)))));}}return o}function cy(t,e,n,r,i){switch(e){case 1:return function(t,e,n,r){const i=[];for(const s of t)for(const t of s){const s=0===r?t.x:t.y;s>=e&&s<=n&&i.push([t]);}return i}(t,n,r,i);case 2:return py(t,n,r,i,!1);case 3:return py(t,n,r,i,!0)}return []}function hy(t,e,r,i,s){const o=0===i?fy:dy;let a=[];const l=[];for(let n=0;ne&&a.push(o(u,c,e)):h>r?p=e&&(a.push(o(u,c,e)),f=!0),p>r&&h<=r&&(a.push(o(u,c,r)),f=!0),!s&&f&&(l.push(a),a=[]);}const u=t.length-1,c=0===i?t[u].x:t[u].y;return c>=e&&c<=r&&a.push(t[u]),s&&a.length>0&&!a[0].equals(a[a.length-1])&&a.push(new n(a[0].x,a[0].y)),a.length>0&&l.push(a),l}function py(t,e,n,r,i){const s=[];for(const o of t){const t=hy(o,e,n,r,i);t.length>0&&s.push(...t);}return s}function fy(t,e,r){return new n(r,t.y+(r-t.x)/(e.x-t.x)*(e.y-t.y))}function dy(t,e,r){return new n(t.x+(r-t.y)/(e.y-t.y)*(e.x-t.x),r)}ds("FeatureIndex",oy,{omit:["rawTileData","sourceLayerCoder"]});class yy extends n{constructor(t,e,n,r){super(t,e),this.angle=n,void 0!==r&&(this.segment=r);}clone(){return new yy(this.x,this.y,this.angle,this.segment)}}function my(t,e,n,r,i){if(void 0===e.segment||0===n)return !0;let s=e,o=e.segment+1,a=0;for(;a>-n/2;){if(o--,o<0)return !1;a-=t[o].dist(s),s=t[o];}a+=t[o].dist(t[o+1]),o++;const l=[];let u=0;for(;ar;)u-=l.shift().angleDelta;if(u>i)return !1;o++,a+=e.dist(n);}return !0}function gy(t){let e=0;for(let n=0;nu){const c=(u-l)/s,h=xn.number(r.x,i.x,c),p=xn.number(r.y,i.y,c),f=new yy(h,p,i.angleTo(r),n);return f._round(),!o||my(t,f,a,o,e)?f:void 0}l+=s;}}function wy(t,e,n,r,i,s,o,a,l){const u=xy(r,s,o),c=vy(r,i),h=c*o,p=0===t[0].x||t[0].x===l||0===t[0].y||t[0].y===l;return e-h=0&&g=0&&x=0&&p+u<=c){const n=new yy(g,x,y,e);n._round(),r&&!my(t,n,s,r,i)||f.push(n);}}h+=d;}return a||f.length||o||(f=_y(t,h/2,n,r,i,s,o,!0,l)),f}function Sy(t,e,r,i){const s=[],o=t.image,a=o.pixelRatio,l=o.paddedRect.w-2,u=o.paddedRect.h-2;let c={x1:t.left,y1:t.top,x2:t.right,y2:t.bottom};const h=o.stretchX||[[0,l]],p=o.stretchY||[[0,u]],f=(t,e)=>t+e[1]-e[0],d=h.reduce(f,0),y=p.reduce(f,0),m=l-d,g=u-y;let x=0,v=d,b=0,w=y,_=0,S=m,A=0,M=g;if(o.content&&i){const e=o.content,n=e[2]-e[0],r=e[3]-e[1];(o.textFitWidth||o.textFitHeight)&&(c=dp(t)),x=Ay(h,0,e[0]),b=Ay(p,0,e[1]),v=Ay(h,e[0],e[2]),w=Ay(p,e[1],e[3]),_=e[0]-x,A=e[1]-b,S=n-v,M=r-w;}const k=c.x1,I=c.y1,E=c.x2-k,T=c.y2-I,F=(t,i,s,l)=>{const u=ky(t.stretch-x,v,E,k),c=Iy(t.fixed-_,S,t.stretch,d),h=ky(i.stretch-b,w,T,I),p=Iy(i.fixed-A,M,i.stretch,y),f=ky(s.stretch-x,v,E,k),m=Iy(s.fixed-_,S,s.stretch,d),g=ky(l.stretch-b,w,T,I),F=Iy(l.fixed-A,M,l.stretch,y),P=new n(u,h),D=new n(f,h),z=new n(f,g),B=new n(u,g),C=new n(c/a,p/a),V=new n(m/a,F/a),L=e*Math.PI/180;if(L){const t=Math.sin(L),e=Math.cos(L),n=[e,-t,t,e];P._matMult(n),D._matMult(n),B._matMult(n),z._matMult(n);}const O=t.stretch+t.fixed,$=i.stretch+i.fixed;return {tl:P,tr:D,bl:B,br:z,tex:{x:o.paddedRect.x+1+O,y:o.paddedRect.y+1+$,w:s.stretch+s.fixed-O,h:l.stretch+l.fixed-$},writingMode:void 0,glyphOffset:[0,0],sectionIndex:0,pixelOffsetTL:C,pixelOffsetBR:V,minFontScaleX:S/a/E,minFontScaleY:M/a/T,isSDF:r}};if(i&&(o.stretchX||o.stretchY)){const t=My(h,m,d),e=My(p,g,y);for(let n=0;n0&&(r=Math.max(10,r),this.circleDiameter=r);}else {const u=(null===(h=o.image)||void 0===h?void 0:h.content)&&(o.image.textFitWidth||o.image.textFitHeight)?dp(o):{x1:o.left,y1:o.top,x2:o.right,y2:o.bottom};u.y1=u.y1*a-l[0],u.y2=u.y2*a+l[2],u.x1=u.x1*a-l[3],u.x2=u.x2*a+l[1];const p=o.collisionPadding;if(p&&(u.x1-=p[0]*a,u.y1-=p[1]*a,u.x2+=p[2]*a,u.y2+=p[3]*a),c){const t=new n(u.x1,u.y1),e=new n(u.x2,u.y1),r=new n(u.x1,u.y2),i=new n(u.x2,u.y2),s=c*Math.PI/180;t._rotate(s),e._rotate(s),r._rotate(s),i._rotate(s),u.x1=Math.min(t.x,e.x,r.x,i.x),u.x2=Math.max(t.x,e.x,r.x,i.x),u.y1=Math.min(t.y,e.y,r.y,i.y),u.y2=Math.max(t.y,e.y,r.y,i.y);}t.emplaceBack(e.x,e.y,u.x1,u.y1,u.x2,u.y2,r,i,s);}this.boxEndIndex=t.length;}}class Ty{constructor(t=[],e=(t,e)=>te?1:0){if(this.data=t,this.length=this.data.length,this.compare=e,this.length>0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t);}push(t){this.data.push(t),this._up(this.length++);}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return --this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:n}=this,r=e[t];for(;t>0;){const i=t-1>>1,s=e[i];if(n(r,s)>=0)break;e[t]=s,t=i;}e[t]=r;}_down(t){const{data:e,compare:n}=this,r=this.length>>1,i=e[t];for(;t=0)break;e[t]=e[r],t=r;}e[t]=i;}}function Fy(t,e=1){const r=tf.fromPoints(t[0]),i=Math.min(r.width(),r.height());let s=i/2;const o=new Ty([],Py),{minX:a,minY:l,maxX:u,maxY:c}=r;if(0===i)return new n(a,l);for(let e=a;ep.d||!p.d)&&(p=n),n.max-p.d<=e||(s=n.h/2,o.push(new Dy(n.p.x-s,n.p.y-s,s,t)),o.push(new Dy(n.p.x+s,n.p.y-s,s,t)),o.push(new Dy(n.p.x-s,n.p.y+s,s,t)),o.push(new Dy(n.p.x+s,n.p.y+s,s,t)));}return h.d>0&&p.d-h.d<=e?h.p:p.p}function Py(t,e){return e.max-t.max}class Dy{constructor(t,e,r,i){this.p=new n(t,e),this.h=r,this.d=function(t,e){let n=!1,r=1/0;for(const i of e)for(let e=0,s=i.length,o=s-1;et.y!=a.y>t.y&&t.x<(a.x-s.x)*(t.y-s.y)/(a.y-s.y)+s.x&&(n=!n),r=Math.min(r,nl(t,s,a));}return (n?1:-1)*Math.sqrt(r)}(this.p,i),this.max=this.d+this.h*Math.SQRT2;}}var zy;t.aM=void 0,(zy=t.aM||(t.aM={}))[zy.center=1]="center",zy[zy.left=2]="left",zy[zy.right=3]="right",zy[zy.top=4]="top",zy[zy.bottom=5]="bottom",zy[zy["top-left"]=6]="top-left",zy[zy["top-right"]=7]="top-right",zy[zy["bottom-left"]=8]="bottom-left",zy[zy["bottom-right"]=9]="bottom-right";const By=Number.POSITIVE_INFINITY;function Cy(t,e){return e[1]!==By?function(t,e,n){let r=0,i=0;switch(e=Math.abs(e),n=Math.abs(n),t){case "top-right":case "top-left":case "top":i=n-7;break;case "bottom-right":case "bottom-left":case "bottom":i=7-n;}switch(t){case "top-right":case "bottom-right":case "right":r=-e;break;case "top-left":case "bottom-left":case "left":r=e;}return [r,i]}(t,e[0],e[1]):function(t,e){let n=0,r=0;e<0&&(e=0);const i=e/Math.SQRT2;switch(t){case "top-right":case "top-left":r=i-7;break;case "bottom-right":case "bottom-left":r=7-i;break;case "bottom":r=7-e;break;case "top":r=e-7;}switch(t){case "top-right":case "bottom-right":n=-i;break;case "top-left":case "bottom-left":n=i;break;case "left":n=e;break;case "right":n=-e;}return [n,r]}(t,e[0])}function Vy(t,e,n){var r;const i=t.layout,s=null===(r=i.get("text-variable-anchor-offset"))||void 0===r?void 0:r.evaluate(e,{},n);if(s){const t=s.values,e=[];for(let n=0;nt*Ih));r.startsWith("top")?i[1]-=7:r.startsWith("bottom")&&(i[1]+=7),e[n+1]=i;}return new $e(e)}const o=i.get("text-variable-anchor");if(o){let r;r=void 0!==t._unevaluatedLayout.getValue("text-radial-offset")?[i.get("text-radial-offset").evaluate(e,{},n)*Ih,By]:i.get("text-offset").evaluate(e,{},n).map((t=>t*Ih));const s=[];for(const t of o)s.push(t,Cy(t,r));return new $e(s)}return null}function Ly(t){switch(t){case "right":case "top-right":case "bottom-right":return "right";case "left":case "top-left":case "bottom-left":return "left"}return "center"}function Oy(e,n,r,i,s,o,a,l,u,c,h,p){let f=o.textMaxSize.evaluate(n,{});void 0===f&&(f=a);const d=e.layers[0].layout,y=d.get("icon-offset").evaluate(n,{},h),m=Ry(r.horizontal),g=a/24,x=e.tilePixelRatio*g,v=e.tilePixelRatio*f/24,b=e.tilePixelRatio*l,w=e.tilePixelRatio*d.get("symbol-spacing"),_=d.get("text-padding")*e.tilePixelRatio,S=function(t,e,n,r=1){const i=t.get("icon-padding").evaluate(e,{},n),s=null==i?void 0:i.values;return [s[0]*r,s[1]*r,s[2]*r,s[3]*r]}(d,n,h,e.tilePixelRatio),A=d.get("text-max-angle")/180*Math.PI,M="viewport"!==d.get("text-rotation-alignment")&&"point"!==d.get("symbol-placement"),k="map"===d.get("icon-rotation-alignment")&&"point"!==d.get("symbol-placement"),I=d.get("symbol-placement"),E=w/2,F=d.get("icon-text-fit");let P;i&&"none"!==F&&(e.allowVerticalPlacement&&r.vertical&&(P=yp(i,r.vertical,F,d.get("icon-text-fit-padding"),y,g)),m&&(i=yp(i,m,F,d.get("icon-text-fit-padding"),y,g)));const D=h?p.line.getGranularityForZoomLevel(h.z):1,z=(l,p)=>{p.x<0||p.x>=T||p.y<0||p.y>=T||function(e,n,r,i,s,o,a,l,u,c,h,p,f,d,y,m,g,x,v,b,w,_,S,A,M){const k=e.addToLineVertexArray(n,r);let I,E,T,F,P=0,D=0,z=0,B=0,C=-1,V=-1;const L={};let O=ba("");if(e.allowVerticalPlacement&&i.vertical){const t=l.layout.get("text-rotate").evaluate(w,{},A)+90;T=new Ey(u,n,c,h,p,i.vertical,f,d,y,t),a&&(F=new Ey(u,n,c,h,p,a,g,x,y,t));}if(s){const r=l.layout.get("icon-rotate").evaluate(w,{}),i="none"!==l.layout.get("icon-text-fit"),o=Sy(s,r,S,i),f=a?Sy(a,r,S,i):void 0;E=new Ey(u,n,c,h,p,s,g,x,!1,r),P=4*o.length;const d=e.iconSizeData;let y=null;"source"===d.kind?(y=[mp*l.layout.get("icon-size").evaluate(w,{})],y[0]>gp&&G(`${e.layerIds[0]}: Value for "icon-size" is >= 255. Reduce your "icon-size".`)):"composite"===d.kind&&(y=[mp*_.compositeIconSizes[0].evaluate(w,{},A),mp*_.compositeIconSizes[1].evaluate(w,{},A)],(y[0]>gp||y[1]>gp)&&G(`${e.layerIds[0]}: Value for "icon-size" is >= 255. Reduce your "icon-size".`)),e.addSymbols(e.icon,o,y,b,v,w,t.ax.none,n,k.lineStartIndex,k.lineLength,-1,A),C=e.icon.placedSymbolArray.length-1,f&&(D=4*f.length,e.addSymbols(e.icon,f,y,b,v,w,t.ax.vertical,n,k.lineStartIndex,k.lineLength,-1,A),V=e.icon.placedSymbolArray.length-1);}const $=Object.keys(i.horizontal);for(const r of $){const s=i.horizontal[r];if(!I){O=ba(s.text);const t=l.layout.get("text-rotate").evaluate(w,{},A);I=new Ey(u,n,c,h,p,s,f,d,y,t);}const a=1===s.positionedLines.length;if(z+=$y(e,n,s,o,l,y,w,m,k,i.vertical?t.ax.horizontal:t.ax.horizontalOnly,a?$:[r],L,C,_,A),a)break}i.vertical&&(B+=$y(e,n,i.vertical,o,l,y,w,m,k,t.ax.vertical,["vertical"],L,V,_,A));const R=I?I.boxStartIndex:e.collisionBoxArray.length,N=I?I.boxEndIndex:e.collisionBoxArray.length,U=T?T.boxStartIndex:e.collisionBoxArray.length,j=T?T.boxEndIndex:e.collisionBoxArray.length,q=E?E.boxStartIndex:e.collisionBoxArray.length,X=E?E.boxEndIndex:e.collisionBoxArray.length,Y=F?F.boxStartIndex:e.collisionBoxArray.length,Z=F?F.boxEndIndex:e.collisionBoxArray.length;let H=-1;const W=(t,e)=>(null==t?void 0:t.circleDiameter)?Math.max(t.circleDiameter,e):e;H=W(I,H),H=W(T,H),H=W(E,H),H=W(F,H);const K=H>-1?1:0;K&&(H*=M/Ih),e.glyphOffsetArray.length>=kp.MAX_GLYPHS&&G("Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907"),void 0!==w.sortKey&&e.addToSortKeyRanges(e.symbolInstances.length,w.sortKey);const J=Vy(l,w,A),[Q,tt]=function(e,n){const r=e.length,i=null==n?void 0:n.values;if((null==i?void 0:i.length)>0)for(let n=0;n=0?L.right:-1,L.center>=0?L.center:-1,L.left>=0?L.left:-1,L.vertical||-1,C,V,O,R,N,U,j,q,X,Y,Z,c,z,B,P,D,K,0,f,H,Q,tt);}(e,p,l,r,i,s,P,e.layers[0],e.collisionBoxArray,n.index,n.sourceLayerIndex,e.index,x,[_,_,_,_],M,u,b,S,k,y,n,o,c,h,a);};if("line"===I)for(const t of uy(n.geometry,0,0,T,T)){const n=Su(t,D),s=wy(n,w,A,r.vertical||m,i,24,v,e.overscaling,T);for(const t of s)m&&Ny(e,m.text,E,t)||z(n,t);}else if("line-center"===I){for(const t of n.geometry)if(t.length>1){const e=Su(t,D),n=by(e,A,r.vertical||m,i,24,v);n&&z(e,n);}}else if("Polygon"===n.type)for(const t of rr(n.geometry,0)){const e=Fy(t,16);z(Su(t[0],D,!0),new yy(e.x,e.y,0));}else if("LineString"===n.type)for(const t of n.geometry){const e=Su(t,D);z(e,new yy(e[0].x,e[0].y,0));}else if("Point"===n.type)for(const t of n.geometry)for(const e of t)z([e],new yy(e.x,e.y,0));}function $y(t,e,r,i,s,o,a,l,u,c,h,p,f,d,y){const m=function(t,e,r,i,s,o,a,l){const u=i.layout.get("text-rotate").evaluate(o,{})*Math.PI/180,c=[];for(const t of e.positionedLines)for(const i of t.positionedGlyphs){if(!i.rect)continue;const o=i.rect||{};let h=4,p=!0,f=1,d=0;const y=(s||l)&&i.vertical,m=i.metrics.advance*i.scale/2;if(l&&e.verticalizable&&(d=t.lineOffset/2-(i.imageName?-(Ih-i.metrics.width*i.scale)/2:(i.scale-1)*Ih)),i.imageName){const t=a[i.imageName];p=t.sdf,f=t.pixelRatio,h=1/f;}const g=s?[i.x+m,i.y]:[0,0];let x=s?[0,0]:[i.x+m+r[0],i.y+r[1]-d],v=[0,0];y&&(v=x,x=[0,0]);const b=i.metrics.isDoubleResolution?2:1,w=(i.metrics.left-h)*i.scale-m+x[0],_=(-i.metrics.top-h)*i.scale+x[1],S=w+o.w/b*i.scale/f,A=_+o.h/b*i.scale/f,M=new n(w,_),k=new n(S,_),I=new n(w,A),E=new n(S,A);if(y){const t=new n(-m,m- -17),e=-Math.PI/2,r=12-m,s=new n(22-r,-(i.imageName?r:0)),o=new n(...v);M._rotateAround(e,t)._add(s)._add(o),k._rotateAround(e,t)._add(s)._add(o),I._rotateAround(e,t)._add(s)._add(o),E._rotateAround(e,t)._add(s)._add(o);}if(u){const t=Math.sin(u),e=Math.cos(u),n=[e,-t,t,e];M._matMult(n),k._matMult(n),I._matMult(n),E._matMult(n);}const T=new n(0,0),F=new n(0,0);c.push({tl:M,tr:k,bl:I,br:E,tex:o,writingMode:e.writingMode,glyphOffset:g,sectionIndex:i.sectionIndex,isSDF:p,pixelOffsetTL:T,pixelOffsetBR:F,minFontScaleX:0,minFontScaleY:0});}return c}(0,r,l,s,o,a,i,t.allowVerticalPlacement),g=t.textSizeData;let x=null;"source"===g.kind?(x=[mp*s.layout.get("text-size").evaluate(a,{})],x[0]>gp&&G(`${t.layerIds[0]}: Value for "text-size" is >= 255. Reduce your "text-size".`)):"composite"===g.kind&&(x=[mp*d.compositeTextSizes[0].evaluate(a,{},y),mp*d.compositeTextSizes[1].evaluate(a,{},y)],(x[0]>gp||x[1]>gp)&&G(`${t.layerIds[0]}: Value for "text-size" is >= 255. Reduce your "text-size".`)),t.addSymbols(t.text,m,x,l,o,a,c,e,u.lineStartIndex,u.lineLength,f,y);for(const e of h)p[e]=t.text.placedSymbolArray.length-1;return 4*m.length}function Ry(t){for(const e in t)return t[e];return null}function Ny(t,e,n,r){const i=t.compareText;if(e in i){const t=i[e];for(let e=t.length-1;e>=0;e--)if(r.dist(t[e])this.process())),this.subscription=tt(this.target,"message",(t=>this.receive(t)),!1),this.globalScope=Y(self)?t:window;}registerMessageHandler(t,e){this.messageHandlers[t]=e;}unregisterMessageHandler(t){delete this.messageHandlers[t];}sendAsync(t,e){return new Promise(((n,r)=>{const i=Math.round(1e18*Math.random()).toString(36).substring(0,10),s=e?tt(e.signal,"abort",(()=>{null==s||s.unsubscribe(),delete this.resolveRejects[i];const e={id:i,type:"",origin:location.origin,targetMapId:t.targetMapId,sourceMapId:this.mapId};this.target.postMessage(e);}),Lp):null;this.resolveRejects[i]={resolve:t=>{null==s||s.unsubscribe(),n(t);},reject:t=>{null==s||s.unsubscribe(),r(t);}};const o=[],a=Object.assign(Object.assign({},t),{id:i,sourceMapId:this.mapId,origin:location.origin,data:xs(t.data,o)});this.target.postMessage(a,{transfer:o});}))}receive(t){const e=t.data,n=e.id,r=["file://","resource://android","null"],i=[e.origin,location.origin],s=e.origin===location.origin,o=i.some((t=>r.includes(t)));if((s||o)&&(!e.targetMapId||this.mapId===e.targetMapId)){if(""===e.type){delete this.tasks[n];const t=this.abortControllers[n];return delete this.abortControllers[n],void(t&&t.abort())}if(Y(self)||e.mustQueue)return this.tasks[n]=e,this.taskQueue.push(n),void this.invoker.trigger();this.processTask(n,e);}}process(){if(0===this.taskQueue.length)return;const t=this.taskQueue.shift(),e=this.tasks[t];delete this.tasks[t],this.taskQueue.length>0&&this.invoker.trigger(),e&&this.processTask(t,e);}processTask(t,n){return e(this,void 0,void 0,(function*(){if(""===n.type){const e=this.resolveRejects[t];if(delete this.resolveRejects[t],!e)return;return void(n.error?e.reject(P(vs(n.error))):e.resolve(vs(n.data)))}if(!this.messageHandlers[n.type])return void this.completeTask(t,new Error(`Could not find a registered handler for ${n.type}, map ID: ${this.mapId}, available handlers: ${Object.keys(this.messageHandlers).join(", ")}`));const e=vs(n.data),r=new AbortController;this.abortControllers[t]=r;try{const i=yield this.messageHandlers[n.type](n.sourceMapId,e,r);this.completeTask(t,null,i);}catch(e){this.completeTask(t,P(e));}}))}completeTask(t,e,n){const r=[];delete this.abortControllers[t];const i={id:t,type:"",sourceMapId:this.mapId,origin:location.origin,error:e?xs(e):null,data:xs(n,r)};this.target.postMessage(i,{transfer:r});}remove(){this.invoker.remove(),this.subscription.unsubscribe();}},t.O=function(){var t=new f(16);return f!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0),t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},t.P=n,t.Q=function(t,e,n){var r,i,s,o,a,l,u,c,h,p,f,d,y=n[0],m=n[1],g=n[2];return e===t?(t[12]=e[0]*y+e[4]*m+e[8]*g+e[12],t[13]=e[1]*y+e[5]*m+e[9]*g+e[13],t[14]=e[2]*y+e[6]*m+e[10]*g+e[14],t[15]=e[3]*y+e[7]*m+e[11]*g+e[15]):(i=e[1],s=e[2],o=e[3],a=e[4],l=e[5],u=e[6],c=e[7],h=e[8],p=e[9],f=e[10],d=e[11],t[0]=r=e[0],t[1]=i,t[2]=s,t[3]=o,t[4]=a,t[5]=l,t[6]=u,t[7]=c,t[8]=h,t[9]=p,t[10]=f,t[11]=d,t[12]=r*y+a*m+h*g+e[12],t[13]=i*y+l*m+p*g+e[13],t[14]=s*y+u*m+f*g+e[14],t[15]=o*y+c*m+d*g+e[15]),t},t.R=Il,t.S=function(t,e,n){var r=n[0],i=n[1],s=n[2];return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t[4]=e[4]*i,t[5]=e[5]*i,t[6]=e[6]*i,t[7]=e[7]*i,t[8]=e[8]*s,t[9]=e[9]*s,t[10]=e[10]*s,t[11]=e[11]*s,t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},t.T=Ol,t.U=function(t,e,n){var r=e[0],i=e[1],s=e[2],o=e[3],a=e[4],l=e[5],u=e[6],c=e[7],h=e[8],p=e[9],f=e[10],d=e[11],y=e[12],m=e[13],g=e[14],x=e[15],v=n[0],b=n[1],w=n[2],_=n[3];return t[0]=v*r+b*a+w*h+_*y,t[1]=v*i+b*l+w*p+_*m,t[2]=v*s+b*u+w*f+_*g,t[3]=v*o+b*c+w*d+_*x,t[4]=(v=n[4])*r+(b=n[5])*a+(w=n[6])*h+(_=n[7])*y,t[5]=v*i+b*l+w*p+_*m,t[6]=v*s+b*u+w*f+_*g,t[7]=v*o+b*c+w*d+_*x,t[8]=(v=n[8])*r+(b=n[9])*a+(w=n[10])*h+(_=n[11])*y,t[9]=v*i+b*l+w*p+_*m,t[10]=v*s+b*u+w*f+_*g,t[11]=v*o+b*c+w*d+_*x,t[12]=(v=n[12])*r+(b=n[13])*a+(w=n[14])*h+(_=n[15])*y,t[13]=v*i+b*l+w*p+_*m,t[14]=v*s+b*u+w*f+_*g,t[15]=v*o+b*c+w*d+_*x,t},t.V=function(t,e){const n={};for(const r of e)r in t&&(n[r]=t[r]);return n},t.W=$p,t.X=O,t.Y=jp,t.Z=Up,t._=e,t.a=ot,t.a$=function(t){var e=new f(3);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e},t.a0=c,t.a1=h,t.a2=K,t.a3=Jp,t.a4=Gp,t.a5=Xp,t.a6=T,t.a7=Zp,t.a8=tf,t.a9=25,t.aA=function(t){var e=t[0],n=t[1];return Math.sqrt(e*e+n*n)},t.aB=function(t){return t[0]=0,t[1]=0,t},t.aC=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t},t.aD=_p,t.aE=A,t.aF=function(t,e,r,i){const s=e.y-t.y,o=e.x-t.x,a=i.y-r.y,l=i.x-r.x,u=a*o-l*s;if(0===u)return null;const c=(l*(t.y-r.y)-a*(t.x-r.x))/u;return new n(t.x+c*o,t.y+c*s)},t.aG=uy,t.aH=Ha,t.aI=function(t){let e=1/0,n=1/0,r=-1/0,i=-1/0;for(const s of t)e=Math.min(e,s.x),n=Math.min(n,s.y),r=Math.max(r,s.x),i=Math.max(i,s.y);return [e,n,r,i]},t.aJ=Ih,t.aK=F,t.aL=function(t,e,n,r,i=!1){if(!n[0]&&!n[1])return [0,0];const s=i?"map"===r?-t.bearingInRadians:0:"viewport"===r?t.bearingInRadians:0;if(s){const t=Math.sin(s),e=Math.cos(s);n=[n[0]*e-n[1]*t,n[0]*t+n[1]*e];}return [i?n[0]:F(e,n[0],t.zoom),i?n[1]:F(e,n[1],t.zoom)]},t.aN=vp,t.aO=Ly,t.aP=op,t.aQ=t=>"symbol"===t.type,t.aR=ec,t.aS=ao,t.aT=xu,t.aU=Xo,t.aV=ua,t.aW=sa,t.aX=nt,t.aY=Yp,t.aZ=b,t.a_=v,t.aa=Wp,t.ab=t=>{const e=window.document.createElement("video");return e.muted=!0,new Promise((n=>{e.onloadstart=()=>{n(e);};for(const n of t){const t=window.document.createElement("source");yt(n)||(e.crossOrigin="Anonymous"),t.src=n,e.appendChild(t);}}))},t.ac=zt,t.ad=function(){return R++},t.ae=Co,t.af=kp,t.ag=Jd,t.ah=yi,t.ai=Ga,t.aj=nf,t.ak=function(t){const e={};if(t.replace(/(?:^|(?:\s*\,\s*))([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\]|\\.)*)\")))?/g,((t,n,r,i)=>{const s=r||i;return e[n]=!s||s.toLowerCase(),""})),e["max-age"]){const t=parseInt(e["max-age"],10);isNaN(t)?delete e["max-age"]:e["max-age"]=t;}return e},t.al=L,t.am=85.051129,t.an=et,t.ao=function(t){return Math.pow(2,t)},t.ap=y,t.aq=qp,t.ar=function(t){return Math.log(t)/Math.LN2},t.as=function(t){var e=t[0],n=t[1];return e*e+n*n},t.at=function(t){if(!t.length)return new Set;const e=Math.max(...t.map((t=>t.canonical.z)));let n=1/0,r=-1/0,i=1/0,s=-1/0;const o=[];for(const a of t){const{x:t,y:l,z:u}=a.canonical,c=Math.pow(2,e-u),h=t*c,p=l*c;o.push({id:a,x:h,y:p}),hr&&(r=h),ps&&(s=p);}const a=new Set;for(const t of o)t.x!==n&&t.x!==r&&t.y!==i&&t.y!==s||a.add(t.id);return a},t.au=function(t,e){const n=Math.abs(2*t.wrap)-+(t.wrap<0),r=Math.abs(2*e.wrap)-+(e.wrap<0);return t.overscaledZ-e.overscaledZ||r-n||e.canonical.y-t.canonical.y||e.canonical.x-t.canonical.x},t.av=class{constructor(t,e){this.max=t,this.onRemove=e,this.reset();}reset(){for(const t in this.data)for(const e of this.data[t])e.timeout&&clearTimeout(e.timeout),this.onRemove(e.value);return this.data={},this.order=[],this}add(t,e,n){const r=t.wrapped().key;void 0===this.data[r]&&(this.data[r]=[]);const i={value:e,timeout:void 0};if(void 0!==n&&(i.timeout=setTimeout((()=>{this.remove(t,i);}),n)),this.data[r].push(i),this.order.push(r),this.order.length>this.max){const t=this._getAndRemoveByKey(this.order[0]);t&&this.onRemove(t);}return this}has(t){return t.wrapped().key in this.data}getAndRemove(t){return this.has(t)?this._getAndRemoveByKey(t.wrapped().key):null}_getAndRemoveByKey(t){const e=this.data[t].shift();return e.timeout&&clearTimeout(e.timeout),0===this.data[t].length&&delete this.data[t],this.order.splice(this.order.indexOf(t),1),e.value}getByKey(t){const e=this.data[t];return e?e[0].value:null}get(t){return this.has(t)?this.data[t.wrapped().key][0].value:null}remove(t,e){if(!this.has(t))return this;const n=t.wrapped().key,r=void 0===e?0:this.data[n].indexOf(e),i=this.data[n][r];return this.data[n].splice(r,1),i.timeout&&clearTimeout(i.timeout),0===this.data[n].length&&delete this.data[n],this.onRemove(i.value),this.order.splice(this.order.indexOf(n),1),this}setMaxSize(t){for(this.max=t;this.order.length>this.max;){const t=this._getAndRemoveByKey(this.order[0]);t&&this.onRemove(t);}return this}filter(t){const e=[];for(const n in this.data)for(const r of this.data[n])t(r.value)||e.push(r);for(const t of e)this.remove(t.value.tileID,t);}},t.aw=function(t,e){let n=0,r=0;if("constant"===t.kind)r=t.layoutSize;else if("source"!==t.kind){const{interpolationType:i,minZoom:s,maxZoom:o}=t,a=i?L(mn.interpolationFactor(i,e,s,o),0,1):0;"camera"===t.kind?r=xn.number(t.minSize,t.maxSize,a):n=a;}return {uSizeT:n,uSize:r}},t.ay=function(t,{uSize:e,uSizeT:n},{lowerSize:r,upperSize:i}){return "source"===t.kind?r/mp:"composite"===t.kind?xn.number(r/mp,i/mp,n):e},t.az=I,t.b=H,t.b$=Ea,t.b0=function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t[2]=e[2]-n[2],t},t.b1=function(t,e){var n=e[0],r=e[1],i=e[2],s=n*n+r*r+i*i;return s>0&&(s=1/Math.sqrt(s)),t[0]=e[0]*s,t[1]=e[1]*s,t[2]=e[2]*s,t},t.b2=w,t.b3=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},t.b4=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t[2]=e[2]*n[2],t[3]=e[3]*n[3],t},t.b5=g,t.b6=function(t,e,n){const r=e[0]*n[0]+e[1]*n[1]+e[2]*n[2];return 0===r?null:(-(t[0]*n[0]+t[1]*n[1]+t[2]*n[2])-n[3])/r},t.b7=S,t.b8=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t[2]=e[2]*n,t[3]=e[3]*n,t},t.b9=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]},t.bA=k,t.bB=function(t,e,n){var r=n[0],i=n[1],s=n[2],o=n[3],a=e[0],l=e[1],u=e[2],c=i*u-s*l,h=s*a-r*u,p=r*l-i*a;return t[0]=a+o*(c+=c)+i*(p+=p)-s*(h+=h),t[1]=l+o*h+s*c-r*p,t[2]=u+o*p+r*h-i*c,t},t.bC=function(t,e,n){const r=(i=[t[0],t[1],t[2],e[0],e[1],e[2],n[0],n[1],n[2]])[0]*((c=i[8])*(o=i[4])-(a=i[5])*(u=i[7]))+i[1]*(-c*(s=i[3])+a*(l=i[6]))+i[2]*(u*s-o*l);var i,s,o,a,l,u,c;if(0===r)return null;const h=w([],[e[0],e[1],e[2]],[n[0],n[1],n[2]]),p=w([],[n[0],n[1],n[2]],[t[0],t[1],t[2]]),f=w([],[t[0],t[1],t[2]],[e[0],e[1],e[2]]),d=b([],h,-t[3]);return v(d,d,b([],p,-e[3])),v(d,d,b([],f,-n[3])),b(d,d,1/r),d},t.bD=Op,t.bE=function(){return new Float64Array(4)},t.bF=function(t,e,n,r){var i=[],s=[];return i[0]=e[0]-n[0],i[1]=e[1]-n[1],i[2]=e[2]-n[2],s[0]=i[0]*Math.cos(r)-i[1]*Math.sin(r),s[1]=i[0]*Math.sin(r)+i[1]*Math.cos(r),s[2]=i[2],t[0]=s[0]+n[0],t[1]=s[1]+n[1],t[2]=s[2]+n[2],t},t.bG=function(t,e,n,r){var i=[],s=[];return i[0]=e[0]-n[0],i[1]=e[1]-n[1],i[2]=e[2]-n[2],s[0]=i[0],s[1]=i[1]*Math.cos(r)-i[2]*Math.sin(r),s[2]=i[1]*Math.sin(r)+i[2]*Math.cos(r),t[0]=s[0]+n[0],t[1]=s[1]+n[1],t[2]=s[2]+n[2],t},t.bH=function(t,e,n,r){var i=[],s=[];return i[0]=e[0]-n[0],i[1]=e[1]-n[1],i[2]=e[2]-n[2],s[0]=i[2]*Math.sin(r)+i[0]*Math.cos(r),s[1]=i[1],s[2]=i[2]*Math.cos(r)-i[0]*Math.sin(r),t[0]=s[0]+n[0],t[1]=s[1]+n[1],t[2]=s[2]+n[2],t},t.bI=function(t,e,n){var r=Math.sin(n),i=Math.cos(n),s=e[0],o=e[1],a=e[2],l=e[3],u=e[8],c=e[9],h=e[10],p=e[11];return e!==t&&(t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[0]=s*i-u*r,t[1]=o*i-c*r,t[2]=a*i-h*r,t[3]=l*i-p*r,t[8]=s*r+u*i,t[9]=o*r+c*i,t[10]=a*r+h*i,t[11]=l*r+p*i,t},t.bJ=function(t,e){const n=D(t,360),r=D(e,360),i=r-n,s=r>n?i-360:i+360;return Math.abs(i)0?o:-o},t.bM=function(t,e){const n=D(t,2*Math.PI),r=D(e,2*Math.PI);return Math.min(Math.abs(n-r),Math.abs(n-r+2*Math.PI),Math.abs(n-r-2*Math.PI))},t.bN=function(){const t={},e=wt.$version;for(const n in wt.$root){const r=wt.$root[n];if(r.required){let i=null;i="version"===n?e:"array"===r.type?[]:{},null!=i&&(t[n]=i);}}return t},t.bO=ft,t.bP=bs,t.bQ=function t(e,n){if(Array.isArray(e)){if(!Array.isArray(n)||e.length!==n.length)return !1;for(let r=0;r"raster"===t.type,t.bV=j,t.bW=function(t,e){if(!t)return [{command:"setStyle",args:[e]}];let n=[];try{if(!At(t.version,e.version))return [{command:"setStyle",args:[e]}];At(t.center,e.center)||n.push({command:"setCenter",args:[e.center]}),At(t.state,e.state)||n.push({command:"setGlobalState",args:[e.state]}),At(t.centerAltitude,e.centerAltitude)||n.push({command:"setCenterAltitude",args:[e.centerAltitude]}),At(t.zoom,e.zoom)||n.push({command:"setZoom",args:[e.zoom]}),At(t.bearing,e.bearing)||n.push({command:"setBearing",args:[e.bearing]}),At(t.pitch,e.pitch)||n.push({command:"setPitch",args:[e.pitch]}),At(t.roll,e.roll)||n.push({command:"setRoll",args:[e.roll]}),At(t.sprite,e.sprite)||n.push({command:"setSprite",args:[e.sprite]}),At(t.glyphs,e.glyphs)||n.push({command:"setGlyphs",args:[e.glyphs]}),At(t.transition,e.transition)||n.push({command:"setTransition",args:[e.transition]}),At(t.light,e.light)||n.push({command:"setLight",args:[e.light]}),At(t.terrain,e.terrain)||n.push({command:"setTerrain",args:[e.terrain]}),At(t.sky,e.sky)||n.push({command:"setSky",args:[e.sky]}),At(t.projection,e.projection)||n.push({command:"setProjection",args:[e.projection]});const r={},i=[];!function(t,e,n,r){let i;for(i in e=e||{},t=t||{})Object.prototype.hasOwnProperty.call(t,i)&&(Object.prototype.hasOwnProperty.call(e,i)||It(i,n,r));for(i in e)Object.prototype.hasOwnProperty.call(e,i)&&(Object.prototype.hasOwnProperty.call(t,i)?At(t[i],e[i])||("geojson"===t[i].type&&"geojson"===e[i].type&&Tt(t,e,i)?Mt(n,{command:"setGeoJSONSourceData",args:[i,e[i].data]}):Et(i,e,n,r)):kt(i,e,n));}(t.sources,e.sources,i,r);const s=[];t.layers&&t.layers.forEach((t=>{"source"in t&&r[t.source]?n.push({command:"removeLayer",args:[t.id]}):s.push(t);})),n=n.concat(i),function(t,e,n){e=e||[];const r=(t=t||[]).map(Pt),i=e.map(Pt),s=t.reduce(Dt,{}),o=e.reduce(Dt,{}),a=r.slice(),l=Object.create(null);let u,c,h,p,f;for(let t=0,e=0;tp?(i=Math.acos(s),o=Math.sin(i),a=Math.sin((1-r)*i)/o,l=Math.sin(r*i)/o):(a=1-r,l=r),t[0]=a*u+l*d,t[1]=a*c+l*y,t[2]=a*h+l*m,t[3]=a*f+l*g,t},t.bm=function(t){const e=new Float64Array(9);var n,r,i,s,o,a,l,u,c,h,p,f,d,y,m,g,x,v;h=(i=(r=t)[0])*(l=i+i),p=(s=r[1])*l,d=(o=r[2])*l,y=o*(u=s+s),g=(a=r[3])*l,x=a*u,v=a*(c=o+o),(n=e)[0]=1-(f=s*u)-(m=o*c),n[3]=p-v,n[6]=d+x,n[1]=p+v,n[4]=1-h-m,n[7]=y-g,n[2]=d-x,n[5]=y+g,n[8]=1-h-f;const b=nt(-Math.asin(L(e[2],-1,1)));let w,_;return Math.hypot(e[5],e[8])<.001?(w=0,_=-nt(Math.atan2(e[3],e[4]))):(w=nt(0===e[5]&&0===e[8]?0:Math.atan2(e[5],e[8])),_=nt(0===e[1]&&0===e[0]?0:Math.atan2(e[1],e[0]))),{roll:w,pitch:b+90,bearing:_}},t.bn=function(t,e){return t.roll==e.roll&&t.pitch==e.pitch&&t.bearing==e.bearing},t.bo=Te,t.bp=ka,t.bq=vu,t.br=bu,t.bs=gu,t.bt=z,t.bu=B,t.bv=Ne,t.bw=function(t,e,n,r,i){return z(r,i,L((t-e)/(n-e),0,1))},t.bx=function(t,e,n,r){return t[0]=e[0]+n[0]*r,t[1]=e[1]+n[1]*r,t[2]=e[2]+n[2]*r,t},t.by=D,t.bz=function(){return new Float64Array(3)},t.c=ut,t.c$=si,t.c0=class extends Ma{constructor(t,e){super(t,e),this.current=Ta;}set(t){if(t[12]!==this.current[12]||t[0]!==this.current[0])return this.current=t,void this.gl.uniformMatrix4fv(this.location,!1,t);for(let e=1;e<16;e++)if(t[e]!==this.current[e]){this.current=t,this.gl.uniformMatrix4fv(this.location,!1,t);break}}},t.c1=Ia,t.c2=class extends Ma{constructor(t,e){super(t,e),this.current=[0,0,0];}set(t){t[0]===this.current[0]&&t[1]===this.current[1]&&t[2]===this.current[2]||(this.current=t,this.gl.uniform3f(this.location,t[0],t[1],t[2]));}},t.c3=class extends Ma{constructor(t,e){super(t,e),this.current=[0,0];}set(t){t[0]===this.current[0]&&t[1]===this.current[1]||(this.current=t,this.gl.uniform2f(this.location,t[0],t[1]));}},t.c4=d,t.c5=function(t,e){var n=Math.sin(e),r=Math.cos(e);return t[0]=r,t[1]=n,t[2]=0,t[3]=-n,t[4]=r,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},t.c6=function(t,e,n){var r=e[0],i=e[1],s=e[2];return t[0]=r*n[0]+i*n[3]+s*n[6],t[1]=r*n[1]+i*n[4]+s*n[7],t[2]=r*n[2]+i*n[5]+s*n[8],t},t.c7=function(t,e,n,r,i,s,o){var a=1/(e-n),l=1/(r-i),u=1/(s-o);return t[0]=-2*a,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*l,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*u,t[11]=0,t[12]=(e+n)*a,t[13]=(i+r)*l,t[14]=(o+s)*u,t[15]=1,t},t.c8=class extends Ma{constructor(t,e){super(t,e),this.current=new Array;}set(t){if(t!=this.current){this.current=t;const e=new Float32Array(4*t.length);for(let n=0;n25||r<0||r>=1||n<0||n>=1)},t.cE=function(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=e[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=e[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},t.cF=class extends co{},t.cG=pt,t.cH=function(t,e){ut.REGISTERED_PROTOCOLS[t]=e;},t.cI=function(t){delete ut.REGISTERED_PROTOCOLS[t];},t.cJ=function(t,e){const n={};for(let r=0;rt*Ih));}let x=u?"center":s.get("text-justify").evaluate(o,{},e.canonical);const b="point"===s.get("symbol-placement")?s.get("text-max-width").evaluate(o,{},e.canonical)*Ih:1/0,w=()=>{e.bucket.allowVerticalPlacement&&As(n)&&(m.vertical=sp(g,e.glyphMap,e.glyphPositions,e.imagePositions,p,b,l,h,"left",a,v,t.ax.vertical,!0,d,f));};if(!u&&y){const n=new Set;if("auto"===x)for(let t=0;tQd(t,e,n)),t.layers[r]);}(t,n,e),n.finish()},t.cT=function(t,e,n,r,i,s){let o=cy(t,e,n,i,0);return o=cy(o,e,r,s,1),o},t.cU=class{constructor(t){this.maxEntries=t,this.map=new Map;}get(t){const e=this.map.get(t);return void 0!==e&&(this.map.delete(t),this.map.set(t,e)),e}set(t,e){if(this.map.has(t))this.map.delete(t);else if(this.map.size>=this.maxEntries){const t=this.map.keys().next().value;this.map.delete(t);}this.map.set(t,e);}clear(){this.map.clear();}},t.cV=Nu,t.cW=Rh,t.cX=Wd,t.cY=function(t,n,r,i,s){return e(this,void 0,void 0,(function*(){if(h())try{return yield K(t,n,r,i,s)}catch(t){}return function(t,e,n,r,i){const s=t.width,o=t.height;J&&Q||(J=new OffscreenCanvas(s,o),Q=J.getContext("2d",{willReadFrequently:!0})),J.width=s,J.height=o,Q.drawImage(t,0,0,s,o);const a=Q.getImageData(e,n,r,i);return Q.clearRect(0,0,s,o),a.data}(t,n,r,i,s)}))},t.cZ=$l,t.c_=class{constructor(t,e){this.layers={[Jd]:this},this.name=Jd,this.version=e?e.version:1,this.extent=e?e.extent:4096,this.length=t.length,this.features=t;}feature(t){return new Kd(this.features[t],this.extent)}},t.ca=class extends So{},t.cb=Ah,t.cc=class extends Mo{},t.cd=Fl,t.ce=function(t){return t<=1?1:Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))},t.cf=Tl,t.cg=function(t,e,n){var r=e[0],i=e[1],s=e[2],o=n[3]*r+n[7]*i+n[11]*s+n[15];return t[0]=(n[0]*r+n[4]*i+n[8]*s+n[12])/(o=o||1),t[1]=(n[1]*r+n[5]*i+n[9]*s+n[13])/o,t[2]=(n[2]*r+n[6]*i+n[10]*s+n[14])/o,t},t.ch=class extends ho{},t.ci=class extends Do{},t.cj=function(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]&&t[4]===e[4]&&t[5]===e[5]&&t[6]===e[6]&&t[7]===e[7]&&t[8]===e[8]&&t[9]===e[9]&&t[10]===e[10]&&t[11]===e[11]&&t[12]===e[12]&&t[13]===e[13]&&t[14]===e[14]&&t[15]===e[15]},t.ck=function(t,e){var n=t[0],r=t[1],i=t[2],s=t[3],o=t[4],a=t[5],l=t[6],u=t[7],c=t[8],h=t[9],f=t[10],d=t[11],y=t[12],m=t[13],g=t[14],x=t[15],v=e[0],b=e[1],w=e[2],_=e[3],S=e[4],A=e[5],M=e[6],k=e[7],I=e[8],E=e[9],T=e[10],F=e[11],P=e[12],D=e[13],z=e[14],B=e[15];return Math.abs(n-v)<=p*Math.max(1,Math.abs(n),Math.abs(v))&&Math.abs(r-b)<=p*Math.max(1,Math.abs(r),Math.abs(b))&&Math.abs(i-w)<=p*Math.max(1,Math.abs(i),Math.abs(w))&&Math.abs(s-_)<=p*Math.max(1,Math.abs(s),Math.abs(_))&&Math.abs(o-S)<=p*Math.max(1,Math.abs(o),Math.abs(S))&&Math.abs(a-A)<=p*Math.max(1,Math.abs(a),Math.abs(A))&&Math.abs(l-M)<=p*Math.max(1,Math.abs(l),Math.abs(M))&&Math.abs(u-k)<=p*Math.max(1,Math.abs(u),Math.abs(k))&&Math.abs(c-I)<=p*Math.max(1,Math.abs(c),Math.abs(I))&&Math.abs(h-E)<=p*Math.max(1,Math.abs(h),Math.abs(E))&&Math.abs(f-T)<=p*Math.max(1,Math.abs(f),Math.abs(T))&&Math.abs(d-F)<=p*Math.max(1,Math.abs(d),Math.abs(F))&&Math.abs(y-P)<=p*Math.max(1,Math.abs(y),Math.abs(P))&&Math.abs(m-D)<=p*Math.max(1,Math.abs(m),Math.abs(D))&&Math.abs(g-z)<=p*Math.max(1,Math.abs(g),Math.abs(z))&&Math.abs(x-B)<=p*Math.max(1,Math.abs(x),Math.abs(B))},t.cl=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},t.cm=t=>"circle"===t.type,t.cn=t=>"heatmap"===t.type,t.co=t=>"line"===t.type,t.cp=t=>"fill"===t.type,t.cq=t=>"fill-extrusion"===t.type,t.cr=t=>"hillshade"===t.type,t.cs=t=>"color-relief"===t.type,t.ct=t=>"background"===t.type,t.cu=t=>"custom"===t.type,t.cv=C,t.cw=function(t,e,n){if(e<=0)return t;const r=1/e;return void 0===n||Math.abs(n)<1e-10?Math.round(t*r)/r:(n>0?Math.ceil(t*r-1e-9):Math.floor(t*r+1e-10))/r},t.cx=function(t,e,n){const r=E(e.x-n.x,e.y-n.y),i=E(t.x-n.x,t.y-n.y);var s,o;return nt(Math.atan2(r[0]*i[1]-r[1]*i[0],(s=r)[0]*(o=i)[0]+s[1]*o[1]))},t.cy=V,t.cz=function(t,e){var n;if(!it[e])return !1;const r=null==t?void 0:t.target,i=(null===(n=null==r?void 0:r.ownerDocument)||void 0===n?void 0:n.defaultView)||window;return t instanceof i.MouseEvent||t instanceof i.WheelEvent},t.d=P,t.d0=class{constructor(t,e){const n=(e=this.options=Object.assign({},sh,e)).debug;if(n&&console.time("preprocess data"),e.maxZoom<0||e.maxZoom>24)throw new Error("maxZoom should be in the 0-24 range");if(e.promoteId&&e.generateId)throw new Error("promoteId and generateId cannot be used together.");let r=pc(t,e);n&&(console.timeEnd("preprocess data"),console.log("index: maxZoom: %d, maxPoints: %d",e.indexMaxZoom,e.indexMaxPoints),console.time("generate tiles")),r=Lc(r,e),e.updateable&&(this.source=r),this.initializeIndex(r,e);}initializeIndex(t,e){this.tileIndex=e.cluster?new qc(e.clusterOptions):new rh(e),t.length&&this.tileIndex.initialize(t);}getTile(t,e,n){return e=+e,n=+n,(t=+t)<0||t>24?null:this.tileIndex.getTile(t,e,n)}updateData(t,e){const n=this.options;if(!n.updateable)throw new Error("to update tile geojson `updateable` option must be set to true");let{affected:r,source:i}=function(t,e,n){const r=function(t,e){return t?{removeAll:t.removeAll,remove:new Set(t.remove||[]),add:new Map(t.add?.map((t=>[e.promoteId?t.properties[e.promoteId]:t.id,t]))),update:new Map(t.update?.map((t=>[t.id,t])))}:{remove:new Set,add:new Map,update:new Map}}(e,n);let i=[];if(r.removeAll&&(i=t,t=[]),r.remove.size||r.add.size){const e=[];for(const n of t)(r.remove.has(n.id)||r.add.has(n.id))&&e.push(n);if(e.length){i.push(...e);const n=new Set(e.map((t=>t.id)));t=t.filter((t=>!n.has(t.id)));}if(r.add.size){let e=pc({type:"FeatureCollection",features:Array.from(r.add.values())},n);e=Lc(e,n),i.push(...e),t.push(...e);}}if(r.update.size){const e=new Map,s=[];for(const n of t)r.update.has(n.id)?e.set(n.id,[...e.get(n.id)||[],n]):s.push(n);for(const[t,o]of r.update){const r=e.get(t);if(!r||0===r.length)continue;const a=Nc(r,o,n);i.push(...r,...a),s.push(...a);}t=s;}return {affected:i,source:t}}(this.source,t,n);e&&({affected:r,source:i}=this.filterUpdate(i,r,e)),r.length&&(this.source=i,this.tileIndex.updateIndex(i,r,n));}filterUpdate(t,e,n){const r=new Set;for(const i of t)null!=i.id&&(n(xc(i))||(e.push(i),r.add(i.id)));return {affected:e,source:t=t.filter((t=>!r.has(t.id)))}}getData(){if(!this.options.updateable)throw new Error("to retrieve data the `updateable` option must be set to true");return {type:"FeatureCollection",features:this.source.map((t=>xc(t)))}}updateClusterOptions(t,e){const n=this.options.cluster;this.options.cluster=t,this.options.clusterOptions=e,n!=t?this.initializeIndex(this.source,this.options):this.tileIndex.updateIndex(this.source,[],this.options);}getClusterExpansionZoom(t){return this.tileIndex.getClusterExpansionZoom(t)}getClusterChildren(t){return this.tileIndex.getChildren(t)}getClusterLeaves(t,e,n){return this.tileIndex.getLeaves(t,e,n)}},t.d1=Bs,t.e=$,t.f=yt,t.g=ct,t.h=t=>e(void 0,void 0,void 0,(function*(){if(0===t.byteLength)return createImageBitmap(new ImageData(1,1));const e=new Blob([new Uint8Array(t)],{type:"image/png"});try{return createImageBitmap(e)}catch(t){throw new Error(`Could not load image because of ${P(t).message}. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.`)}})),t.i=Y,t.j=t=>new Promise(((e,n)=>{const r=new Image;r.onload=()=>{e(r),URL.revokeObjectURL(r.src),r.onload=null,window.requestAnimationFrame((()=>r.src=W));},r.onerror=()=>n(new Error("Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported."));const i=new Blob([new Uint8Array(t)],{type:"image/png"});r.src=t.byteLength?URL.createObjectURL(i):W;})),t.k=(t,e)=>dt($(t,{type:"json"}),e),t.l=vt,t.m=dt,t.n=xt,t.o=(t,e)=>dt($(t,{type:"arrayBuffer"}),e),t.p=ep,t.q=function(t){return new Rh(t).readFields(Jh,[])},t.r=function(t){return /[\u02EA\u02EB\u1100-\u11FF\u2E80-\u2FDF\u3000-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFE10-\uFE1F\uFE30-\uFE4F\uFF00-\uFFEF]|\uD81B[\uDFE0-\uDFFF]|[\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFFF]|\uD82C[\uDC00-\uDEFB]|\uD83C[\uDE00-\uDEFF]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79]/gim.test(String.fromCodePoint(t))},t.s=tt,t.t=kl,t.u=Ks,t.v=ss,t.w=G,t.x=wt,t.y=Rs,t.z=as;})); + +define("worker",["./shared"],(function(e){"use strict";class t{constructor(e,t){this.keyCache={},e&&this.replace(e,t);}replace(e,t){this._layerConfigs={},this._layers={},this.update(e,[],t);}update(t,i,s){for(const i of t){this._layerConfigs[i.id]=i;const t=this._layers[i.id]=e.bT(i,s);t._featureFilter=e.ah(t.filter,s),this.keyCache[i.id]&&delete this.keyCache[i.id];}for(const e of i)delete this.keyCache[e],delete this._layerConfigs[e],delete this._layers[e];this.familiesBySource={};const r=e.cJ(Object.values(this._layerConfigs),this.keyCache);for(const t of r){const i=t.map((e=>this._layers[e.id])),s=i[0];if(s.isHidden())continue;const r=s.source||"";let o=this.familiesBySource[r];o||(o=this.familiesBySource[r]={});const n=s.sourceLayer||e.ag;let a=o[n];a||(a=o[n]=[]),a.push(i);}}}class i{constructor(t){const i={},s=[];for(const e in t){const r=t[e],o=i[e]={};for(const e in r){const t=r[+e];if(!t||0===t.bitmap.width||0===t.bitmap.height)continue;const i={x:0,y:0,w:t.bitmap.width+2,h:t.bitmap.height+2};s.push(i),o[e]={rect:i,metrics:t.metrics};}}const{w:r,h:o}=e.p(s),n=new e.t({width:r||1,height:o||1});for(const s in t){const r=t[s];for(const t in r){const o=r[+t];if(!o||0===o.bitmap.width||0===o.bitmap.height)continue;const a=i[s][t].rect;e.t.copy(o.bitmap,n,{x:0,y:0},{x:a.x+1,y:a.y+1},o.bitmap);}}this.image=n,this.positions=i;}}e.cK("GlyphAtlas",i);class s{constructor(t){this.tileID=new e.a3(t.tileID.overscaledZ,t.tileID.wrap,t.tileID.canonical.z,t.tileID.canonical.x,t.tileID.canonical.y),this.uid=t.uid,this.zoom=t.zoom,this.pixelRatio=t.pixelRatio,this.tileSize=t.tileSize,this.source=t.source,this.overscaling=this.tileID.overscaleFactor(),this.showCollisionBoxes=t.showCollisionBoxes,this.collectResourceTiming=!!t.collectResourceTiming,this.returnDependencies=!!t.returnDependencies,this.promoteId=t.promoteId,this.inFlightDependencies=[];}parse(t,s,o,n,a){return e._(this,void 0,void 0,(function*(){this.status="parsing",this.data=t,this.collisionBoxArray=new e.ae;const l=new e.cL(Object.keys(t.layers).sort()),c=new e.cM(this.tileID,this.promoteId);c.bucketLayerIDs=[];const h={},d={featureIndex:c,iconDependencies:{},patternDependencies:{},glyphDependencies:{},dashDependencies:{},availableImages:o,subdivisionGranularity:a},u=s.familiesBySource[this.source];for(const i in u){const s=t.layers[i];if(!s)continue;1===s.version&&e.w(`Vector tile source "${this.source}" layer "${i}" does not use vector tile spec v2 and therefore may have some rendering errors.`);const n=l.encode(i),a=[];for(let e=0;ee.id))));}}const g=e.bY(d.glyphDependencies,(e=>Object.keys(e).map(Number)));for(const e of this.inFlightDependencies)null==e||e.abort();this.inFlightDependencies=[];let p=Promise.resolve({});if(Object.keys(g).length){const e=new AbortController;this.inFlightDependencies.push(e),p=n.sendAsync({type:"GG",data:{stacks:g,source:this.source,tileID:this.tileID,type:"glyphs"}},e);}const f=Object.keys(d.iconDependencies);let y=Promise.resolve({});if(f.length){const e=new AbortController;this.inFlightDependencies.push(e),y=n.sendAsync({type:"GI",data:{icons:f,source:this.source,tileID:this.tileID,type:"icons"}},e);}const v=Object.keys(d.patternDependencies);let m=Promise.resolve({});if(v.length){const e=new AbortController;this.inFlightDependencies.push(e),m=n.sendAsync({type:"GI",data:{icons:v,source:this.source,tileID:this.tileID,type:"patterns"}},e);}const _=d.dashDependencies;let S=Promise.resolve({});if(Object.keys(_).length){const e=new AbortController;this.inFlightDependencies.push(e),S=n.sendAsync({type:"GDA",data:{dashes:_}},e);}const[w,b,I,T]=yield Promise.all([p,y,m,S]),k=new i(w),x=new e.cN(b,I);for(const t in h){const i=h[t];i instanceof e.af?(r(i.layers,this.zoom,o),e.cO({bucket:i,glyphMap:w,glyphPositions:k.positions,imageMap:b,imagePositions:x.iconPositions,showCollisionBoxes:this.showCollisionBoxes,canonical:this.tileID.canonical,subdivisionGranularity:d.subdivisionGranularity})):i.hasDependencies&&(i instanceof e.cP||i instanceof e.cQ||i instanceof e.cR)&&(r(i.layers,this.zoom,o),i.addFeatures(d,this.tileID.canonical,x.patternPositions,T));}return this.status="done",{buckets:Object.values(h).filter((e=>!e.isEmpty())),featureIndex:c,collisionBoxArray:this.collisionBoxArray,glyphAtlasImage:k.image,imageAtlas:x,dashPositions:T,glyphMap:this.returnDependencies?w:null,iconMap:this.returnDependencies?b:null,glyphPositions:this.returnDependencies?k.positions:null}}))}}function r(t,i,s){const r=new e.J(i);for(const e of t)e.recalculate(r,s);}class o{constructor(){this.loading={},this.loaded={},this.parsing={};}startLoading(e,t){this.loading[e]=t;}finishLoading(e){delete this.loading[e];}abort(e){const t=this.loading[e];(null==t?void 0:t.abort)&&(t.abort.abort(),delete this.loading[e]);}setParsing(e,t){this.parsing[e]=t;}consumeParsing(e){const t=this.parsing[e];if(t)return delete this.parsing[e],t}clearParsing(e){delete this.parsing[e];}markLoaded(e,t){this.loaded[e]=t;}getLoaded(e){const t=this.loaded[e];if(t)return t}removeLoaded(e){delete this.loaded[e];}clearLoaded(){this.loaded={};}}class n{constructor(e){this.start=`${e}#start`,this.end=`${e}#end`,this.measure=e,performance.mark(this.start);}finish(){performance.mark(this.end);let e=performance.getEntriesByName(this.measure);return 0===e.length&&(performance.measure(this.measure,this.start,this.end),e=performance.getEntriesByName(this.measure),performance.clearMarks(this.start),performance.clearMarks(this.end),performance.clearMeasures(this.measure)),e}}class a{constructor(e,t,i,s,r){this.type=e,this.properties=i||{},this.extent=r,this.pointsArray=t,this.id=s;}loadGeometry(){return this.pointsArray.map((t=>t.map((t=>new e.P(t.x,t.y)))))}}class l{constructor(e,t,i){this.version=2,this._myFeatures=e,this.name=t,this.length=e.length,this.extent=i;}feature(e){return this._myFeatures[e]}}class c{constructor(){this.layers={};}addLayer(e){this.layers[e.name]=e;}}function h(t){let i=e.cS(t);return 0===i.byteOffset&&i.byteLength===i.buffer.byteLength||(i=new Uint8Array(i)),{vectorTile:t,rawData:i.buffer}}function d(t,i,s){const{extent:r}=t,o=Math.pow(2,s.z-i.z),n=(s.x-i.x*o)*r,c=(s.y-i.y*o)*r,h=[];for(let i=0;i0&&u.addLayer(r);}const p=h(u);return this.overzoomedTileResultCache.set(a,p),p}reloadTile(t){return e._(this,void 0,void 0,(function*(){const e=t.uid,i=this.tileState.getLoaded(e);if(!i)throw new Error("Should not be trying to reload a tile that was never loaded or has been removed");if(i.showCollisionBoxes=t.showCollisionBoxes,"parsing"===i.status){const s=this.tileState.consumeParsing(e);return yield this._parseWorkerTile(i,t,s)}if("done"===i.status&&i.vectorTile)return yield this._parseWorkerTile(i,t)}))}abortTile(t){return e._(this,void 0,void 0,(function*(){this.tileState.abort(t.uid);}))}removeTile(t){return e._(this,void 0,void 0,(function*(){this.tileState.removeLoaded(t.uid);}))}}class g{constructor(){this.loaded={};}loadTile(t){return e._(this,void 0,void 0,(function*(){const{uid:i,encoding:s,rawImageData:r,redFactor:o,greenFactor:n,blueFactor:a,baseShift:l}=t,c=r.width+2,h=r.height+2,d=e.b(r)?new e.R({width:c,height:h},yield e.cY(r,-1,-1,c,h)):r,u=new e.cZ(i,d,s,o,n,a,l);return this.loaded||(this.loaded={}),this.loaded[i]=u,u}))}removeTile(e){const t=this.loaded,i=e.uid;(null==t?void 0:t[i])&&delete t[i];}}class p{constructor(e,t,i,s=f){this.actor=e,this.layerIndex=t,this.availableImages=i,this.tileState=new o,this._createGeoJSONIndex=s;}loadVectorTile(t){if(!this._geoJSONIndex)throw new Error("Unable to parse the data into a cluster or geojson");const{z:i,x:s,y:r}=t.tileID.canonical,o=this._geoJSONIndex.getTile(i,s,r);return o?h(new e.c_(o.features,{version:2,extent:e.a6})):null}loadTile(t){return e._(this,void 0,void 0,(function*(){const{uid:e}=t,i=new s(t);i.abort=new AbortController;try{const s=this.loadVectorTile(t);if(!s)return null;const{vectorTile:r,rawData:o}=s;i.vectorTile=r,this.tileState.markLoaded(e,i);const n={rawData:o};this.tileState.setParsing(e,n);try{return yield this._parseWorkerTile(i,t,n)}finally{this.tileState.clearParsing(e);}}catch(t){throw i.status="done",this.tileState.markLoaded(e,i),t}}))}_reloadLoadedTile(t){return e._(this,void 0,void 0,(function*(){const e=t.uid,i=this.tileState.getLoaded(e);if(!i)throw new Error("Should not be trying to reload a tile that was never loaded or has been removed");if(i.showCollisionBoxes=t.showCollisionBoxes,"parsing"===i.status){const s=this.tileState.consumeParsing(e);return yield this._parseWorkerTile(i,t,s)}if("done"===i.status&&i.vectorTile)return yield this._parseWorkerTile(i,t)}))}_parseWorkerTile(t,i,s){return e._(this,void 0,void 0,(function*(){let r=yield t.parse(t.vectorTile,this.layerIndex,this.availableImages,this.actor,i.subdivisionGranularity);if(s){const{rawData:t}=s;r=e.e({rawTileData:t.slice(0),encoding:"mvt"},r);}return r}))}abortTile(t){return e._(this,void 0,void 0,(function*(){this.tileState.abort(t.uid);}))}removeTile(t){return e._(this,void 0,void 0,(function*(){this.tileState.removeLoaded(t.uid);}))}loadData(t){return e._(this,void 0,void 0,(function*(){var i;null===(i=this._pendingRequest)||void 0===i||i.abort();const s=this._startRequestTiming(t);this._pendingRequest=new AbortController;try{yield this.loadAndProcessGeoJSON(t,this._pendingRequest),delete this._pendingRequest,this.tileState.clearLoaded();const e={};return t.request&&(e.data=t.data),this._finishRequestTiming(s,t,e),e}catch(t){if(delete this._pendingRequest,!e.$(t))throw t;return {abandoned:!0}}}))}_startRequestTiming(e){var t;if(null===(t=e.request)||void 0===t?void 0:t.collectResourceTiming)return new n(e.request.url)}_finishRequestTiming(e,t,i){const s=null==e?void 0:e.finish();s&&(i.resourceTiming={[t.source]:JSON.parse(JSON.stringify(s))});}reloadTile(e){return this.tileState.getLoaded(e.uid)?this._reloadLoadedTile(e):this.loadTile(e)}loadAndProcessGeoJSON(t,i){return e._(this,void 0,void 0,(function*(){var s;if(t.request&&(t.data=(yield e.k(t.request,i)).data),t.data)return t.data=this._filterGeoJSON(t.data,t.filter),void(this._geoJSONIndex=this._createGeoJSONIndex(t.data,t));if(t.dataDiff)return null!==(s=this._geoJSONIndex)&&void 0!==s||(this._geoJSONIndex=this._createGeoJSONIndex({type:"FeatureCollection",features:[]},t)),void this._geoJSONIndex.updateData(t.dataDiff,this._getFilterPredicate(t.filter));if(t.updateCluster&&this._geoJSONIndex.updateClusterOptions(t.geojsonVtOptions.cluster,y(t)),null==this._geoJSONIndex)throw new Error(`Input data given to '${t.source}' is not a valid GeoJSON object.`)}))}_filterGeoJSON(e,t){if("FeatureCollection"!==e.type)return e;const i=this._getFilterPredicate(t);return i?{type:"FeatureCollection",features:e.features.filter((e=>i(e)))}:e}_getFilterPredicate(t){if("boolean"!=typeof t&&!(null==t?void 0:t.length))return;const i=e.c$(t,{type:"boolean","property-type":"data-driven",overridable:!1,transition:!1});if("error"===i.result)throw new Error(i.value.map((e=>`${e.key}: ${e.message}`)).join(", "));return e=>i.value.evaluate({zoom:0},e)}removeSource(t){return e._(this,void 0,void 0,(function*(){var e;null===(e=this._pendingRequest)||void 0===e||e.abort();}))}getClusterExpansionZoom(e){return this._geoJSONIndex.getClusterExpansionZoom(e.clusterId)}getClusterChildren(e){return this._geoJSONIndex.getClusterChildren(e.clusterId)}getClusterLeaves(e){return this._geoJSONIndex.getClusterLeaves(e.clusterId,e.limit,e.offset)}}function f(t,i){const s=e.e(i.geojsonVtOptions||{},{updateable:!0,clusterOptions:y(i)});return new e.d0(t,s)}function y({geojsonVtOptions:t,clusterProperties:i}){if(!i||!t.clusterOptions)return t.clusterOptions;const s={},r={},o={accumulated:null,zoom:0},n={properties:null},a=Object.keys(i);for(const t of a){const[o,n]=i[t],a=e.c$(n),l=e.c$("string"==typeof o?[o,["accumulated"],["get",t]]:o);s[t]=a.value,r[t]=l.value;}return t.clusterOptions.map=e=>{n.properties=e;const t={};for(const e of a)t[e]=s[e].evaluate(o,n);return t},t.clusterOptions.reduce=(e,t)=>{n.properties=t;for(const t of a)o.accumulated=e[t],e[t]=r[t].evaluate(o,n);},t.clusterOptions}class v{constructor(t){this.self=t,this.actor=new e.N(t),this.layerIndexes={},this.availableImages={},this.workerSources={},this.demWorkerSources={},this.externalWorkerSourceTypes={},this.globalStates=new Map,this.self.registerWorkerSource=(e,t)=>{if(this.externalWorkerSourceTypes[e])throw new Error(`Worker source with name "${e}" already registered.`);this.externalWorkerSourceTypes[e]=t;},this.self.addProtocol=e.cH,this.self.removeProtocol=e.cI,this.self.registerRTLTextPlugin=t=>{e.d1.setMethods(t);},this.self.makeRequest=e.m,this.actor.registerMessageHandler("LDT",((e,t)=>this._getDEMWorkerSource(e,t.source).loadTile(t))),this.actor.registerMessageHandler("RDT",((t,i)=>e._(this,void 0,void 0,(function*(){this._getDEMWorkerSource(t,i.source).removeTile(i);})))),this.actor.registerMessageHandler("GCEZ",((t,i)=>e._(this,void 0,void 0,(function*(){return this._getWorkerSource(t,i.type,i.source).getClusterExpansionZoom(i)})))),this.actor.registerMessageHandler("GCC",((t,i)=>e._(this,void 0,void 0,(function*(){return this._getWorkerSource(t,i.type,i.source).getClusterChildren(i)})))),this.actor.registerMessageHandler("GCL",((t,i)=>e._(this,void 0,void 0,(function*(){return this._getWorkerSource(t,i.type,i.source).getClusterLeaves(i)})))),this.actor.registerMessageHandler("LD",((e,t)=>this._getWorkerSource(e,t.type,t.source).loadData(t))),this.actor.registerMessageHandler("LT",((e,t)=>this._getWorkerSource(e,t.type,t.source).loadTile(t))),this.actor.registerMessageHandler("RT",((e,t)=>this._getWorkerSource(e,t.type,t.source).reloadTile(t))),this.actor.registerMessageHandler("AT",((e,t)=>this._getWorkerSource(e,t.type,t.source).abortTile(t))),this.actor.registerMessageHandler("RMT",((e,t)=>this._getWorkerSource(e,t.type,t.source).removeTile(t))),this.actor.registerMessageHandler("RS",((t,i)=>e._(this,void 0,void 0,(function*(){var e,s;if(!(null===(s=null===(e=this.workerSources[t])||void 0===e?void 0:e[i.type])||void 0===s?void 0:s[i.source]))return;const r=this.workerSources[t][i.type][i.source];delete this.workerSources[t][i.type][i.source],void 0!==r.removeSource&&r.removeSource(i);})))),this.actor.registerMessageHandler("RM",(t=>e._(this,void 0,void 0,(function*(){delete this.layerIndexes[t],delete this.availableImages[t],delete this.workerSources[t],delete this.demWorkerSources[t],this.globalStates.delete(t);})))),this.actor.registerMessageHandler("SR",((t,i)=>e._(this,void 0,void 0,(function*(){this.referrer=i;})))),this.actor.registerMessageHandler("SRPS",((e,t)=>this._syncRTLPluginState(e,t))),this.actor.registerMessageHandler("IS",((t,i)=>e._(this,void 0,void 0,(function*(){this.self.importScripts(i);})))),this.actor.registerMessageHandler("SI",((e,t)=>this._setImages(e,t))),this.actor.registerMessageHandler("UL",((t,i)=>e._(this,void 0,void 0,(function*(){this._getLayerIndex(t).update(i.layers,i.removedIds,this._getGlobalState(t));})))),this.actor.registerMessageHandler("UGS",((t,i)=>e._(this,void 0,void 0,(function*(){const e=this._getGlobalState(t);for(const t in i)e[t]=i[t];})))),this.actor.registerMessageHandler("SL",((t,i)=>e._(this,void 0,void 0,(function*(){this._getLayerIndex(t).replace(i,this._getGlobalState(t));}))));}_getGlobalState(e){let t=this.globalStates.get(e);return t||(t={},this.globalStates.set(e,t)),t}_setImages(t,i){return e._(this,void 0,void 0,(function*(){this.availableImages[t]=i;for(const e in this.workerSources[t]){const s=this.workerSources[t][e];for(const e in s)s[e].availableImages=i;}}))}_syncRTLPluginState(t,i){return e._(this,void 0,void 0,(function*(){return yield e.d1.syncState(i,this.self.importScripts)}))}_getAvailableImages(e){let t=this.availableImages[e];return t||(t=[]),t}_getLayerIndex(e){let i=this.layerIndexes[e];return i||(i=this.layerIndexes[e]=new t),i}_getWorkerSource(e,t,i){var s,r;if((s=this.workerSources)[e]||(s[e]={}),(r=this.workerSources[e])[t]||(r[t]={}),!this.workerSources[e][t][i]){const s={sendAsync:(t,i)=>(t.targetMapId=e,this.actor.sendAsync(t,i))};switch(t){case "vector":this.workerSources[e][t][i]=new u(s,this._getLayerIndex(e),this._getAvailableImages(e));break;case "geojson":this.workerSources[e][t][i]=new p(s,this._getLayerIndex(e),this._getAvailableImages(e));break;default:this.workerSources[e][t][i]=new this.externalWorkerSourceTypes[t](s,this._getLayerIndex(e),this._getAvailableImages(e));}}return this.workerSources[e][t][i]}_getDEMWorkerSource(e,t){var i,s;return (i=this.demWorkerSources)[e]||(i[e]={}),(s=this.demWorkerSources[e])[t]||(s[t]=new g),this.demWorkerSources[e][t]}}return e.i(self)&&(self.worker=new v(self)),v})); + +define("index",["exports","./shared"],(function(e,t){"use strict";var i="5.24.0";function o(){var e=new t.A(4);return t.A!=Float32Array&&(e[1]=0,e[2]=0),e[0]=1,e[3]=1,e}let a,r,s;const n={frame(e,i,o,a){const r=a||window,s=r.requestAnimationFrame((e=>{n(),i(e);})),{unsubscribe:n}=t.s(e.signal,"abort",(()=>{n(),r.cancelAnimationFrame(s),o(new t.a(e.signal.reason));}),!1);},frameAsync(e,t){return new Promise(((i,o)=>{this.frame(e,i,o,t);}))},getImageData(e,t=0){return this.getImageCanvasContext(e).getImageData(-t,-t,e.width+2*t,e.height+2*t)},getImageCanvasContext(e){const t=window.document.createElement("canvas"),i=t.getContext("2d",{willReadFrequently:!0});if(!i)throw new Error("failed to create canvas 2d context");return t.width=e.width,t.height=e.height,i.drawImage(e,0,0,e.width,e.height),i},resolveURL:e=>(a||(a=document.createElement("a")),a.href=e,a.href),hardwareConcurrency:"undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,get prefersReducedMotion(){return void 0!==s?s:!!matchMedia&&(null!=r||(r=matchMedia("(prefers-reduced-motion: reduce)")),r.matches)},set prefersReducedMotion(e){s=e;}},l=new class{constructor(){this._frozenAt=null;}getCurrentTime(){return null!==this._frozenAt?this._frozenAt:performance.now()}setNow(e){this._frozenAt=e;}restoreNow(){this._frozenAt=null;}isFrozen(){return null!==this._frozenAt}};function c(){return l.getCurrentTime()}var h,u;class d{static create(e,t,i){const o=window.document.createElement(e);return void 0!==t&&(o.className=t),i&&i.appendChild(o),o}static createNS(e,t){return window.document.createElementNS(e,t)}static disableDrag(){d.docStyle&&d.selectProp&&(d.userSelect=d.docStyle[d.selectProp],d.docStyle[d.selectProp]="none");}static enableDrag(){d.docStyle&&d.selectProp&&(d.docStyle[d.selectProp]=d.userSelect);}static suppressClickInternal(e){e.preventDefault(),e.stopPropagation(),window.removeEventListener("click",d.suppressClickInternal,!0);}static suppressClick(){window.addEventListener("click",d.suppressClickInternal,!0),window.setTimeout((()=>{window.removeEventListener("click",d.suppressClickInternal,!0);}),0);}static getScale(e){const t=e.getBoundingClientRect();return {x:t.width/e.offsetWidth||1,y:t.height/e.offsetHeight||1,boundingClientRect:t}}static getPoint(e,i,o){const a=i.boundingClientRect;return new t.P((o.clientX-a.left)/i.x-e.clientLeft,(o.clientY-a.top)/i.y-e.clientTop)}static mousePos(e,t){const i=d.getScale(e);return d.getPoint(e,i,t)}static touchPos(e,t){const i=[],o=d.getScale(e);for(const a of t)i.push(d.getPoint(e,o,a));return i}static sanitize(e){const t=(new DOMParser).parseFromString(e,"text/html").body||document.createElement("body"),i=t.querySelectorAll("script");for(const e of i)e.remove();return d.clean(t),t.innerHTML}static isPossiblyDangerous(e,t){const i=t.replace(/\s+/g,"").toLowerCase();return !(!["src","href","xlink:href"].includes(e)||!i.includes("javascript:")&&!i.includes("data:"))||!!e.startsWith("on")||void 0}static clean(e){const t=e.children;for(const e of t)d.removeAttributes(e),d.clean(e);}static removeAttributes(e){for(const{name:t,value:i}of e.attributes)d.isPossiblyDangerous(t,i)&&e.removeAttribute(t);}}d.docStyle="undefined"!=typeof window&&(null===(h=window.document)||void 0===h?void 0:h.documentElement.style),d.selectProp=!d.docStyle||"userSelect"in d.docStyle?"userSelect":"webkitUserSelect",function(e){let i,o,a,r;e.resetRequestQueue=()=>{i=[],o=0,a=0,r={};},e.addThrottleControl=e=>{const t=a++;return r[t]=e,t},e.removeThrottleControl=e=>{delete r[e],n();},e.getImage=(e,o,a=!0)=>new Promise(((r,s)=>{e.headers||(e.headers={}),e.headers.accept="image/webp,*/*",t.e(e,{type:"image"}),i.push({abortController:o,requestParameters:e,supportImageRefresh:a,state:"queued",onError:e=>{s(e);},onSuccess:e=>{r(e);}}),n();}));const s=e=>t._(this,void 0,void 0,(function*(){e.state="running";const{requestParameters:i,supportImageRefresh:a,onError:r,onSuccess:s,abortController:c}=e,h=!1===a&&!t.i(self)&&!t.g(i.url)&&(!i.headers||Object.keys(i.headers).reduce(((e,t)=>e&&"accept"===t),!0));o++;const u=h?l(i,c):t.m(i,c);try{const i=yield u;delete e.abortController,e.state="completed",i.data instanceof HTMLImageElement||t.b(i.data)?s(i):i.data&&s({data:yield(d=i.data,"function"==typeof createImageBitmap?t.h(d):t.j(d)),cacheControl:i.cacheControl,expires:i.expires});}catch(i){delete e.abortController,r(t.d(i));}finally{o--,n();}var d;})),n=()=>{const e=(()=>{for(const e of Object.keys(r))if(r[e]())return !0;return !1})()?t.c.MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME:t.c.MAX_PARALLEL_IMAGE_REQUESTS;for(let t=o;t0;t++){const e=i.shift();e.abortController.signal.aborted?t--:s(e);}},l=(e,i)=>new Promise(((o,a)=>{const r=new Image,s=e.url,n=e.credentials;n&&"include"===n?r.crossOrigin="use-credentials":(n&&"same-origin"===n||!t.f(s))&&(r.crossOrigin="anonymous"),i.signal.addEventListener("abort",(()=>{r.src="",a(new t.a(i.signal.reason));})),r.fetchPriority="high",r.onload=()=>{r.onerror=r.onload=null,o({data:r});},r.onerror=()=>{r.onerror=r.onload=null,i.signal.aborted||a(new Error("Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported."));},r.src=s;}));}(u||(u={})),u.resetRequestQueue();class _{constructor(e){this._transformRequestFn=null!=e?e:null;}transformRequest(e,t){return this._transformRequestFn&&this._transformRequestFn(e,t)||{url:e}}setTransformRequest(e){this._transformRequestFn=e;}}function p(e){const t=[];if("string"==typeof e)t.push({id:"default",url:e});else if(e&&e.length>0){const i=[];for(const{id:o,url:a}of e){const e=`${o}${a}`;i.includes(e)||(i.push(e),t.push({id:o,url:a}));}}return t}function m(e,t,i){try{const o=new URL(e);return o.pathname+=`${t}${i}`,o.toString()}catch(t){throw new Error(`Invalid sprite URL "${e}", must be absolute. Modify style specification directly or use TransformStyleFunction to correct the issue dynamically`)}}function f(e){const{userImage:t}=e;return !(!(null==t?void 0:t.render)||!t.render()||(e.data.replace(new Uint8Array(t.data.buffer)),0))}class g extends t.E{constructor(){super(),this.images={},this.updatedImages={},this.callbackDispatchedThisFrame={},this.loaded=!1,this.requestors=[],this.patterns={},this.atlasImage=new t.R({width:1,height:1}),this.dirty=!0;}destroy(){this.atlasTexture&&(this.atlasTexture.destroy(),this.atlasTexture=null);for(const e of Object.keys(this.images))this.removeImage(e);this.patterns={},this.atlasImage=new t.R({width:1,height:1}),this.dirty=!0;}isLoaded(){return this.loaded}setLoaded(e){if(this.loaded!==e&&(this.loaded=e,e)){for(const{ids:e,promiseResolve:t}of this.requestors)t(this._getImagesForIds(e));this.requestors=[];}}getImage(e){const i=this.images[e];if(i&&!i.data&&i.spriteData){const e=i.spriteData;i.data=new t.R({width:e.width,height:e.height},e.context.getImageData(e.x,e.y,e.width,e.height).data),i.spriteData=null;}return i}addImage(e,t){if(this.images[e])throw new Error(`Image id ${e} already exist, use updateImage instead`);this._validate(e,t)&&(this.images[e]=t);}_validate(e,i){let o=!0;const a=i.data||i.spriteData;return this._validateStretch(i.stretchX,null==a?void 0:a.width)||(this.fire(new t.l(new Error(`Image "${e}" has invalid "stretchX" value`))),o=!1),this._validateStretch(i.stretchY,null==a?void 0:a.height)||(this.fire(new t.l(new Error(`Image "${e}" has invalid "stretchY" value`))),o=!1),this._validateContent(i.content,i)||(this.fire(new t.l(new Error(`Image "${e}" has invalid "content" value`))),o=!1),o}_validateStretch(e,t){if(!e)return !0;let i=0;for(const o of e){if(o[0]=e[1]))}updateImage(e,t,i=!0){const o=this.getImage(e);if(i&&(o.data.width!==t.data.width||o.data.height!==t.data.height))throw new Error(`size mismatch between old image (${o.data.width}x${o.data.height}) and new image (${t.data.width}x${t.data.height}).`);t.version=o.version+1,this.images[e]=t,this.updatedImages[e]=!0;}removeImage(e){var t;const i=this.images[e];delete this.images[e],delete this.patterns[e],(null===(t=i.userImage)||void 0===t?void 0:t.onRemove)&&i.userImage.onRemove();}listImages(){return Object.keys(this.images)}getImages(e){return new Promise(((t,i)=>{let o=!0;if(!this.isLoaded())for(const t of e)this.images[t]||(o=!1);this.isLoaded()||o?t(this._getImagesForIds(e)):this.requestors.push({ids:e,promiseResolve:t});}))}_getImagesForIds(e){var i;const o={};for(const a of e){let e=this.getImage(a);e||(this.fire(new t.n("styleimagemissing",{id:a})),e=this.getImage(a)),e?o[a]={data:e.data.clone(),pixelRatio:e.pixelRatio,sdf:e.sdf,version:e.version,stretchX:e.stretchX,stretchY:e.stretchY,content:e.content,textFitWidth:e.textFitWidth,textFitHeight:e.textFitHeight,hasRenderCallback:Boolean(null===(i=e.userImage)||void 0===i?void 0:i.render)}:t.w(`Image "${a}" could not be loaded. Please make sure you have added the image with map.addImage() or a "sprite" property in your style. You can provide missing images by listening for the "styleimagemissing" map event.`);}return o}getPixelSize(){const{width:e,height:t}=this.atlasImage;return {width:e,height:t}}getPattern(e){const i=this.patterns[e],o=this.getImage(e);if(!o)return null;if(i&&i.position.version===o.version)return i.position;if(i)i.position.version=o.version;else {const i={w:o.data.width+2,h:o.data.height+2,x:0,y:0},a=new t.I(i,o);this.patterns[e]={bin:i,position:a};}return this._updatePatternAtlas(),this.patterns[e].position}bind(e){const i=e.gl;this.atlasTexture?this.dirty&&(this.atlasTexture.update(this.atlasImage),this.dirty=!1):this.atlasTexture=new t.T(e,this.atlasImage,i.RGBA),this.atlasTexture.bind(i.LINEAR,i.CLAMP_TO_EDGE);}_updatePatternAtlas(){const e=[];for(const t in this.patterns)e.push(this.patterns[t].bin);const{w:i,h:o}=t.p(e),a=this.atlasImage;a.resize({width:i||1,height:o||1});for(const e in this.patterns){const{bin:i}=this.patterns[e],o=i.x+1,r=i.y+1,s=this.getImage(e).data,n=s.width,l=s.height;t.R.copy(s,a,{x:0,y:0},{x:o,y:r},{width:n,height:l}),t.R.copy(s,a,{x:0,y:l-1},{x:o,y:r-1},{width:n,height:1}),t.R.copy(s,a,{x:0,y:0},{x:o,y:r+l},{width:n,height:1}),t.R.copy(s,a,{x:n-1,y:0},{x:o-1,y:r},{width:1,height:l}),t.R.copy(s,a,{x:0,y:0},{x:o+n,y:r},{width:1,height:l});}this.dirty=!0;}beginFrame(){this.callbackDispatchedThisFrame={};}dispatchRenderCallbacks(e){for(const i of e){if(this.callbackDispatchedThisFrame[i])continue;this.callbackDispatchedThisFrame[i]=!0;const e=this.getImage(i);e||t.w(`Image with ID: "${i}" was not found`),f(e)&&this.updateImage(i,e);}}cloneImages(){const e={};for(const t in this.images){const i=this.images[t];e[t]=Object.assign(Object.assign({},i),{data:i.data?i.data.clone():null});}return e}}const v=1e20,x=new Float64Array(256);for(let e=0;e<256;e++){const t=.5-Math.pow(e/255,1/2.2);x[e]=t*Math.abs(t);}function b(e,t,i,o,a,r,s,n,l){for(let c=t;c-1);l++,r[l]=n,s[l]=c,s[l+1]=v;}for(let n=0,l=0;n/[-\w]+/.test(e)?e:`'${CSS.escape(e)}'`)).join(",");return new T.TinySDF({fontSize:48,buffer:6,radius:16,cutoff:.25,fontFamily:i,fontWeight:this._fontWeight(t[0]),fontStyle:this._fontStyle(t[0]),lang:this.lang})}_fontStyle(e){return /italic/i.test(e)?"italic":/oblique/i.test(e)?"oblique":"normal"}_fontWeight(e){const t={thin:100,hairline:100,"extra light":200,"ultra light":200,light:300,normal:400,regular:400,medium:500,semibold:600,demibold:600,bold:700,"extra bold":800,"ultra bold":800,black:900,heavy:900,"extra black":950,"ultra black":950};let i;for(const[o,a]of Object.entries(t))new RegExp(`\\b${o}\\b`,"i").test(e)&&(i=`${a}`);return i}destroy(){for(const e in this.entries){const t=this.entries[e];t.tinySDF=null,t.ideographTinySDF=null,t.glyphs={},t.requests={},t.ranges={};}this.entries={};}}T.loadGlyphRange=function(e,i,o,a){return t._(this,void 0,void 0,(function*(){const r=256*i,s=r+255,n=yield a.transformRequest(o.replace("{fontstack}",e).replace("{range}",`${r}-${s}`),"Glyphs"),l=yield t.o(n,new AbortController);if(!(null==l?void 0:l.data))throw new Error(`Could not load glyph range. range: ${i}, ${r}-${s}`);const c={};for(const e of t.q(l.data))c[e.id]=e;return c}))},T.TinySDF=class{constructor({fontSize:e=24,buffer:t=3,radius:i=8,cutoff:o=.25,fontFamily:a="sans-serif",fontWeight:r="normal",fontStyle:s="normal",lang:n=null}={}){this.buffer=t,this.radius=i,this.cutoff=o,this.lang=n;const l=this.size=e+4*t,c=this._createCanvas(l),h=this.ctx=c.getContext("2d",{willReadFrequently:!0});h.font=`${s} ${r} ${e}px ${a}`,h.textBaseline="alphabetic",h.textAlign="left",h.fillStyle="black",this.gridOuter=new Float64Array(l*l),this.gridInner=new Float64Array(l*l),this.f=new Float64Array(l),this.z=new Float64Array(l+1),this.v=new Uint16Array(l);}_createCanvas(e){if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,e);const t=document.createElement("canvas");return t.width=t.height=e,t}draw(e){const{width:t,actualBoundingBoxAscent:i,actualBoundingBoxDescent:o,actualBoundingBoxLeft:a,actualBoundingBoxRight:r}=this.ctx.measureText(e),s=Math.ceil(i),n=Math.floor(a),l=Math.max(0,Math.min(this.size-this.buffer,Math.ceil(r)-n)),c=Math.max(0,Math.min(this.size-this.buffer,s+Math.ceil(o))),h=l+2*this.buffer,u=c+2*this.buffer,d=Math.max(h*u,0),_=new Uint8ClampedArray(d),p={data:_,width:h,height:u,glyphWidth:l,glyphHeight:c,glyphTop:s,glyphLeft:n,glyphAdvance:t};if(0===l||0===c)return p;const{ctx:m,buffer:f,gridInner:g,gridOuter:y}=this;this.lang&&(m.lang=this.lang),m.clearRect(f,f,l,c),m.fillText(e,f-n,f+s);const w=m.getImageData(f,f,l,c);y.fill(v,0,d),g.fill(0,0,d);let T=3;for(let e=0;e1&&(s=e[++r]);const l=Math.abs(n-s.left),c=Math.abs(n-s.right),h=Math.min(l,c);let u;const d=t/i*(o+1);if(s.isDash){const e=o-Math.abs(d);u=Math.sqrt(h*h+e*e);}else u=o-Math.sqrt(h*h+d*d);this.data[a+n]=Math.max(0,Math.min(255,u+128));}}}addRegularDash(e){for(let t=e.length-1;t>=0;--t){const i=e[t],o=e[t+1];i.zeroLength?e.splice(t,1):o&&o.isDash===i.isDash&&(o.left=i.left,e.splice(t,1));}const t=e[0],i=e[e.length-1];t.isDash===i.isDash&&(t.left=i.left-this.width,i.right=t.right+this.width);const o=this.width*this.nextRow;let a=0,r=e[a];for(let t=0;t1&&(r=e[++a]);const i=Math.abs(t-r.left),s=Math.abs(t-r.right),n=Math.min(i,s);this.data[o+t]=Math.max(0,Math.min(255,(r.isDash?n:-n)+128));}}addDash(e,i){const o=i?7:0,a=2*o+1;if(this.nextRow+a>this.height)return t.w("LineAtlas out of space"),null;let r=0;for(const t of e)r+=t;if(0!==r){const t=this.width/r,a=this.getDashRanges(e,this.width,t);i?this.addRoundDash(a,t,o):this.addRegularDash(a);}const s={y:this.nextRow+o,height:2*o,width:r};return this.nextRow+=a,this.dirty=!0,s}bind(e){const t=e.gl;this.texture?(t.bindTexture(t.TEXTURE_2D,this.texture),this.dirty&&(this.dirty=!1,t.texSubImage2D(t.TEXTURE_2D,0,0,0,this.width,this.height,t.ALPHA,t.UNSIGNED_BYTE,this.data))):(this.texture=t.createTexture(),t.bindTexture(t.TEXTURE_2D,this.texture),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.REPEAT),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.REPEAT),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR),t.texImage2D(t.TEXTURE_2D,0,t.ALPHA,this.width,this.height,0,t.ALPHA,t.UNSIGNED_BYTE,this.data));}}const z="maplibre_preloaded_worker_pool";class R{constructor(){this.active={};}acquire(e){if(!this.workers)for(this.workers=[];this.workers.lengtht.m(i,o)))),L}function O(e,i){const o=t.O();return t.Q(o,o,[1,1,0]),t.S(o,o,[.5*e.width,.5*e.height,1]),e.calculatePosMatrix?t.U(o,o,e.calculatePosMatrix(i.toUnwrapped())):o}function j(e,t,i,o,a,r,s){var n;const l=function(e,t,i){if(e)for(const o of e){const e=t[o];if((null==e?void 0:e.source)===i&&"fill-extrusion"===e.type)return !0}else for(const e in t){const o=t[e];if(o.source===i&&"fill-extrusion"===o.type)return !0}return !1}(null!==(n=null==a?void 0:a.layers)&&void 0!==n?n:null,t,e.id),c=r.maxPitchScaleFactor(),h=e.tilesIn(o,c,l);h.sort(N);const u=[];for(const o of h)u.push({wrappedTileID:o.tileID.wrapped().key,queryResults:o.tile.queryRenderedFeatures(t,i,e.getState(),o.queryGeometry,o.cameraQueryGeometry,o.scale,a,r,c,O(r,o.tileID),s?(e,t)=>s(o.tileID,e,t):void 0)});return function(e,t){for(const i in e)for(const o of e[i])Z(o,t);return e}(function(e){const t={},i={};for(const{queryResults:o,wrappedTileID:a}of e){i[a]||(i[a]={});const e=i[a];for(const i in o){const a=o[i];e[i]||(e[i]={});const r=e[i];t[i]||(t[i]=[]);for(const e of a)r[e.featureIndex]||(r[e.featureIndex]=!0,t[i].push(e));}}return t}(u),e)}function N(e,t){const i=e.tileID,o=t.tileID;return i.overscaledZ-o.overscaledZ||i.canonical.y-o.canonical.y||i.wrap-o.wrap||i.canonical.x-o.canonical.x}function Z(e,t){const i=e.feature,o=t.getFeatureState(i.layer["source-layer"],i.id);i.source=i.layer.source,i.layer["source-layer"]&&(i.sourceLayer=i.layer["source-layer"]),i.state=o;}function U(e,i,o,a){return t._(this,void 0,void 0,(function*(){let r=e;if(e.url?r=(yield t.k(yield i.transformRequest(e.url,"Source"),o)).data:yield n.frameAsync(o,a),!r)return null;const s=t.V(t.e(r,e),["tiles","minzoom","maxzoom","attribution","bounds","scheme","tileSize","encoding"]);return "vector_layers"in r&&r.vector_layers&&(s.vectorLayerIds=r.vector_layers.map((e=>e.id))),s}))}class G{constructor(e,t){e&&(t?this.setSouthWest(e).setNorthEast(t):Array.isArray(e)&&(4===e.length?this.setSouthWest([e[0],e[1]]).setNorthEast([e[2],e[3]]):this.setSouthWest(e[0]).setNorthEast(e[1])));}setNorthEast(e){return this._ne=e instanceof t.W?new t.W(e.lng,e.lat):t.W.convert(e),this}setSouthWest(e){return this._sw=e instanceof t.W?new t.W(e.lng,e.lat):t.W.convert(e),this}extend(e){const i=this._sw,o=this._ne;let a,r;if(e instanceof t.W)a=e,r=e;else {if(!(e instanceof G))return Array.isArray(e)?4===e.length||e.every(Array.isArray)?this.extend(G.convert(e)):this.extend(t.W.convert(e)):e&&("lng"in e||"lon"in e)&&"lat"in e?this.extend(t.W.convert(e)):this;if(a=e._sw,r=e._ne,!a||!r)return this}return i||o?(i.lng=Math.min(a.lng,i.lng),i.lat=Math.min(a.lat,i.lat),o.lng=Math.max(r.lng,o.lng),o.lat=Math.max(r.lat,o.lat)):(this._sw=new t.W(a.lng,a.lat),this._ne=new t.W(r.lng,r.lat)),this}getCenter(){return new t.W((this._sw.lng+this._ne.lng)/2,(this._sw.lat+this._ne.lat)/2)}getSouthWest(){return this._sw}getNorthEast(){return this._ne}getNorthWest(){return new t.W(this.getWest(),this.getNorth())}getSouthEast(){return new t.W(this.getEast(),this.getSouth())}getWest(){return this._sw.lng}getSouth(){return this._sw.lat}getEast(){return this._ne.lng}getNorth(){return this._ne.lat}toArray(){return [this._sw.toArray(),this._ne.toArray()]}toString(){return `LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`}isEmpty(){return !(this._sw&&this._ne)}contains(e){const{lng:i,lat:o}=t.W.convert(e);let a=this._sw.lng<=i&&i<=this._ne.lng;return this._sw.lng>this._ne.lng&&(a=this._sw.lng>=i&&i>=this._ne.lng),this._sw.lat<=o&&o<=this._ne.lat&&a}intersects(e){if(!((e=G.convert(e)).getNorth()>=this.getSouth()&&e.getSouth()<=this.getNorth()))return !1;const i=Math.abs(this.getEast()-this.getWest()),o=Math.abs(e.getEast()-e.getWest());if(i>=360||o>=360)return !0;const a=t.X(this.getWest(),-180,180),r=t.X(this.getEast(),-180,180),s=t.X(e.getWest(),-180,180),n=t.X(e.getEast(),-180,180),l=a>r,c=s>n;return !(!l||!c)||(l?n>=a||s<=r:c?r>=s||a<=n:s<=r&&n>=a)}static convert(e){return e instanceof G?e:e?new G(e):e}static fromLngLat(e,i=0){const o=360*i/40075017,a=o/Math.cos(Math.PI/180*e.lat);return new G(new t.W(e.lng-a,e.lat-o),new t.W(e.lng+a,e.lat+o))}adjustAntiMeridian(){const e=new t.W(this._sw.lng,this._sw.lat),i=new t.W(this._ne.lng,this._ne.lat);return new G(e,e.lng>i.lng?new t.W(i.lng+360,i.lat):i)}}class V{constructor(e,t,i){this.bounds=G.convert(this.validateBounds(e)),this.minzoom=t||0,this.maxzoom=i||24;}validateBounds(e){return Array.isArray(e)&&4===e.length?[Math.max(-180,e[0]),Math.max(-90,e[1]),Math.min(180,e[2]),Math.min(90,e[3])]:[-180,-90,180,90]}contains(e){const i=Math.pow(2,e.z),o=Math.floor(t.Z(this.bounds.getWest())*i),a=Math.floor(t.Y(this.bounds.getNorth())*i),r=Math.ceil(t.Z(this.bounds.getEast())*i),s=Math.ceil(t.Y(this.bounds.getSouth())*i);return e.x>=o&&e.x=a&&e.y{this._options.tiles=e;})),this}setUrl(e){return this.setSourceProperty((()=>{this.url=e,this._options.url=e;})),this}onRemove(){this._tileJSONRequest&&(this._tileJSONRequest.abort(),this._tileJSONRequest=null);}serialize(){return t.e({},this._options)}loadTile(e){return t._(this,void 0,void 0,(function*(){const t=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme),i={request:yield this.map._requestManager.transformRequest(t,"Tile"),uid:e.uid,tileID:e.tileID,zoom:e.tileID.overscaledZ,tileSize:this.tileSize*e.tileID.overscaleFactor(),type:this.type,source:this.id,pixelRatio:this.map.getPixelRatio(),showCollisionBoxes:this.map.showCollisionBoxes,promoteId:this.promoteId,subdivisionGranularity:this.map.style.projection.subdivisionGranularity,encoding:this.encoding,overzoomParameters:yield this._getOverzoomParameters(e),etag:e.etag};i.request.collectResourceTiming=this._collectResourceTiming;let o="RT";if(e.actor&&"expired"!==e.state){if("loading"===e.state)return new Promise(((t,i)=>{e.reloadPromise={resolve:t,reject:i};}))}else e.actor=this.dispatcher.getActor(),o="LT";e.abortController=new AbortController;try{const t=yield e.actor.sendAsync({type:o,data:i},e.abortController);if(delete e.abortController,e.aborted)return;this._afterTileLoadWorkerResponse(e,t);const a={};return (null==t?void 0:t.etagUnmodified)&&(a.unmodified=!0),a}catch(t){if(delete e.abortController,e.aborted)return;if(t&&404!==t.status)throw t;this._afterTileLoadWorkerResponse(e,null);}}))}_getOverzoomParameters(e){return t._(this,void 0,void 0,(function*(){if(e.tileID.canonical.z<=this.maxzoom)return;if(void 0===this.map._zoomLevelsToOverscale)return;const t=e.tileID.scaledTo(this.maxzoom).canonical,i=t.url(this.tiles,this.map.getPixelRatio(),this.scheme);return {maxZoomTileID:t,overzoomRequest:yield this.map._requestManager.transformRequest(i,"Tile")}}))}_afterTileLoadWorkerResponse(e,t){if((null==t?void 0:t.resourceTiming)&&(e.resourceTiming=t.resourceTiming),t&&this.map._refreshExpiredTiles&&e.setExpiryData(t),e.etag=null==t?void 0:t.etag,e.loadVectorData(t,this.map.painter),e.reloadPromise){const t=e.reloadPromise;e.reloadPromise=null,this.loadTile(e).then(t.resolve).catch(t.reject);}}abortTile(e){return t._(this,void 0,void 0,(function*(){e.abortController&&(e.abortController.abort(),delete e.abortController),e.actor&&(yield e.actor.sendAsync({type:"AT",data:{uid:e.uid,type:this.type,source:this.id}}));}))}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.unloadVectorData(),e.actor&&(yield e.actor.sendAsync({type:"RMT",data:{uid:e.uid,type:this.type,source:this.id}}));}))}hasTransition(){return !1}}class q extends t.E{constructor(e,i,o,a){super(),this.id=e,this.dispatcher=o,this.setEventedParent(a),this.type="raster",this.minzoom=0,this.maxzoom=22,this.roundZoom=!0,this.scheme="xyz",this.tileSize=512,this._loaded=!1,this._options=t.e({type:"raster"},i),t.e(this,t.V(i,["url","scheme","tileSize"]));}load(){return t._(this,arguments,void 0,(function*(e=!1){this._loaded=!1,this.fire(new t.n("dataloading",{dataType:"source"})),this._tileJSONRequest=new AbortController;try{const i=yield U(this._options,this.map._requestManager,this._tileJSONRequest,this.map._ownerWindow);this._tileJSONRequest=null,this._loaded=!0,i&&(t.e(this,i),i.bounds&&(this.tileBounds=new V(i.bounds,this.minzoom,this.maxzoom)),this.fire(new t.n("data",{dataType:"source",sourceDataType:"metadata"})),this.fire(new t.n("data",{dataType:"source",sourceDataType:"content",sourceDataChanged:e})));}catch(e){this._tileJSONRequest=null,this._loaded=!0,t.$(e)||this.fire(new t.l(t.d(e)));}}))}loaded(){return this._loaded}onAdd(e){this.map=e,this.load();}onRemove(){this._tileJSONRequest&&(this._tileJSONRequest.abort(),this._tileJSONRequest=null);}setSourceProperty(e){this._tileJSONRequest&&(this._tileJSONRequest.abort(),this._tileJSONRequest=null),e(),this.load(!0);}setTiles(e){return this.setSourceProperty((()=>{this._options.tiles=e;})),this}setUrl(e){return this.setSourceProperty((()=>{this.url=e,this._options.url=e;})),this}serialize(){return t.e({},this._options)}hasTile(e){return !this.tileBounds||this.tileBounds.contains(e.canonical)}loadTile(e){return t._(this,void 0,void 0,(function*(){const i=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme);e.abortController=new AbortController;try{const o=yield u.getImage(yield this.map._requestManager.transformRequest(i,"Tile"),e.abortController,this.map._refreshExpiredTiles);if(delete e.abortController,e.aborted)return void(e.state="unloaded");if(null==o?void 0:o.data){this.map._refreshExpiredTiles&&(o.cacheControl||o.expires)&&e.setExpiryData({cacheControl:o.cacheControl,expires:o.expires});const i=this.map.painter.context,a=i.gl,r=o.data;e.texture=this.map.painter.getTileTexture(r.width),e.texture?e.texture.update(r,{useMipmap:!0}):(e.texture=new t.T(i,r,a.RGBA,{useMipmap:!0}),e.texture.bind(a.LINEAR,a.CLAMP_TO_EDGE,a.LINEAR_MIPMAP_NEAREST)),e.state="loaded";}}catch(t){if(delete e.abortController,e.aborted)e.state="unloaded";else if(t)throw e.state="errored",t}}))}abortTile(e){return t._(this,void 0,void 0,(function*(){e.abortController&&(e.abortController.abort(),delete e.abortController);}))}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.texture&&this.map.painter.saveTileTexture(e.texture);}))}hasTransition(){return !1}}class $ extends q{constructor(e,i,o,a){super(e,i,o,a),this.type="raster-dem",this.maxzoom=22,this._options=t.e({type:"raster-dem"},i),this.encoding=i.encoding||"mapbox",this.redFactor=i.redFactor,this.greenFactor=i.greenFactor,this.blueFactor=i.blueFactor,this.baseShift=i.baseShift;}loadTile(e){return t._(this,void 0,void 0,(function*(){const i=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme),o=yield this.map._requestManager.transformRequest(i,"Tile");e.neighboringTiles=this._getNeighboringTiles(e.tileID),e.abortController=new AbortController;try{const i=yield u.getImage(o,e.abortController,this.map._refreshExpiredTiles);if(delete e.abortController,e.aborted)return void(e.state="unloaded");if(null==i?void 0:i.data){const o=i.data;this.map._refreshExpiredTiles&&(i.cacheControl||i.expires)&&e.setExpiryData({cacheControl:i.cacheControl,expires:i.expires});const a=t.b(o)&&t.a0()?o:yield this.readImageNow(o),r={type:this.type,uid:e.uid,source:this.id,rawImageData:a,encoding:this.encoding,redFactor:this.redFactor,greenFactor:this.greenFactor,blueFactor:this.blueFactor,baseShift:this.baseShift};if(e.actor&&"expired"!==e.state&&"reloading"!==e.state)return;e.actor&&"expired"!==e.state||(e.actor=this.dispatcher.getActor()),e.dem=yield e.actor.sendAsync({type:"LDT",data:r}),e.needsHillshadePrepare=!0,e.needsTerrainPrepare=!0,e.state="loaded";}}catch(t){if(delete e.abortController,e.aborted)e.state="unloaded";else if(t)throw e.state="errored",t}}))}readImageNow(e){return t._(this,void 0,void 0,(function*(){if("undefined"!=typeof VideoFrame&&t.a1()){const i=e.width+2,o=e.height+2;try{return new t.R({width:i,height:o},yield t.a2(e,-1,-1,i,o))}catch(e){}}return n.getImageData(e,1)}))}_getNeighboringTiles(e){const i=e.canonical,o=Math.pow(2,i.z),a=(i.x-1+o)%o,r=0===i.x?e.wrap-1:e.wrap,s=(i.x+1+o)%o,n=i.x+1===o?e.wrap+1:e.wrap,l={};return l[new t.a3(e.overscaledZ,r,i.z,a,i.y).key]={backfilled:!1},l[new t.a3(e.overscaledZ,n,i.z,s,i.y).key]={backfilled:!1},i.y>0&&(l[new t.a3(e.overscaledZ,r,i.z,a,i.y-1).key]={backfilled:!1},l[new t.a3(e.overscaledZ,e.wrap,i.z,i.x,i.y-1).key]={backfilled:!1},l[new t.a3(e.overscaledZ,n,i.z,s,i.y-1).key]={backfilled:!1}),i.y+1e.key===i));t>-1&&e.addOrUpdateProperties.splice(t,1);}return (e.removeAllProperties||t.removeAllProperties)&&(i.removeAllProperties=!0),(e.removeProperties||t.removeProperties)&&(i.removeProperties=[...e.removeProperties||[],...t.removeProperties||[]]),(e.addOrUpdateProperties||t.addOrUpdateProperties)&&(i.addOrUpdateProperties=[...e.addOrUpdateProperties||[],...t.addOrUpdateProperties||[]]),(e.newGeometry||t.newGeometry)&&(i.newGeometry=t.newGeometry||e.newGeometry),i}function K(e){var t,i;if(!e)return {};const o={};return o.removeAll=e.removeAll,o.remove=new Set(e.remove||[]),o.add=new Map(null===(t=e.add)||void 0===t?void 0:t.map((e=>[e.id,e]))),o.update=new Map(null===(i=e.update)||void 0===i?void 0:i.map((e=>[e.id,e]))),o}function Y(e){return e&&0!==e.length?"number"==typeof e[0]?[e]:e.flatMap((e=>Y(e))):[]}function Q(e){return "GeometryCollection"===e.type?e.geometries.flatMap((e=>Q(e))):Y(e.coordinates)}function J(e){const t=new G;let i;switch(e.type){case "FeatureCollection":i=e.features.flatMap((e=>Q(e.geometry)));break;case "Feature":i=Q(e.geometry);break;default:i=Q(e);}if(0===i.length)return t;for(const e of i){const[i,o]=e;t.extend([i,o]);}return t}class ee extends t.E{constructor(e,i,o,a){super(),this.id=e,this.type="geojson",this.minzoom=0,this.maxzoom=18,this.tileSize=512,this.isTileClipped=!0,this.reparseOverscaled=!0,this._removed=!1,this._isUpdatingWorker=!1,this._pendingWorkerUpdate={data:i.data},this.actor=o.getActor(),this.setEventedParent(a),this._data="string"==typeof i.data?{url:i.data}:{geojson:i.data},this._options=t.e({},i),this._collectResourceTiming=i.collectResourceTiming,void 0!==i.maxzoom&&(this.maxzoom=i.maxzoom),i.type&&(this.type=i.type),i.attribution&&(this.attribution=i.attribution),this.promoteId=i.promoteId,void 0!==i.clusterMaxZoom&&this.maxzoom<=i.clusterMaxZoom&&t.w(`The maxzoom value "${this.maxzoom}" is expected to be greater than the clusterMaxZoom value "${i.clusterMaxZoom}".`),this.workerOptions=t.e({source:this.id,geojsonVtOptions:{buffer:this._pixelsToTileUnits(void 0!==i.buffer?i.buffer:128),tolerance:this._pixelsToTileUnits(void 0!==i.tolerance?i.tolerance:.375),extent:t.a6,maxZoom:this.maxzoom,lineMetrics:i.lineMetrics||!1,generateId:i.generateId||!1,promoteId:"string"==typeof i.promoteId?i.promoteId:void 0,cluster:i.cluster||!1,clusterOptions:{maxZoom:this._getClusterMaxZoom(i.clusterMaxZoom),minPoints:Math.max(2,i.clusterMinPoints||2),extent:t.a6,radius:this._pixelsToTileUnits(i.clusterRadius||50),log:!1,generateId:i.generateId||!1}},clusterProperties:i.clusterProperties,filter:i.filter},i.workerOptions);}_hasPendingWorkerUpdate(){return void 0!==this._pendingWorkerUpdate.data||void 0!==this._pendingWorkerUpdate.diff||this._pendingWorkerUpdate.updateCluster}_pixelsToTileUnits(e){return e*(t.a6/this.tileSize)}_getClusterMaxZoom(e){const i=e?Math.round(e):this.maxzoom-1;return Number.isInteger(e)||void 0===e||t.w(`Integer expected for option 'clusterMaxZoom': provided value "${e}" rounded to "${i}"`),i}load(){return t._(this,void 0,void 0,(function*(){yield this._updateWorkerData();}))}onAdd(e){this.map=e,this.load();}setData(e,t){this._data="string"==typeof e?{url:e}:{geojson:e},this._pendingWorkerUpdate={data:e};const i=this._updateWorkerData();return t?i:this}updateData(e,t){this._pendingWorkerUpdate.diff=function(e,t){if(!e)return t||{};if(!t)return e||{};const i=K(e),o=K(t);!function(e,t){t.removeAll&&(e.add.clear(),e.update.clear(),e.remove.clear(),t.remove.clear());for(const i of t.remove)e.add.delete(i),e.update.delete(i);for(const[i,o]of t.update){const a=e.update.get(i);a&&(t.update.set(i,X(a,o)),e.update.delete(i));}}(i,o);const a={};if((i.removeAll||o.removeAll)&&(a.removeAll=!0),a.remove=new Set([...i.remove,...o.remove]),a.add=new Map([...i.add,...o.add]),a.update=new Map([...i.update,...o.update]),a.remove.size&&a.add.size)for(const e of a.add.keys())a.remove.delete(e);return function(e){const t={};return e.removeAll&&(t.removeAll=e.removeAll),e.remove&&(t.remove=Array.from(e.remove)),e.add&&(t.add=Array.from(e.add.values())),e.update&&(t.update=Array.from(e.update.values())),t}(a)}(this._pendingWorkerUpdate.diff,e);const i=this._updateWorkerData();return t?i:this}getData(){return t._(this,void 0,void 0,(function*(){return this._data.url&&(yield this.once("data")),this._data.geojson?this._data.geojson:{type:"FeatureCollection",features:Array.from(this._data.updateable.values())}}))}getBounds(){return t._(this,void 0,void 0,(function*(){return J(yield this.getData())}))}setClusterOptions(e){return this.workerOptions.geojsonVtOptions.cluster=e.cluster,void 0!==e.clusterRadius&&(this.workerOptions.geojsonVtOptions.clusterOptions.radius=this._pixelsToTileUnits(e.clusterRadius)),void 0!==e.clusterMaxZoom&&(this.workerOptions.geojsonVtOptions.clusterOptions.maxZoom=this._getClusterMaxZoom(e.clusterMaxZoom)),this._pendingWorkerUpdate.updateCluster=!0,this._updateWorkerData(),this}getClusterExpansionZoom(e){return this.actor.sendAsync({type:"GCEZ",data:{type:this.type,clusterId:e,source:this.id}})}getClusterChildren(e){return this.actor.sendAsync({type:"GCC",data:{type:this.type,clusterId:e,source:this.id}})}getClusterLeaves(e,t,i){return this.actor.sendAsync({type:"GCL",data:{type:this.type,source:this.id,clusterId:e,limit:t,offset:i}})}_updateWorkerData(){return t._(this,void 0,void 0,(function*(){if(this._isUpdatingWorker)return;if(!this._hasPendingWorkerUpdate())return void t.w(`No pending worker updates for GeoJSONSource ${this.id}.`);const{data:e,diff:i,updateCluster:o}=this._pendingWorkerUpdate,a=this._getLoadGeoJSONParameters(e,i,o);void 0!==e?this._pendingWorkerUpdate.data=void 0:i?this._pendingWorkerUpdate.diff=void 0:o&&(this._pendingWorkerUpdate.updateCluster=void 0),yield this._dispatchWorkerUpdate(a);}))}_getLoadGeoJSONParameters(e,i,o){return t._(this,void 0,void 0,(function*(){const a=t.e({type:this.type},this.workerOptions);return "string"==typeof e?(a.request=yield this.map._requestManager.transformRequest(n.resolveURL(e),"Source"),a.request.collectResourceTiming=this._collectResourceTiming,a):void 0!==e?(a.data=e,a):i?(a.dataDiff=i,a):o?(a.updateCluster=!0,a):void 0}))}_dispatchWorkerUpdate(e){return t._(this,void 0,void 0,(function*(){this._isUpdatingWorker=!0,this.fire(new t.n("dataloading",{dataType:"source"}));try{const i=yield e,o=yield this.actor.sendAsync({type:"LD",data:i});if(this._isUpdatingWorker=!1,this._removed||o.abandoned)return void this.fire(new t.n("dataabort",{dataType:"source"}));o.data&&(this._data={geojson:o.data});const a=this._applyDiffToSource(i.dataDiff),r=this._getShouldReloadTileOptions(a),s={dataType:"source"};this._applyResourceTiming(s,o),this.fire(new t.n("data",Object.assign(Object.assign({},s),{sourceDataType:"metadata"}))),this.fire(new t.n("data",Object.assign(Object.assign({},s),{sourceDataType:"content",shouldReloadTileOptions:r})));}catch(e){if(this._isUpdatingWorker=!1,this._removed)return void this.fire(new t.n("dataabort",{dataType:"source"}));this.fire(new t.l(t.d(e)));}finally{this._hasPendingWorkerUpdate()&&this._updateWorkerData();}}))}_applyResourceTiming(e,i){var o;if(!this._collectResourceTiming)return;const a=null===(o=i.resourceTiming)||void 0===o?void 0:o[this.id];if(!a)return;const r=a.slice(0);(null==r?void 0:r.length)&&t.e(e,{resourceTiming:r});}_applyDiffToSource(e){if(!e)return;const t="string"==typeof this.promoteId?this.promoteId:void 0;if(!this._data.url&&!this._data.updateable){const e=function(e,t){const i=new Map;if(null==e)return i;if(null==e.type)return i;if("Feature"===e.type){const o=H(e,t);if(null==o)return;return i.set(o,e),i}if("FeatureCollection"===e.type){const o=new Set;for(const a of e.features){const e=H(a,t);if(null==e)return;if(o.has(e))return;o.add(e),i.set(e,a);}return i}}(this._data.geojson,t);if(!e)throw new Error(`GeoJSONSource "${this.id}": GeoJSON data is not compatible with updateData`);this._data={updateable:e};}if(!this._data.updateable)return;const i=function(e,t,i){var o,a;const r=[];if(t.removeAll)e.clear();else if(t.remove)for(const i of t.remove){const t=e.get(i);t&&(r.push(t.geometry),e.delete(i));}if(t.add)for(const o of t.add){const t=H(o,i);if(null==t)continue;const a=e.get(t);a&&r.push(a.geometry),r.push(o.geometry),e.set(t,o);}if(t.update)for(const i of t.update){const t=e.get(i.id);if(!t)continue;const s=!!i.newGeometry,n=i.removeAllProperties||(null===(o=i.removeProperties)||void 0===o?void 0:o.length)>0||(null===(a=i.addOrUpdateProperties)||void 0===a?void 0:a.length)>0;if(!s&&!n)continue;r.push(t.geometry);const l=Object.assign({},t);if(e.set(i.id,l),s&&(r.push(i.newGeometry),l.geometry=i.newGeometry),n){if(l.properties=i.removeAllProperties?{}:Object.assign({},l.properties||{}),i.removeProperties)for(const e of i.removeProperties)delete l.properties[e];if(i.addOrUpdateProperties)for(const{key:e,value:t}of i.addOrUpdateProperties)l.properties[e]=t;}}return r}(this._data.updateable,e,t);return e.removeAll||this._options.cluster?void 0:i}_getShouldReloadTileOptions(e){if(e)return {affectedBounds:e.filter(Boolean).map((e=>J(e)))}}shouldReloadTile(e,{affectedBounds:i}){if("loading"===e.state)return !0;if("unloaded"===e.state)return !1;const{buffer:o,extent:a}=this.workerOptions.geojsonVtOptions,r=function({x:e,y:i,z:o},a=0){const r=t.a4((e-a)/Math.pow(2,o)),s=t.a5((i+1+a)/Math.pow(2,o)),n=t.a4((e+1+a)/Math.pow(2,o)),l=t.a5((i-a)/Math.pow(2,o));return new G([r,s],[n,l])}(e.tileID.canonical,o/a);for(const e of i)if(r.intersects(e))return !0;return !1}loaded(){return !this._isUpdatingWorker&&!this._hasPendingWorkerUpdate()}loadTile(e){return t._(this,void 0,void 0,(function*(){const t=e.actor?"RT":"LT";e.actor=this.actor;const i={type:this.type,uid:e.uid,tileID:e.tileID,zoom:e.tileID.overscaledZ,maxZoom:this.maxzoom,tileSize:this.tileSize,source:this.id,pixelRatio:this.map.getPixelRatio(),showCollisionBoxes:this.map.showCollisionBoxes,promoteId:this.promoteId,subdivisionGranularity:this.map.style.projection.subdivisionGranularity};e.abortController=new AbortController;const o=yield this.actor.sendAsync({type:t,data:i},e.abortController);delete e.abortController,e.unloadVectorData(),e.aborted||e.loadVectorData(o,this.map.painter,"RT"===t);}))}abortTile(e){return t._(this,void 0,void 0,(function*(){e.abortController&&(e.abortController.abort(),delete e.abortController),e.aborted=!0;}))}unloadTile(e){return t._(this,void 0,void 0,(function*(){e.unloadVectorData(),yield this.actor.sendAsync({type:"RMT",data:{uid:e.uid,type:this.type,source:this.id}});}))}onRemove(){this._removed=!0,this.actor.sendAsync({type:"RS",data:{type:this.type,source:this.id}});}serialize(){return t.e({},this._options,{type:this.type,data:this._data.updateable?{type:"FeatureCollection",features:Array.from(this._data.updateable.values())}:this._data.url||this._data.geojson})}hasTransition(){return !1}}class te extends t.E{constructor(e,t,i,o){super(),this.flippedWindingOrder=!1,this.id=e,this.dispatcher=i,this.coordinates=t.coordinates,this.type="image",this.minzoom=0,this.maxzoom=22,this.tileSize=512,this.tiles={},this._loaded=!1,this.setEventedParent(o),this.options=t;}load(e){return t._(this,void 0,void 0,(function*(){this._loaded=!1,this.fire(new t.n("dataloading",{dataType:"source"})),this.url=this.options.url,this._request=new AbortController;try{const t=yield u.getImage(yield this.map._requestManager.transformRequest(this.url,"Image"),this._request);this._request=null,this._loaded=!0,(null==t?void 0:t.data)&&(this.image=t.data,e&&(this.coordinates=e),this._finishLoading());}catch(e){this._request=null,this._loaded=!0,t.$(e)||this.fire(new t.l(t.d(e)));}}))}loaded(){return this._loaded}updateImage(e){return e.url?(this._request&&(this._request.abort(),this._request=null),this.options.url=e.url,this.load(e.coordinates).finally((()=>this.texture=null)),this):this}_finishLoading(){this.map&&(this.setCoordinates(this.coordinates),this.fire(new t.n("data",{dataType:"source",sourceDataType:"metadata"})));}onAdd(e){this.map=e,this.load();}onRemove(){this._request&&(this._request.abort(),this._request=null);}setCoordinates(e){this.coordinates=e;const i=e.map(t.a7.fromLngLat);var o;return this.tileID=function(e){const i=t.a8.fromPoints(e),o=i.width(),a=i.height(),r=Math.max(o,a),s=Math.max(0,Math.floor(-Math.log(r)/Math.LN2)),n=Math.pow(2,s);return new t.aa(s,Math.floor((i.minX+i.maxX)/2*n),Math.floor((i.minY+i.maxY)/2*n))}(i),this.terrainTileRanges=this._getOverlappingTileRanges(i),this.minzoom=this.maxzoom=this.tileID.z,this.tileCoords=i.map((e=>this.tileID.getTilePoint(e)._round())),this.flippedWindingOrder=((o=this.tileCoords)[1].x-o[0].x)*(o[2].y-o[0].y)-(o[1].y-o[0].y)*(o[2].x-o[0].x)<0,this.fire(new t.n("data",{dataType:"source",sourceDataType:"content"})),this}prepare(){if(0===Object.keys(this.tiles).length||!this.image)return;const e=this.map.painter.context,i=e.gl;this.texture||(this.texture=new t.T(e,this.image,i.RGBA),this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE));let o=!1;for(const e in this.tiles){const t=this.tiles[e];"loaded"!==t.state&&(t.state="loaded",t.texture=this.texture,o=!0);}o&&this.fire(new t.n("data",{dataType:"source",sourceDataType:"idle",sourceId:this.id}));}loadTile(e){return t._(this,void 0,void 0,(function*(){var t;(null===(t=this.tileID)||void 0===t?void 0:t.equals(e.tileID.canonical))?(this.tiles[String(e.tileID.wrap)]=e,e.buckets={}):e.state="errored";}))}serialize(){return {type:"image",url:this.options.url,coordinates:this.coordinates}}hasTransition(){return !1}_getOverlappingTileRanges(e){const{minX:i,minY:o,maxX:a,maxY:r}=t.a8.fromPoints(e),s={};for(let e=0;e<=t.a9;e++){const t=Math.pow(2,e),n=Math.floor(i*t),l=Math.floor(o*t),c=Math.floor(a*t),h=Math.floor(r*t),u=(n%t+t)%t,d=c%t,_=Math.floor(n/t),p=Math.floor(c/t);s[e]={minWrap:_,maxWrap:p,minTileXWrapped:u,maxTileXWrapped:d,minTileY:l,maxTileY:h};}return s}}class ie extends te{constructor(e,t,i,o){super(e,t,i,o),this._onPlayingHandler=()=>{var e;null===(e=this.map)||void 0===e||e.triggerRepaint();},this.roundZoom=!0,this.type="video",this.options=t;}load(){return t._(this,void 0,void 0,(function*(){this._loaded=!1;const e=this.options;this.urls=[];for(const t of e.urls)this.urls.push((yield this.map._requestManager.transformRequest(t,"Source")).url);try{const e=yield t.ab(this.urls);if(this._loaded=!0,!e)return;this.video=e,this.video.loop=!0,this.video.addEventListener("playing",this._onPlayingHandler),this.map&&this.video.play(),this._finishLoading();}catch(e){this.fire(new t.l(t.d(e)));}}))}pause(){this.video&&this.video.pause();}play(){this.video&&this.video.play();}seek(e){if(this.video){const i=this.video.seekable;ei.end(0)?this.fire(new t.l(new t.ac(`sources.${this.id}`,null,`Playback for this video can be set only between the ${i.start(0)} and ${i.end(0)}-second mark.`))):this.video.currentTime=e;}}getVideo(){return this.video}onAdd(e){this.map||(this.map=e,this.load(),this.video&&(this.video.play(),this.setCoordinates(this.coordinates)));}onRemove(){super.onRemove(),this.video&&(this.video.removeEventListener("playing",this._onPlayingHandler),this.video.pause());}prepare(){if(0===Object.keys(this.tiles).length||this.video.readyState<2)return;const e=this.map.painter.context,i=e.gl;this.texture?this.video.paused||(this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE),i.texSubImage2D(i.TEXTURE_2D,0,0,0,i.RGBA,i.UNSIGNED_BYTE,this.video)):(this.texture=new t.T(e,this.video,i.RGBA),this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE));let o=!1;for(const e in this.tiles){const t=this.tiles[e];"loaded"!==t.state&&(t.state="loaded",t.texture=this.texture,o=!0);}o&&this.fire(new t.n("data",{dataType:"source",sourceDataType:"idle",sourceId:this.id}));}serialize(){return {type:"video",urls:this.urls,coordinates:this.coordinates}}hasTransition(){return this.video&&!this.video.paused}}class oe extends te{constructor(e,i,o,a){super(e,i,o,a),i.coordinates?Array.isArray(i.coordinates)&&4===i.coordinates.length&&!i.coordinates.some((e=>!Array.isArray(e)||2!==e.length||e.some((e=>"number"!=typeof e))))||this.fire(new t.l(new t.ac(`sources.${e}`,null,'"coordinates" property must be an array of 4 longitude/latitude array pairs'))):this.fire(new t.l(new t.ac(`sources.${e}`,null,'missing required property "coordinates"'))),i.animate&&"boolean"!=typeof i.animate&&this.fire(new t.l(new t.ac(`sources.${e}`,null,'optional "animate" property must be a boolean value'))),i.canvas?"string"==typeof i.canvas||i.canvas instanceof HTMLCanvasElement||this.fire(new t.l(new t.ac(`sources.${e}`,null,'"canvas" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance'))):this.fire(new t.l(new t.ac(`sources.${e}`,null,'missing required property "canvas"'))),this.options=i,this.animate=void 0===i.animate||i.animate;}load(){return t._(this,void 0,void 0,(function*(){this._loaded=!0,this.canvas||(this.canvas=this.options.canvas instanceof HTMLCanvasElement?this.options.canvas:document.getElementById(this.options.canvas)),this.width=this.canvas.width,this.height=this.canvas.height,this._hasInvalidDimensions()?this.fire(new t.l(new Error("Canvas dimensions cannot be less than or equal to zero."))):(this.play=function(){this._playing=!0,this.map.triggerRepaint();},this.pause=function(){this._playing&&(this.prepare(),this._playing=!1);},this._finishLoading());}))}getCanvas(){return this.canvas}onAdd(e){this.map=e,this.load(),this.canvas&&this.animate&&this.play();}onRemove(){this.pause();}prepare(){let e=!1;if(this.canvas.width!==this.width&&(this.width=this.canvas.width,e=!0),this.canvas.height!==this.height&&(this.height=this.canvas.height,e=!0),this._hasInvalidDimensions())return;if(0===Object.keys(this.tiles).length)return;const i=this.map.painter.context,o=i.gl;this.texture?(e||this._playing)&&this.texture.update(this.canvas,{premultiply:!0}):(this.texture=new t.T(i,this.canvas,o.RGBA,{premultiply:!0}),this.texture.bind(o.LINEAR,o.CLAMP_TO_EDGE));let a=!1;for(const e in this.tiles){const t=this.tiles[e];"loaded"!==t.state&&(t.state="loaded",t.texture=this.texture,a=!0);}a&&this.fire(new t.n("data",{dataType:"source",sourceDataType:"idle",sourceId:this.id}));}serialize(){return {type:"canvas",animate:this.animate,canvas:this.options.canvas,coordinates:this.coordinates}}hasTransition(){return this._playing}_hasInvalidDimensions(){for(const e of [this.canvas.width,this.canvas.height])if(isNaN(e)||e<=0)return !0;return !1}}const ae={},re=e=>{switch(e){case "geojson":return ee;case "image":return te;case "raster":return q;case "raster-dem":return $;case "vector":return W;case "video":return ie;case "canvas":return oe}return ae[e]},se="RTLPluginLoaded";class ne extends t.E{constructor(){super(...arguments),this.status="unavailable",this.url=null,this.dispatcher=B();}_syncState(e){return this.status=e,this.dispatcher.broadcast("SRPS",{pluginStatus:e,pluginURL:this.url}).catch((e=>{throw this.status="error",e}))}getRTLTextPluginStatus(){return this.status}clearRTLTextPlugin(){this.status="unavailable",this.url=null;}setRTLTextPlugin(e){return t._(this,arguments,void 0,(function*(e,t=!1){if(this.url)throw new Error("setRTLTextPlugin cannot be called multiple times.");if(this.url=n.resolveURL(e),!this.url)throw new Error(`requested url ${e} is invalid`);if("unavailable"===this.status){if(!t)return this._requestImport();this.status="deferred",this._syncState(this.status);}else if("requested"===this.status)return this._requestImport()}))}_requestImport(){return t._(this,void 0,void 0,(function*(){yield this._syncState("loading"),this.status="loaded",this.fire(new t.n(se));}))}lazyLoad(){"unavailable"===this.status?this.status="requested":"deferred"===this.status&&this._requestImport();}}let le=null;function ce(){return le||(le=new ne),le}var he,ue;!function(e){e[e.Base=0]="Base",e[e.Parent=1]="Parent";}(he||(he={})),function(e){e[e.Departing=0]="Departing",e[e.Incoming=1]="Incoming";}(ue||(ue={}));class de{constructor(e,i){this.timeAdded=0,this.fadeEndTime=0,this.fadeOpacity=1,this.tileID=e,this.uid=t.ad(),this.uses=0,this.tileSize=i,this.buckets={},this.expirationTime=null,this.queryPadding=0,this.hasSymbolBuckets=!1,this.hasRTLText=!1,this.dependencies={},this.rtt=[],this.rttFingerprint={},this.expiredRequestCount=0,this.state="loading";}isRenderable(e){return this.hasData()&&(!this.fadeEndTime||this.fadeOpacity>0)&&(e||!this.holdingForSymbolFade())}setCrossFadeLogic({fadingRole:e,fadingDirection:t,fadingParentID:i,fadeEndTime:o}){this.resetFadeLogic(),this.fadingRole=e,this.fadingDirection=t,this.fadingParentID=i,this.fadeEndTime=o;}setSelfFadeLogic(e){this.resetFadeLogic(),this.selfFading=!0,this.fadeEndTime=e;}resetFadeLogic(){this.fadingRole=null,this.fadingDirection=null,this.fadingParentID=null,this.selfFading=!1,this.timeAdded=c(),this.fadeEndTime=0,this.fadeOpacity=1;}wasRequested(){return "errored"===this.state||"loaded"===this.state||"reloading"===this.state}clearTextures(e){this.demTexture&&e.saveTileTexture(this.demTexture),this.demTexture=null;}loadVectorData(e,i,o){if(!0!==(null==e?void 0:e.etagUnmodified))if(this.hasData()&&this.unloadVectorData(),this.state="loaded",e){e.featureIndex&&(this.latestFeatureIndex=e.featureIndex,e.rawTileData?(this.latestRawTileData=e.rawTileData,this.latestEncoding=e.encoding,this.latestFeatureIndex.rawTileData=e.rawTileData,this.latestFeatureIndex.encoding=e.encoding):this.latestRawTileData&&(this.latestFeatureIndex.rawTileData=this.latestRawTileData,this.latestFeatureIndex.encoding=this.latestEncoding)),this.collisionBoxArray=e.collisionBoxArray,this.buckets=function(e,t){const i={};if(!t)return i;for(const o of e){const e=o.layerIds.map((e=>t.getLayer(e))).filter(Boolean);if(0!==e.length){o.layers=e,o.stateDependentLayerIds&&(o.stateDependentLayers=o.stateDependentLayerIds.map((t=>e.filter((e=>e.id===t))[0])));for(const t of e)i[t.id]=o;}}return i}(e.buckets,null==i?void 0:i.style),this.hasSymbolBuckets=!1;for(const e in this.buckets){const i=this.buckets[e];if(i instanceof t.af){if(this.hasSymbolBuckets=!0,!o)break;i.justReloaded=!0;}}if(this.hasRTLText=!1,this.hasSymbolBuckets)for(const e in this.buckets){const i=this.buckets[e];if(i instanceof t.af&&i.hasRTLText){this.hasRTLText=!0,ce().lazyLoad();break}}this.queryPadding=0;for(const e in this.buckets){const t=this.buckets[e];this.queryPadding=Math.max(this.queryPadding,i.style.getLayer(e).queryRadius(t));}e.imageAtlas&&(this.imageAtlas=e.imageAtlas),e.glyphAtlasImage&&(this.glyphAtlasImage=e.glyphAtlasImage),this.dashPositions=e.dashPositions;}else this.collisionBoxArray=new t.ae;else this.state="loaded";}unloadVectorData(){for(const e in this.buckets)this.buckets[e].destroy();this.buckets={},this.imageAtlasTexture&&this.imageAtlasTexture.destroy(),this.glyphAtlasTexture&&this.glyphAtlasTexture.destroy(),this.imageAtlas=null,this.dashPositions=null,this.latestFeatureIndex=null,this.state="unloaded";}getBucket(e){return this.buckets[e.id]}upload(e){for(const t in this.buckets){const i=this.buckets[t];i.uploadPending()&&i.upload(e);}const i=e.gl;this.imageAtlas&&!this.imageAtlas.uploaded&&(this.imageAtlasTexture=new t.T(e,this.imageAtlas.image,i.RGBA),this.imageAtlas.uploaded=!0),this.glyphAtlasImage&&(this.glyphAtlasTexture=new t.T(e,this.glyphAtlasImage,i.ALPHA),this.glyphAtlasImage=null);}prepare(e){this.imageAtlas&&this.imageAtlas.patchUpdatedImages(e,this.imageAtlasTexture);}queryRenderedFeatures(e,t,i,o,a,r,s,n,l,c,h){var u;return (null===(u=this.latestFeatureIndex)||void 0===u?void 0:u.rawTileData)?this.latestFeatureIndex.query({queryGeometry:o,cameraQueryGeometry:a,scale:r,tileSize:this.tileSize,pixelPosMatrix:c,transform:n,params:s,queryPadding:this.queryPadding*l,getElevation:h},e,t,i):{}}querySourceFeatures(e,i){const o=this.latestFeatureIndex;if(!(null==o?void 0:o.rawTileData))return;const a=o.loadVTLayers(),r=(null==i?void 0:i.sourceLayer)?i.sourceLayer:"",s=a[t.ag]||a[r];if(!s)return;const n=t.ah(null==i?void 0:i.filter,null==i?void 0:i.globalState),{z:l,x:c,y:h}=this.tileID.canonical,u={z:l,x:c,y:h};for(let i=0;ie)t=!1;else if(i)if(this.expirationTime({zoom:0,x:0,y:0,wrap:e,fullyVisible:!1}),b=[],y=[];if(e.renderWorldCopies&&n.allowWorldCopies())for(let e=1;e<=3;e++)b.push(x(-e)),b.push(x(e));for(b.push(x(0));b.length>0;){const _=b.pop(),f=_.x,x=_.y;let w=_.fullyVisible;const T={x:f,y:x,z:_.zoom},P=n.getTileBoundingVolume(T,_.wrap,e.elevation,i);if(!w){const e=ye(o,P,a);if(0===e)continue;w=2===e;}const C=n.distanceToTile2d(r.x,r.y,T,P);let M=c;l&&(M=(i.calculateTileZoom||Pe)(e.zoom+t.ar(e.tileSize/i.tileSize),C,g,v,e.fov)),M=(i.roundZoom?Math.round:Math.floor)(M),M=Math.max(0,M);const I=Math.min(M,u);if(_.wrap=n.getWrap(s,T,_.wrap),_.zoom>=I){if(_.zoom>1),wrap:_.wrap,fullyVisible:w});}return y.sort(((e,t)=>e.distanceSq-t.distanceSq)).map((e=>e.tileID))}const Ie=t.a8.fromPoints([new t.P(0,0),new t.P(t.a6,t.a6)]);function Ee(e){return "raster"===e||"image"===e||"video"===e}function Se(e,t,i,o,a,r,s){if(!t.hasData())return !1;const{tileID:n,fadingRole:l,fadingDirection:c,fadingParentID:h}=t;if(l===he.Base&&c===ue.Incoming&&h)return i[h.key]=h,!0;const u=Math.max(n.overscaledZ-a,r);for(let a=n.overscaledZ-1;a>=u;a--){const r=n.scaledTo(a),l=e.getLoadedTile(r);if(l)return t.setCrossFadeLogic({fadingRole:he.Base,fadingDirection:ue.Incoming,fadingParentID:l.tileID,fadeEndTime:o+s}),l.setCrossFadeLogic({fadingRole:he.Parent,fadingDirection:ue.Departing,fadeEndTime:o+s}),i[r.key]=r,!0}return !1}function ze(e,t,i,o,a,r){if(!t.hasData())return !1;const s=t.tileID.children(a);let n=Re(e,t,s,i,o,a,r);if(n)return !0;for(const l of s)Re(e,t,l.children(a),i,o,a,r)&&(n=!0);return n}function Re(e,t,i,o,a,r,s){if(i[0].overscaledZ>=r)return !1;let n=!1;for(const r of i){const i=e.getLoadedTile(r);if(!i)continue;const{fadingRole:l,fadingDirection:c,fadingParentID:h}=i;l===he.Base&&c===ue.Departing&&h||(i.setCrossFadeLogic({fadingRole:he.Base,fadingDirection:ue.Departing,fadingParentID:t.tileID,fadeEndTime:a+s}),t.setCrossFadeLogic({fadingRole:he.Parent,fadingDirection:ue.Incoming,fadeEndTime:a+s})),o[r.key]=r,n=!0;}return n}function De(e,t,i,o){const a=e.tileID;return !!e.selfFading||!e.hasData()&&!!t.has(a)&&(e.setSelfFadeLogic(i+o),!0)}function Ae(e,t){var i;e.needsHillshadePrepare=!0,e.needsTerrainPrepare=!0;let o=t.tileID.canonical.x-e.tileID.canonical.x;const a=t.tileID.canonical.y-e.tileID.canonical.y,r=Math.pow(2,e.tileID.canonical.z),s=t.tileID.key;0===o&&0===a||Math.abs(a)>1||(Math.abs(o)>1&&(1===Math.abs(o+r)?o+=r:1===Math.abs(o-r)&&(o-=r)),t.dem&&e.dem&&(e.dem.backfillBorder(t.dem,o,a),(null===(i=e.neighboringTiles)||void 0===i?void 0:i[s])&&(e.neighboringTiles[s].backfilled=!0)));}class Le{constructor(){this._tiles={};}handleWrapJump(e){const t={};for(const i in this._tiles){const o=this._tiles[i];o.tileID=o.tileID.unwrapTo(o.tileID.wrap+e),t[o.tileID.key]=o;}this._tiles=t;}setFeatureState(e,t){for(const i in this._tiles)this._tiles[i].setFeatureState(e,t);}getAllTiles(){return Object.values(this._tiles)}getAllIds(e=!1){return e?Object.values(this._tiles).map((e=>e.tileID)).sort(t.au).map((e=>e.key)):Object.keys(this._tiles)}getTileById(e){return this._tiles[e]}setTile(e,t){this._tiles[e]=t;}deleteTileById(e){delete this._tiles[e];}getLoadedTile(e){const t=this.getTileById(e.key);return (null==t?void 0:t.hasData())?t:null}isIdRenderable(e,t=!1){var i;return null===(i=this.getTileById(e))||void 0===i?void 0:i.isRenderable(t)}getRenderableIds(e=0,i){const o=[];for(const e of this.getAllIds())this.isIdRenderable(e,i)&&o.push(this.getTileById(e));return i?o.sort(((i,o)=>{const a=i.tileID,r=o.tileID,s=new t.P(a.canonical.x,a.canonical.y)._rotate(-e),n=new t.P(r.canonical.x,r.canonical.y)._rotate(-e);return a.overscaledZ-r.overscaledZ||n.y-s.y||n.x-s.x})).map((e=>e.tileID.key)):o.map((e=>e.tileID)).sort(t.au).map((e=>e.key))}}class Fe extends t.E{constructor(e,i,o){super(),this.id=e,this.dispatcher=o,this.on("data",(e=>{this._dataHandler(e);})),this.on("dataloading",(()=>{this._sourceErrored=!1;})),this.on("error",(()=>{this._sourceErrored=this._source.loaded();})),this._source=((e,t,i,o)=>{const a=new(re(t.type))(e,t,i,o);if(a.id!==e)throw new Error(`Expected Source id to be ${e} instead of ${a.id}`);return a})(e,i,o,this),this._inViewTiles=new Le,this._outOfViewCache=new t.av(0,(e=>this._unloadTile(e))),this._timers={},this._maxTileCacheSize=null,this._maxTileCacheZoomLevels=null,this._rasterFadeDuration=0,this._maxFadingAncestorLevels=5,this._state=new _e,this._didEmitContent=!1,this._updated=!1;}onAdd(e){var t;this.map=e,this._maxTileCacheSize=e?e._maxTileCacheSize:null,this._maxTileCacheZoomLevels=e?e._maxTileCacheZoomLevels:null,(null===(t=this._source)||void 0===t?void 0:t.onAdd)&&this._source.onAdd(e);}onRemove(e){var t;for(const e of this._inViewTiles.getAllTiles())e.unloadVectorData();this.clearTiles(),(null===(t=this._source)||void 0===t?void 0:t.onRemove)&&this._source.onRemove(e),this._inViewTiles=new Le;}loaded(){if(this._sourceErrored)return !0;if(!this._sourceLoaded)return !1;if(!this._source.loaded())return !1;if(!(void 0===this.used&&void 0===this.usedForTerrain||this.used||this.usedForTerrain))return !0;if(!this._updated)return !1;for(const e of this._inViewTiles.getAllTiles())if("loaded"!==e.state&&"errored"!==e.state)return !1;return !0}getSource(){return this._source}getState(){return this._state}pause(){this._paused=!0;}resume(){if(!this._paused)return;const e=this._shouldReloadOnResume;this._paused=!1,this._shouldReloadOnResume=!1,e&&this.reload(),this.transform&&this.update(this.transform,this.terrain);}_loadTile(e,i,o){return t._(this,void 0,void 0,(function*(){try{const t=yield this._source.loadTile(e);this._tileLoaded(e,i,o,t);}catch(i){e.state="errored",404!==i.status?this._source.fire(new t.l(t.d(i),{tile:e})):this.update(this.transform,this.terrain);}}))}_unloadTile(e){this._source.unloadTile&&this._source.unloadTile(e);}_abortTile(e){this._source.abortTile&&this._source.abortTile(e),this._source.fire(new t.n("dataabort",{tile:e,coord:e.tileID,dataType:"source"}));}serialize(){return this._source.serialize()}prepare(e){this._source.prepare&&this._source.prepare(),this._state.coalesceChanges(this._inViewTiles,this.map?this.map.painter:null);for(const t of this._inViewTiles.getAllTiles())t.upload(e),t.prepare(this.map.style.imageManager);}getIds(){return this._inViewTiles.getAllIds(!0)}getRenderableIds(e){var t;return this._inViewTiles.getRenderableIds(null===(t=this.transform)||void 0===t?void 0:t.bearingInRadians,e)}hasRenderableParent(e){const t=e.overscaledZ-1;if(t>=this._source.minzoom){const i=this.getLoadedTile(e.scaledTo(t));if(i)return this._inViewTiles.isIdRenderable(i.tileID.key)}return !1}reload(e,t=void 0){if(this._paused)this._shouldReloadOnResume=!0;else {this._outOfViewCache.reset();for(const i of this._inViewTiles.getAllIds()){const o=this._inViewTiles.getTileById(i);t&&!this._source.shouldReloadTile(o,t)||(e?this._reloadTile(i,"expired"):"errored"!==o.state&&this._reloadTile(i,"reloading"));}}}_reloadTile(e,i){return t._(this,void 0,void 0,(function*(){const t=this._inViewTiles.getTileById(e);t&&("loading"!==t.state&&(t.state=i),yield this._loadTile(t,e,i));}))}_tileLoaded(e,i,o,a){e.timeAdded=c(),e.selfFading&&(e.fadeEndTime=e.timeAdded+this._rasterFadeDuration),"expired"===o&&(e.refreshedUponExpiration=!0),this._setTileReloadTimer(i,e),(null==a?void 0:a.unmodified)||("raster-dem"===this.getSource().type&&e.dem&&function(e,t){var i,o,a;const r=t.getRenderableIds();for(const s of r){if(!(null===(i=e.neighboringTiles)||void 0===i?void 0:i[s]))continue;const r=t.getTileById(s);e.neighboringTiles[s].backfilled||Ae(e,r),(null===(a=null===(o=r.neighboringTiles)||void 0===o?void 0:o[e.tileID.key])||void 0===a?void 0:a.backfilled)||Ae(r,e);}}(e,this._inViewTiles),this._state.initializeTileState(e,this.map?this.map.painter:null),e.aborted||this._source.fire(new t.n("data",{dataType:"source",tile:e,coord:e.tileID})));}getTile(e){return this.getTileByID(e.key)}getTileByID(e){return this._inViewTiles.getTileById(e)}_retainLoadedChildren(e,t){const i=this._getLoadedDescendents(t),o=new Set;for(const a of t){const t=i[a.key];if(!(null==t?void 0:t.length)){o.add(a);continue}const r=a.overscaledZ+Fe.maxOverzooming,s=t.filter((e=>e.tileID.overscaledZ<=r));if(!s.length){o.add(a);continue}const n=Math.min(...s.map((e=>e.tileID.overscaledZ))),l=s.filter((e=>e.tileID.overscaledZ===n)).map((e=>e.tileID));for(const t of l)e[t.key]=t;this._areDescendentsComplete(l,n,a.overscaledZ)||o.add(a);}return o}_getLoadedDescendents(e){var t;const i={};for(const o of this._inViewTiles.getAllTiles().filter((e=>e.hasData())))for(const a of e)o.tileID.isChildOf(a)&&(i[t=a.key]||(i[t]=[]),i[a.key].push(o));return i}_areDescendentsComplete(e,t,i){return 1===e.length&&e[0].isOverscaled()?e[0].overscaledZ===t:Math.pow(4,t-i)===e.length}getLoadedTile(e){return this._inViewTiles.getLoadedTile(e)}updateCacheSize(e){const i=Math.ceil(e.width/this._source.tileSize)+1,o=Math.ceil(e.height/this._source.tileSize)+1,a=Math.floor(i*o*(null===this._maxTileCacheZoomLevels?t.c.MAX_TILE_CACHE_ZOOM_LEVELS:this._maxTileCacheZoomLevels)),r="number"==typeof this._maxTileCacheSize?Math.min(this._maxTileCacheSize,a):a;this._outOfViewCache.setMaxSize(r);}handleWrapJump(e){const t=Math.round((e-(void 0===this._prevLng?e:this._prevLng))/360);this._prevLng=e,t&&(this._inViewTiles.handleWrapJump(t),this._resetTileReloadTimers());}update(e,i){if(!this._sourceLoaded||this._paused)return;let o;this.transform=e,this.terrain=i,this.updateCacheSize(e),this.handleWrapJump(this.transform.center.lng),this.used||this.usedForTerrain?this._source.tileID?o=e.getVisibleUnwrappedCoordinates(this._source.tileID).map((e=>new t.a3(e.canonical.z,e.wrap,e.canonical.z,e.canonical.x,e.canonical.y))):(o=Me(e,{tileSize:this.usedForTerrain?this.tileSize:this._source.tileSize,minzoom:this._source.minzoom,maxzoom:"vector"===this._source.type&&void 0!==this.map._zoomLevelsToOverscale?e.maxZoom-this.map._zoomLevelsToOverscale:this._source.maxzoom,roundZoom:!this.usedForTerrain&&this._source.roundZoom,reparseOverscaled:this._source.reparseOverscaled,terrain:i,calculateTileZoom:this._source.calculateTileZoom}),this._source.hasTile&&(o=o.filter((e=>this._source.hasTile(e))))):o=[],this.usedForTerrain&&(o=this._addTerrainIdealTiles(o));const a=0===o.length&&!this._updated&&this._didEmitContent;this._updated=!0,a&&this.fire(new t.n("data",{sourceDataType:"idle",dataType:"source",sourceId:this.id}));const r=Ce(e,this._source),s=this._updateRetainedTiles(o,r),n=Ee(this._source.type);n&&this._rasterFadeDuration>0&&!i&&function(e,i,o,a,r,s,n){const l=c(),h=t.at(i);for(const t of i){const i=e.getTileById(t.key);i.fadingDirection!==ue.Departing&&0!==i.fadeOpacity||i.resetFadeLogic(),Se(e,i,o,l,a,r,n)||ze(e,i,o,l,s,n)||De(i,h,l,n)||i.resetFadeLogic();}}(this._inViewTiles,o,s,this._maxFadingAncestorLevels,this._source.minzoom,this._source.maxzoom,this._rasterFadeDuration),n?this._cleanUpRasterTiles(s):this._cleanUpVectorTiles(s);}_cleanUpRasterTiles(e){for(const t of this._inViewTiles.getAllIds())e[t]||this._removeTile(t);}_cleanUpVectorTiles(e){for(const t of this._inViewTiles.getAllIds()){const i=this._inViewTiles.getTileById(t);e[t]?i.clearSymbolFadeHold():i.hasSymbolBuckets?i.holdingForSymbolFade()?i.symbolFadeFinished()&&this._removeTile(t):i.setSymbolHoldDuration(this.map._fadeDuration):this._removeTile(t);}}_addTerrainIdealTiles(e){const t=[];for(const i of e)if(i.canonical.z>this._source.minzoom){const e=i.scaledTo(i.canonical.z-1);t.push(e);const o=i.scaledTo(Math.max(this._source.minzoom,Math.min(i.canonical.z,5)));t.push(o);}return e.concat(t)}releaseSymbolFadeTiles(){for(const e of this._inViewTiles.getAllIds())this._inViewTiles.getTileById(e).holdingForSymbolFade()&&this._removeTile(e);}_updateRetainedTiles(e,t){var i;const o=new Set;for(const t of e)this._addTile(t).hasData()||o.add(t);const a=e.reduce(((e,t)=>(e[t.key]=t,e)),{}),r=this._retainLoadedChildren(a,o),s={},n=Math.max(t-Fe.maxUnderzooming,this._source.minzoom);for(const e of r){let t=this._inViewTiles.getTileById(e.key),o=null==t?void 0:t.wasRequested();for(let r=e.overscaledZ-1;r>=n;--r){const n=e.scaledTo(r);if(s[n.key])break;if(s[n.key]=!0,t=this.getTile(n),!t&&o&&(t=this._addTile(n)),t){const e=t.hasData();if((e||!(null===(i=this.map)||void 0===i?void 0:i.cancelPendingTileRequestsWhileZooming)||o)&&(a[n.key]=n),o=t.wasRequested(),e)break}}}return a}_addTile(e){let i=this._inViewTiles.getTileById(e.key);if(i)return i;i=this._outOfViewCache.getAndRemove(e),i&&(i.resetFadeLogic(),this._setTileReloadTimer(e.key,i),i.tileID=e,this._state.initializeTileState(i,this.map?this.map.painter:null));const o=i;return i||(i=new de(e,this._source.tileSize*e.overscaleFactor()),this._loadTile(i,e.key,i.state)),i.uses++,this._inViewTiles.setTile(e.key,i),o||this._source.fire(new t.n("dataloading",{tile:i,coord:i.tileID,dataType:"source"})),i}_setTileReloadTimer(e,t){this._clearTileReloadTimer(e);const i=t.getExpiryTimeout();i&&(this._timers[e]=setTimeout((()=>{this._reloadTile(e,"expired"),delete this._timers[e];}),i));}_clearTileReloadTimer(e){const t=this._timers[e];t&&(clearTimeout(t),delete this._timers[e]);}_resetTileReloadTimers(){for(const e in this._timers)clearTimeout(this._timers[e]),delete this._timers[e];for(const e of this._inViewTiles.getAllIds()){const t=this._inViewTiles.getTileById(e);this._setTileReloadTimer(e,t);}}refreshTiles(e){for(const t of this._inViewTiles.getAllIds()){const i=this._inViewTiles.getTileById(t);(this._inViewTiles.isIdRenderable(t)||"errored"==i.state)&&e.some((e=>e.equals(i.tileID.canonical)))&&this._reloadTile(t,"expired");}}_removeTile(e){const t=this._inViewTiles.getTileById(e);t&&(t.uses--,this._inViewTiles.deleteTileById(e),this._clearTileReloadTimer(e),t.uses>0||(t.hasData()&&"reloading"!==t.state?this._outOfViewCache.add(t.tileID,t,t.getExpiryTimeout()):(t.aborted=!0,this._abortTile(t),this._unloadTile(t))));}_dataHandler(e){"source"===e.dataType&&("metadata"!==e.sourceDataType?"content"===e.sourceDataType&&this._sourceLoaded&&!this._paused&&(this.reload(e.sourceDataChanged,e.shouldReloadTileOptions),this.transform&&this.update(this.transform,this.terrain),this._didEmitContent=!0):this._sourceLoaded=!0);}clearTiles(){this._shouldReloadOnResume=!1,this._paused=!1;for(const e of this._inViewTiles.getAllIds())this._removeTile(e);this._outOfViewCache.reset();}tilesIn(e,i,o){const a=[],r=this.transform;if(!r)return a;const s=r.getCoveringTilesDetailsProvider().allowWorldCopies(),n=o?r.getCameraQueryGeometry(e):e,l=e=>r.screenPointToMercatorCoordinate(e,this.terrain),c=this.transformBbox(e,l,!s),h=this.transformBbox(n,l,!s),u=this.getIds(),d=t.a8.fromPoints(h);for(const e of u){const o=this._inViewTiles.getTileById(e);if(o.holdingForSymbolFade())continue;const n=s?[o.tileID]:[o.tileID.unwrapTo(-1),o.tileID.unwrapTo(0)],l=Math.pow(2,r.zoom-o.tileID.overscaledZ),u=i*o.queryPadding*t.a6/o.tileSize/l;for(const e of n){const i=d.map((i=>e.getTilePoint(new t.a7(i.x,i.y))));if(i.expandBy(u),i.intersects(Ie)){const t=c.map((t=>e.getTilePoint(t))),i=h.map((t=>e.getTilePoint(t)));a.push({tile:o,tileID:s?e:e.unwrapTo(0),queryGeometry:t,cameraQueryGeometry:i,scale:l});}}}return a}transformBbox(e,i,o){let a=e.map(i);if(o){const o=t.a8.fromPoints(e);o.shrinkBy(.001*Math.min(o.width(),o.height()));const r=o.map(i);t.a8.fromPoints(a).covers(r)||(a=a.map((e=>e.x>.5?new t.a7(e.x-1,e.y,e.z):e)));}return a}getVisibleCoordinates(e){const t=this.getRenderableIds(e).map((e=>this._inViewTiles.getTileById(e).tileID));return this.transform&&this.transform.populateCache(t),t}hasTransition(){return !!this._source.hasTransition()||Ee(this._source.type)&&function(e,t){if(t<=0)return !1;const i=c();for(const t of e.getAllTiles())if(t.fadeEndTime>=i)return !0;return !1}(this._inViewTiles,this._rasterFadeDuration)}setRasterFadeDuration(e){this._rasterFadeDuration=e;}setFeatureState(e,i,o){e||(e=t.ag),this._state.updateState(e,i,o);}removeFeatureState(e,i,o){e||(e=t.ag),this._state.removeFeatureState(e,i,o);}getFeatureState(e,i){return e||(e=t.ag),this._state.getState(e,i)}setDependencies(e,t,i){const o=this._inViewTiles.getTileById(e);o&&o.setDependencies(t,i);}reloadTilesForDependencies(e,t){for(const i of this._inViewTiles.getAllIds())this._inViewTiles.getTileById(i).hasDependency(e,t)&&this._reloadTile(i,"reloading");this._outOfViewCache.filter((i=>!i.hasDependency(e,t)));}areTilesLoaded(){for(const e of this._inViewTiles.getAllTiles())if("loaded"!==e.state&&"errored"!==e.state)return !1;return !0}}Fe.maxUnderzooming=10,Fe.maxOverzooming=3;class ke{constructor(e,t){this.reset(e,t);}reset(e,t){this.points=e||[],this._distances=[0];for(let e=1;e0?(a-s)/n:0;return this.points[r].mult(1-l).add(this.points[i].mult(l))}}function Be(e,t){let i=!0;return "always"===e||"never"!==e&&"never"!==t||(i=!1),i}class Oe{constructor(e,t,i){const o=this.boxCells=[],a=this.circleCells=[];this.xCellCount=Math.ceil(e/i),this.yCellCount=Math.ceil(t/i);for(let e=0;ethis.width||o<0||t>this.height)return [];const n=[];if(e<=0&&t<=0&&this.width<=i&&this.height<=o){if(a)return [{key:null,x1:e,y1:t,x2:i,y2:o}];for(let e=0;e0}hitTestCircle(e,t,i,o,a){const r=e-i,s=e+i,n=t-i,l=t+i;if(s<0||r>this.width||l<0||n>this.height)return !1;const c=[];return this._forEachCell(r,n,s,l,this._queryCellCircle,c,{hitTest:!0,overlapMode:o,circle:{x:e,y:t,radius:i},seenUids:{box:{},circle:{}}},a),c.length>0}_queryCell(e,t,i,o,a,r,s,n){const{seenUids:l,hitTest:c,overlapMode:h}=s,u=this.boxCells[a],d=1e-6;if(null!==u){const a=this.bboxes;for(const s of u)if(!l.box[s]){l.box[s]=!0;const u=4*s,_=this.boxKeys[s];if(e<=a[u+2]+d&&t<=a[u+3]+d&&i>=a[u+0]-d&&o>=a[u+1]-d&&(!n||n(_))&&(!c||!Be(h,_.overlapMode))&&(r.push({key:_,x1:a[u],y1:a[u+1],x2:a[u+2],y2:a[u+3]}),c))return !0}}const _=this.circleCells[a];if(null!==_){const a=this.circles;for(const s of _)if(!l.circle[s]){l.circle[s]=!0;const u=3*s,d=this.circleKeys[s];if(this._circleAndRectCollide(a[u],a[u+1],a[u+2],e,t,i,o)&&(!n||n(d))&&(!c||!Be(h,d.overlapMode))){const e=a[u],t=a[u+1],i=a[u+2];if(r.push({key:d,x1:e-i,y1:t-i,x2:e+i,y2:t+i}),c)return !0}}}return !1}_queryCellCircle(e,t,i,o,a,r,s,n){const{circle:l,seenUids:c,overlapMode:h}=s,u=this.boxCells[a];if(null!==u){const e=this.bboxes;for(const t of u)if(!c.box[t]){c.box[t]=!0;const i=4*t,o=this.boxKeys[t];if(this._circleAndRectCollide(l.x,l.y,l.radius,e[i+0],e[i+1],e[i+2],e[i+3])&&(!n||n(o))&&!Be(h,o.overlapMode))return r.push(!0),!0}}const d=this.circleCells[a];if(null!==d){const e=this.circles;for(const t of d)if(!c.circle[t]){c.circle[t]=!0;const i=3*t,o=this.circleKeys[t];if(this._circlesCollide(e[i],e[i+1],e[i+2],l.x,l.y,l.radius)&&(!n||n(o))&&!Be(h,o.overlapMode))return r.push(!0),!0}}}_forEachCell(e,t,i,o,a,r,s,n){const l=this._convertToXCellCoord(e),c=this._convertToYCellCoord(t),h=this._convertToXCellCoord(i),u=this._convertToYCellCoord(o);for(let d=l;d<=h;d++)for(let l=c;l<=u;l++)if(a.call(this,e,t,i,o,this.xCellCount*l+d,r,s,n))return}_convertToXCellCoord(e){return Math.max(0,Math.min(this.xCellCount-1,Math.floor(e*this.xScale)))}_convertToYCellCoord(e){return Math.max(0,Math.min(this.yCellCount-1,Math.floor(e*this.yScale)))}_circlesCollide(e,t,i,o,a,r){const s=o-e,n=a-t,l=i+r;return l*l>s*s+n*n}_circleAndRectCollide(e,t,i,o,a,r,s){const n=(r-o)/2,l=Math.abs(e-(o+n));if(l>n+i)return !1;const c=(s-a)/2,h=Math.abs(t-(a+c));if(h>c+i)return !1;if(l<=n||h<=c)return !0;const u=l-n,d=h-c;return u*u+d*d<=i*i}}function je(e,t){const i=1/(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]),o=1/(t[8]*t[8]+t[9]*t[9]+t[10]*t[10]),a=t[0]*i,r=t[4]*i,s=t[8]*o,n=t[1]*i,l=t[5]*i,c=t[9]*o,h=t[2]*i,u=t[6]*i,d=t[10]*o;e[0]=a,e[1]=r,e[2]=s,e[4]=n,e[5]=l,e[6]=c,e[8]=h,e[9]=u,e[10]=d;const _=t[12],p=t[13],m=t[14];return e[12]=-a*_-n*p-h*m,e[13]=-r*_-l*p-u*m,e[14]=-s*_-c*p-d*m,e[3]=0,e[7]=0,e[11]=0,e[15]=1,e}const Ne=t.O();function Ze(e,i,a){const r=t.O();if(!e){const{vecSouth:e,vecEast:t}=Ge(i),a=o();a[0]=t[0],a[1]=t[1],a[2]=e[0],a[3]=e[1],s=a,(d=(l=(n=a)[0])*(u=n[3])-(h=n[2])*(c=n[1]))&&(s[0]=u*(d=1/d),s[1]=-c*d,s[2]=-h*d,s[3]=l*d),r[0]=a[0],r[1]=a[1],r[4]=a[2],r[5]=a[3];}var s,n,l,c,h,u,d;return t.S(r,r,[1/a,1/a,1]),r}function Ue(e,i,o,a){if(e){const e=t.O();if(!i){const{vecSouth:t,vecEast:i}=Ge(o);e[0]=i[0],e[1]=i[1],e[4]=t[0],e[5]=t[1];}return t.S(e,e,[a,a,1]),e}return o.pixelsToClipSpaceMatrix}function Ge(e){const i=Math.cos(e.rollInRadians),o=Math.sin(e.rollInRadians),a=Math.cos(e.pitchInRadians),r=Math.cos(e.bearingInRadians),s=Math.sin(e.bearingInRadians),n=t.az();n[0]=-r*a*o-s*i,n[1]=-s*a*o+r*i;const l=t.aA(n);l<1e-9?t.aB(n):t.aC(n,n,1/l);const c=t.az();c[0]=r*a*i-s*o,c[1]=s*a*i+r*o;const h=t.aA(c);return h<1e-9?t.aB(c):t.aC(c,c,1/h),{vecEast:c,vecSouth:n}}function Ve(e,i,o,a){let r;a?(r=[e,i,a(e,i),1],t.aE(r,r,o)):(r=[e,i,0,1],nt(r,r,o));const s=r[3];return {point:new t.P(r[0]/s,r[1]/s),signedDistanceFromCamera:s,isOccluded:!1}}function We(e,t){return .5+e/t*.5}function qe(e,t){return e.x>=-t[0]&&e.x<=t[0]&&e.y>=-t[1]&&e.y<=t[1]}function $e(e,i,o,a,r,s,n,l,c,h,u,d,_){const p=o?e.textSizeData:e.iconSizeData,m=t.aw(p,i.transform.zoom),f=[256/i.width*2+1,256/i.height*2+1],g=o?e.text.dynamicLayoutVertexArray:e.icon.dynamicLayoutVertexArray;g.clear();const v=e.lineVertexArray,x=o?e.text.placedSymbolArray:e.icon.placedSymbolArray,b=i.transform.width/i.transform.height;let y=!1;for(let o=0;oMath.abs(o.x-i.x)*a?{useVertical:!0}:(e===t.ax.vertical?i.yo.x)?{needsFlipping:!0}:null}function Ke(e){const{projectionContext:i,pitchedLabelPlaneMatrixInverse:o,symbol:a,fontSize:r,flip:s,keepUpright:n,glyphOffsetArray:l,dynamicLayoutVertexArray:c,aspectRatio:h,rotateToLine:u}=e,d=r/24,_=a.lineOffsetX*d,p=a.lineOffsetY*d;let m;if(a.numGlyphs>1){const e=a.glyphStartIndex+a.numGlyphs,t=a.lineStartIndex,r=a.lineStartIndex+a.lineLength,c=He(d,l,_,p,s,a,u,i);if(!c)return {notEnoughRoom:!0};const f=et(c.first.point.x,c.first.point.y,i,o),g=et(c.last.point.x,c.last.point.y,i,o);if(n&&!s){const e=Xe(a.writingMode,f,g,h);if(e)return e}m=[c.first];for(let o=a.glyphStartIndex+1;o0?n.point:Ye(i.tileAnchorPoint,s,e,1,i),c=et(e.x,e.y,i,o),u=et(l.x,l.y,i,o),d=Xe(a.writingMode,c,u,h);if(d)return d}const e=at(d*l.getoffsetX(a.glyphStartIndex),_,p,s,a.segment,a.lineStartIndex,a.lineStartIndex+a.lineLength,i,u);if(!e||i.projectionCache.anyProjectionOccluded)return {notEnoughRoom:!0};m=[e];}for(const e of m)t.aD(c,e.point,e.angle);return {}}function Ye(e,t,i,o,a){const r=e.add(e.sub(t)._unit()),s=Je(r.x,r.y,a).point,n=i.sub(s);return i.add(n._mult(o/n.mag()))}function Qe(e,i,o){const a=i.projectionCache;if(a.projections[e])return a.projections[e];const r=new t.P(i.lineVertexArray.getx(e),i.lineVertexArray.gety(e)),s=Je(r.x,r.y,i);if(s.signedDistanceFromCamera>0)return a.projections[e]=s.point,a.anyProjectionOccluded||(a.anyProjectionOccluded=s.isOccluded),s.point;const n=e-o.direction;return Ye(0===o.distanceFromAnchor?i.tileAnchorPoint:new t.P(i.lineVertexArray.getx(n),i.lineVertexArray.gety(n)),r,o.previousVertex,o.absOffsetX-o.distanceFromAnchor+1,i)}function Je(e,t,i){const o=e+i.translation[0],a=t+i.translation[1];let r;return i.pitchWithMap?(r=Ve(o,a,i.pitchedLabelPlaneMatrix,i.getElevation),r.isOccluded=!1):(r=i.transform.projectTileCoordinates(o,a,i.unwrappedTileID,i.getElevation),r.point.x=(.5*r.point.x+.5)*i.width,r.point.y=(.5*-r.point.y+.5)*i.height),r}function et(e,i,o,a){if(o.pitchWithMap){const r=[e,i,0,1];return t.aE(r,r,a),o.transform.projectTileCoordinates(r[0]/r[3],r[1]/r[3],o.unwrappedTileID,o.getElevation).point}return {x:e/o.width*2-1,y:1-i/o.height*2}}function tt(e,t,i){return i.transform.projectTileCoordinates(e,t,i.unwrappedTileID,i.getElevation)}function it(e,t,i){return e._unit()._perp()._mult(t*i)}function ot(e,i,o,a,r,s,n,l,c){if(l.projectionCache.offsets[e])return l.projectionCache.offsets[e];const h=o.add(i);if(e+c.direction=r)return l.projectionCache.offsets[e]=h,h;const u=Qe(e+c.direction,l,c),d=it(u.sub(o),n,c.direction),_=o.add(d),p=u.add(d);return l.projectionCache.offsets[e]=t.aF(s,h,_,p)||h,l.projectionCache.offsets[e]}function at(e,t,i,o,a,r,s,n,l){const c=o?e-t:e+t;let h=c>0?1:-1,u=0;o&&(h*=-1,u=Math.PI),h<0&&(u+=Math.PI);let d,_=h>0?r+a:r+a+1;n.projectionCache.cachedAnchorPoint?d=n.projectionCache.cachedAnchorPoint:(d=Je(n.tileAnchorPoint.x,n.tileAnchorPoint.y,n).point,n.projectionCache.cachedAnchorPoint=d);let p,m,f=d,g=d,v=0,x=0;const b=Math.abs(c),y=[];let w;for(;v+x<=b;){if(_+=h,_=s)return null;v+=x,g=f,m=p;const e={absOffsetX:b,direction:h,distanceFromAnchor:v,previousVertex:g};if(f=Qe(_,n,e),0===i)y.push(g),w=f.sub(g);else {let t;const o=f.sub(g);t=0===o.mag()?it(Qe(_+h,n,e).sub(f),i,h):it(o,i,h),m||(m=g.add(t)),p=ot(_,t,f,r,s,m,i,n,e),y.push(m),w=p.sub(m);}x=w.mag();}const T=w._mult((b-v)/x)._add(m||g),P=u+Math.atan2(f.y-g.y,f.x-g.x);return y.push(T),{point:T,angle:l?P:0,path:y}}const rt=new Float32Array([-1/0,-1/0,0,-1/0,-1/0,0,-1/0,-1/0,0,-1/0,-1/0,0]);function st(e,t){for(let i=0;i=1;e--)_.push(s.path[e]);for(let e=1;ee.signedDistanceFromCamera<=0))?[]:e.map((e=>e.point));}let f=[];if(_.length>0){const e=_[0].clone(),i=_[0].clone();for(let t=1;t<_.length;t++)e.x=Math.min(e.x,_[t].x),e.y=Math.min(e.y,_[t].y),i.x=Math.max(i.x,_[t].x),i.y=Math.max(i.y,_[t].y);f=e.x>=o.x&&i.x<=a.x&&e.y>=o.y&&i.y<=a.y?[_]:i.xa.x||i.ya.y?[]:t.aG([_],o.x,o.y,a.x,a.y);}for(const t of f){r.reset(t,.25*i);let o=0;o=r.length<=.5*i?1:Math.ceil(r.paddedLength/p)+1;for(let t=0;t{const o=Ve(e.x,e.y,i,t.getElevation),a=t.transform.projectTileCoordinates(o.point.x,o.point.y,t.unwrappedTileID,t.getElevation);return a.point.x=(.5*a.point.x+.5)*t.width,a.point.y=(.5*-a.point.y+.5)*t.height,a}))}(e,t);return function(e){let t=0,i=0,o=0,a=0;for(let r=0;ri&&(i=a,t=o));return e.slice(t,t+i)}(i)}queryRenderedSymbols(e){if(0===e.length||0===this.grid.keysLength()&&0===this.ignoredGrid.keysLength())return {};const i=[],o=new t.a8;for(const a of e){const e=new t.P(a.x+lt,a.y+lt);o.extend(e),i.push(e);}const{minX:a,minY:r,maxX:s,maxY:n}=o,l=this.grid.query(a,r,s,n).concat(this.ignoredGrid.query(a,r,s,n)),c={},h={};for(const e of l){const o=e.key;if(void 0===c[o.bucketInstanceId]&&(c[o.bucketInstanceId]={}),c[o.bucketInstanceId][o.featureIndex])continue;const a=[new t.P(e.x1,e.y1),new t.P(e.x2,e.y1),new t.P(e.x2,e.y2),new t.P(e.x1,e.y2)];t.aH(i,a)&&(c[o.bucketInstanceId][o.featureIndex]=!0,void 0===h[o.bucketInstanceId]&&(h[o.bucketInstanceId]=[]),h[o.bucketInstanceId].push(o.featureIndex));}return h}insertCollisionBox(e,t,i,o,a,r){(i?this.ignoredGrid:this.grid).insert({bucketInstanceId:o,featureIndex:a,collisionGroupID:r,overlapMode:t},e[0],e[1],e[2],e[3]);}insertCollisionCircles(e,t,i,o,a,r){const s=i?this.ignoredGrid:this.grid,n={bucketInstanceId:o,featureIndex:a,collisionGroupID:r,overlapMode:t};for(let t=0;t=this.screenRightBoundary||othis.screenBottomBoundary}isInsideGrid(e,t,i,o){return i>=0&&e=0&&tthis.projectAndGetPerspectiveRatio(e.x,e.y,a,c,u)));S=e.some((e=>!e.isOccluded)),E=e.map((e=>new t.P(e.x,e.y)));}else S=!0;return {box:t.aI(E),allPointsOccluded:!S}}}class ht{constructor(e,t,i,o){this.opacity=e?Math.max(0,Math.min(1,e.opacity+(e.placed?t:-t))):o&&i?1:0,this.placed=i;}isHidden(){return 0===this.opacity&&!this.placed}}class ut{constructor(e,t,i,o,a){this.text=new ht(e?e.text:null,t,i,a),this.icon=new ht(e?e.icon:null,t,o,a);}isHidden(){return this.text.isHidden()&&this.icon.isHidden()}}class dt{constructor(e,t,i){this.text=e,this.icon=t,this.skipFade=i;}}class _t{constructor(e,t,i,o,a){this.bucketInstanceId=e,this.featureIndex=t,this.sourceLayerIndex=i,this.bucketIndex=o,this.tileID=a;}}class pt{constructor(e){this.crossSourceCollisions=e,this.maxGroupID=0,this.collisionGroups={};}get(e){if(this.crossSourceCollisions)return {ID:0,predicate:null};if(!this.collisionGroups[e]){const t=++this.maxGroupID;this.collisionGroups[e]={ID:t,predicate:e=>e.collisionGroupID===t};}return this.collisionGroups[e]}}function mt(e,i,o,a,r){const{horizontalAlign:s,verticalAlign:n}=t.aP(e);return new t.P(-(s-.5)*i+a[0]*r,-(n-.5)*o+a[1]*r)}class ft{constructor(e,t,i,o,a){this.transform=e.clone(),this.terrain=t,this.collisionIndex=new ct(this.transform),this.placements={},this.opacities={},this.variableOffsets={},this.stale=!1,this.commitTime=0,this.fadeDuration=i,this.retainedQueryData={},this.collisionGroups=new pt(o),this.collisionCircleArrays={},this.collisionBoxArrays=new Map,this.prevPlacement=a,a&&(a.prevPlacement=void 0),this.placedOrientations={};}_getTerrainElevationFunc(e){const t=this.terrain;return t?(i,o)=>t.getElevation(e,i,o):null}getBucketParts(e,i,o,a){const r=o.getBucket(i),s=o.latestFeatureIndex;if(!r||!s||i.id!==r.layerIds[0])return;const n=o.collisionBoxArray,l=r.layers[0].layout,c=r.layers[0].paint,h=Math.pow(2,this.transform.zoom-o.tileID.overscaledZ),u=o.tileSize/t.a6,d=o.tileID.toUnwrapped(),_="map"===l.get("text-rotation-alignment"),p=t.aK(o,1,this.transform.zoom),m=t.aL(this.collisionIndex.transform,o,c.get("text-translate"),c.get("text-translate-anchor")),f=t.aL(this.collisionIndex.transform,o,c.get("icon-translate"),c.get("icon-translate-anchor")),g=Ze(_,this.transform,p);this.retainedQueryData[r.bucketInstanceId]=new _t(r.bucketInstanceId,s,r.sourceLayerIndex,r.index,o.tileID);const v={bucket:r,layout:l,translationText:m,translationIcon:f,unwrappedTileID:d,pitchedLabelPlaneMatrix:g,scale:h,textPixelRatio:u,holdingForFade:o.holdingForSymbolFade(),collisionBoxArray:n,partiallyEvaluatedTextSize:t.aw(r.textSizeData,this.transform.zoom),collisionGroup:this.collisionGroups.get(r.sourceID)};if(a)for(const t of r.sortKeyRanges){const{sortKey:i,symbolInstanceStart:o,symbolInstanceEnd:a}=t;e.push({sortKey:i,symbolInstanceStart:o,symbolInstanceEnd:a,parameters:v});}else e.push({symbolInstanceStart:0,symbolInstanceEnd:r.symbolInstances.length,parameters:v});}attemptAnchorPlacement(e,i,o,a,r,s,n,l,c,h,u,d,_,p,m,f,g,v,x,b){var y,w,T;const P=t.aM[e.textAnchor],C=[e.textOffset0,e.textOffset1],M=mt(P,o,a,C,r),I=this.collisionIndex.placeCollisionBox(i,d,l,c,h,n,s,f,u.predicate,x,M,b);if((!v||this.collisionIndex.placeCollisionBox(v,d,l,c,h,n,s,g,u.predicate,x,M,b).placeable)&&I.placeable){let e;if((null===(y=this.prevPlacement)||void 0===y?void 0:y.variableOffsets[_.crossTileID])&&(null===(T=null===(w=this.prevPlacement)||void 0===w?void 0:w.placements[_.crossTileID])||void 0===T?void 0:T.text)&&(e=this.prevPlacement.variableOffsets[_.crossTileID].anchor),0===_.crossTileID)throw new Error("symbolInstance.crossTileID can't be 0");return this.variableOffsets[_.crossTileID]={textOffset:C,width:o,height:a,anchor:P,textBoxScale:r,prevAnchor:e},this.markUsedJustification(p,P,_,m),p.allowVerticalPlacement&&(this.markUsedOrientation(p,m,_),this.placedOrientations[_.crossTileID]=m),{shift:M,placedGlyphBoxes:I}}}placeLayerBucketPart(e,i,o){const{bucket:a,layout:r,translationText:s,translationIcon:n,unwrappedTileID:l,pitchedLabelPlaneMatrix:c,textPixelRatio:h,holdingForFade:u,collisionBoxArray:d,partiallyEvaluatedTextSize:_,collisionGroup:p}=e.parameters,m=r.get("text-optional"),f=r.get("icon-optional"),g=t.aN(r,"text-overlap","text-allow-overlap"),v="always"===g,x=t.aN(r,"icon-overlap","icon-allow-overlap"),b="always"===x,y="map"===r.get("text-rotation-alignment"),w="map"===r.get("text-pitch-alignment"),T="none"!==r.get("icon-text-fit"),P="viewport-y"===r.get("symbol-z-order"),C=v&&(b||!a.hasIconData()||f),M=b&&(v||!a.hasTextData()||m);!a.collisionArrays&&d&&a.deserializeCollisionBoxes(d);const I=this.retainedQueryData[a.bucketInstanceId].tileID,E=this._getTerrainElevationFunc(I),S=this.transform.getFastPathSimpleProjectionMatrix(I),z=(e,d,b)=>{var P,z;if(i[e.crossTileID])return;if(u)return void(this.placements[e.crossTileID]=new dt(!1,!1,!1));let R=!1,D=!1,A=!0,L=null,F={box:null,placeable:!1,offscreen:null,occluded:!1},k={placeable:!1},B=null,O=null,j=null,N=0,Z=0,U=0;d.textFeatureIndex?N=d.textFeatureIndex:e.useRuntimeCollisionCircles&&(N=e.featureIndex),d.verticalTextFeatureIndex&&(Z=d.verticalTextFeatureIndex);const G=d.textBox;if(G){const i=i=>{let o=t.ax.horizontal;if(a.allowVerticalPlacement&&!i&&this.prevPlacement){const t=this.prevPlacement.placedOrientations[e.crossTileID];t&&(this.placedOrientations[e.crossTileID]=t,o=t,this.markUsedOrientation(a,o,e));}return o},r=(i,o)=>{if(a.allowVerticalPlacement&&e.numVerticalGlyphVertices>0&&d.verticalTextBox){for(const e of a.writingModes)if(e===t.ax.vertical?(F=o(),k=F):F=i(),null==F?void 0:F.placeable)break}else F=i();},c=e.textAnchorOffsetStartIndex,u=e.textAnchorOffsetEndIndex;if(u===c){const o=(t,i)=>{const o=this.collisionIndex.placeCollisionBox(t,g,h,I,l,w,y,s,p.predicate,E,void 0,S);return (null==o?void 0:o.placeable)&&(this.markUsedOrientation(a,i,e),this.placedOrientations[e.crossTileID]=i),o};r((()=>o(G,t.ax.horizontal)),(()=>{const i=d.verticalTextBox;return a.allowVerticalPlacement&&e.numVerticalGlyphVertices>0&&i?o(i,t.ax.vertical):{box:null,offscreen:null}})),i(null==F?void 0:F.placeable);}else {let _=t.aM[null===(z=null===(P=this.prevPlacement)||void 0===P?void 0:P.variableOffsets[e.crossTileID])||void 0===z?void 0:z.anchor];const m=(t,i,r)=>{const d=t.x2-t.x1,m=t.y2-t.y1,f=e.textBoxScale,v=T&&"never"===x?i:null;let b=null,P="never"===g?1:2,C="never";_&&P++;for(let i=0;im(G,d.iconBox,t.ax.horizontal)),(()=>{const i=d.verticalTextBox;return a.allowVerticalPlacement&&!(null==F?void 0:F.placeable)&&e.numVerticalGlyphVertices>0&&i?m(i,d.verticalIconBox,t.ax.vertical):{box:null,occluded:!0,offscreen:null}})),F&&(R=F.placeable,A=F.offscreen);const f=i(null==F?void 0:F.placeable);if(!R&&this.prevPlacement){const t=this.prevPlacement.variableOffsets[e.crossTileID];t&&(this.variableOffsets[e.crossTileID]=t,this.markUsedJustification(a,t.anchor,e,f));}}}if(B=F,R=null==B?void 0:B.placeable,A=null==B?void 0:B.offscreen,e.useRuntimeCollisionCircles&&e.centerJustifiedTextSymbolIndex>=0){const i=a.text.placedSymbolArray.get(e.centerJustifiedTextSymbolIndex),n=t.ay(a.textSizeData,_,i),h=r.get("text-padding");O=this.collisionIndex.placeCollisionCircles(g,i,a.lineVertexArray,a.glyphOffsetArray,n,l,c,o,w,p.predicate,e.collisionCircleDiameter,h,s,E),O.circles.length&&O.collisionDetected&&!o&&t.w("Collisions detected, but collision boxes are not shown"),R=v||O.circles.length>0&&!O.collisionDetected,A&&(A=O.offscreen);}if(d.iconFeatureIndex&&(U=d.iconFeatureIndex),d.iconBox){const e=e=>this.collisionIndex.placeCollisionBox(e,x,h,I,l,w,y,n,p.predicate,E,T&&L?L:void 0,S);k&&k.placeable&&d.verticalIconBox?(j=e(d.verticalIconBox),D=j.placeable):(j=e(d.iconBox),D=j.placeable),A&&(A=j.offscreen);}const V=m||0===e.numHorizontalGlyphVertices&&0===e.numVerticalGlyphVertices,W=f||0===e.numIconVertices;V||W?W?V||D&&(D=R):R=D&&R:D=R=D&&R;const q=D&&j.placeable;if(R&&B.placeable&&this.collisionIndex.insertCollisionBox(B.box,g,r.get("text-ignore-placement"),a.bucketInstanceId,k&&k.placeable&&Z?Z:N,p.ID),q&&this.collisionIndex.insertCollisionBox(j.box,x,r.get("icon-ignore-placement"),a.bucketInstanceId,U,p.ID),O&&R&&this.collisionIndex.insertCollisionCircles(O.circles,g,r.get("text-ignore-placement"),a.bucketInstanceId,N,p.ID),o&&this.storeCollisionData(a.bucketInstanceId,b,d,B,j,O),0===e.crossTileID)throw new Error("symbolInstance.crossTileID can't be 0");if(0===a.bucketInstanceId)throw new Error("bucket.bucketInstanceId can't be 0");this.placements[e.crossTileID]=new dt((R||C)&&!(null==B?void 0:B.occluded),(D||M)&&!(null==j?void 0:j.occluded),A||a.justReloaded),i[e.crossTileID]=!0;};if(P){if(0!==e.symbolInstanceStart)throw new Error("bucket.bucketInstanceId should be 0");const t=a.getSortedSymbolIndexes(-this.transform.bearingInRadians);for(let e=t.length-1;e>=0;--e){const i=t[e];z(a.symbolInstances.get(i),a.collisionArrays[i],i);}}else for(let t=e.symbolInstanceStart;t=0&&(e.text.placedSymbolArray.get(t).crossTileID=r>=0&&t!==r?0:o.crossTileID);}markUsedOrientation(e,i,o){const a=i===t.ax.horizontal||i===t.ax.horizontalOnly?i:0,r=i===t.ax.vertical?i:0,s=[o.leftJustifiedTextSymbolIndex,o.centerJustifiedTextSymbolIndex,o.rightJustifiedTextSymbolIndex];for(const t of s)e.text.placedSymbolArray.get(t).placedOrientation=a;o.verticalPlacedTextSymbolIndex&&(e.text.placedSymbolArray.get(o.verticalPlacedTextSymbolIndex).placedOrientation=r);}commit(e){this.commitTime=e,this.zoomAtLastRecencyCheck=this.transform.zoom;const t=this.prevPlacement;let i=!1;this.prevZoomAdjustment=t?t.zoomAdjustment(this.transform.zoom):0;const o=t?t.symbolFadeChange(e):1,a=t?t.opacities:{},r=t?t.variableOffsets:{},s=t?t.placedOrientations:{};for(const e in this.placements){const t=this.placements[e],r=a[e];r?(this.opacities[e]=new ut(r,o,t.text,t.icon),i||(i=t.text!==r.text.placed),i||(i=t.icon!==r.icon.placed)):(this.opacities[e]=new ut(null,o,t.text,t.icon,t.skipFade),i||(i=t.text||t.icon));}for(const e in a){const t=a[e];if(!this.opacities[e]){const a=new ut(t,o,!1,!1);a.isHidden()||(this.opacities[e]=a,i||(i=t.text.placed),i||(i=t.icon.placed));}}for(const e in r)this.variableOffsets[e]||!this.opacities[e]||this.opacities[e].isHidden()||(this.variableOffsets[e]=r[e]);for(const e in s)this.placedOrientations[e]||!this.opacities[e]||this.opacities[e].isHidden()||(this.placedOrientations[e]=s[e]);if(t&&void 0===t.lastPlacementChangeTime)throw new Error("Last placement time for previous placement is not defined");i?this.lastPlacementChangeTime=e:"number"!=typeof this.lastPlacementChangeTime&&(this.lastPlacementChangeTime=t?t.lastPlacementChangeTime:e);}updateLayerOpacities(e,t){const i={};for(const o of t){const t=o.getBucket(e);t&&o.latestFeatureIndex&&e.id===t.layerIds[0]&&this.updateBucketOpacities(t,o.tileID,i,o.collisionBoxArray);}}updateBucketOpacities(e,i,o,a){e.hasTextData()&&(e.text.opacityVertexArray.clear(),e.text.hasVisibleVertices=!1),e.hasIconData()&&(e.icon.opacityVertexArray.clear(),e.icon.hasVisibleVertices=!1),e.hasIconCollisionBoxData()&&e.iconCollisionBox.collisionVertexArray.clear(),e.hasTextCollisionBoxData()&&e.textCollisionBox.collisionVertexArray.clear();const r=e.layers[0],s=r.layout,n=new ut(null,0,!1,!1,!0),l=s.get("text-allow-overlap"),c=s.get("icon-allow-overlap"),h=r._unevaluatedLayout.hasValue("text-variable-anchor")||r._unevaluatedLayout.hasValue("text-variable-anchor-offset"),u="map"===s.get("text-rotation-alignment"),d="map"===s.get("text-pitch-alignment"),_="none"!==s.get("icon-text-fit"),p=new ut(null,0,l&&(c||!e.hasIconData()||s.get("icon-optional")),c&&(l||!e.hasTextData()||s.get("text-optional")),!0);!e.collisionArrays&&a&&(e.hasIconCollisionBoxData()||e.hasTextCollisionBoxData())&&e.deserializeCollisionBoxes(a);const m=(e,t,i)=>{for(let o=0;o0,v=this.placedOrientations[a.crossTileID],x=v===t.ax.vertical,b=v===t.ax.horizontal||v===t.ax.horizontalOnly;if(r>0||s>0){const t=Ct(c.text);m(e.text,r,x?Mt:t),m(e.text,s,b?Mt:t);const i=c.text.isHidden(),o=[a.rightJustifiedTextSymbolIndex,a.centerJustifiedTextSymbolIndex,a.leftJustifiedTextSymbolIndex];for(const t of o)t>=0&&(e.text.placedSymbolArray.get(t).hidden=i||x?1:0);a.verticalPlacedTextSymbolIndex>=0&&(e.text.placedSymbolArray.get(a.verticalPlacedTextSymbolIndex).hidden=i||b?1:0);const n=this.variableOffsets[a.crossTileID];n&&this.markUsedJustification(e,n.anchor,a,v);const l=this.placedOrientations[a.crossTileID];l&&(this.markUsedJustification(e,"left",a,l),this.markUsedOrientation(e,l,a));}if(g){const t=Ct(c.icon),i=!(_&&a.verticalPlacedIconSymbolIndex&&x);a.placedIconSymbolIndex>=0&&(m(e.icon,a.numIconVertices,i?t:Mt),e.icon.placedSymbolArray.get(a.placedIconSymbolIndex).hidden=c.icon.isHidden()),a.verticalPlacedIconSymbolIndex>=0&&(m(e.icon,a.numVerticalIconVertices,i?Mt:t),e.icon.placedSymbolArray.get(a.verticalPlacedIconSymbolIndex).hidden=c.icon.isHidden());}const y=(null==f?void 0:f.has(i))?f.get(i):{text:null,icon:null};if(e.hasIconCollisionBoxData()||e.hasTextCollisionBoxData()){const o=e.collisionArrays[i];if(o){let i=new t.P(0,0);if(o.textBox||o.verticalTextBox){let t=!0;if(h){const e=this.variableOffsets[l];e?(i=mt(e.anchor,e.width,e.height,e.textOffset,e.textBoxScale),u&&i._rotate(d?-this.transform.bearingInRadians:this.transform.bearingInRadians)):t=!1;}if(o.textBox||o.verticalTextBox){let a;o.textBox&&(a=x),o.verticalTextBox&&(a=b),gt(e.textCollisionBox.collisionVertexArray,c.text.placed,!t||a,y.text,i.x,i.y);}}if(o.iconBox||o.verticalIconBox){const t=Boolean(!b&&o.verticalIconBox);let a;o.iconBox&&(a=t),o.verticalIconBox&&(a=!t),gt(e.iconCollisionBox.collisionVertexArray,c.icon.placed,a,y.icon,_?i.x:0,_?i.y:0);}}}}if(e.sortFeatures(-this.transform.bearingInRadians),this.retainedQueryData[e.bucketInstanceId]&&(this.retainedQueryData[e.bucketInstanceId].featureSortOrder=e.featureSortOrder),e.hasTextData()&&e.text.opacityVertexBuffer&&e.text.opacityVertexBuffer.updateData(e.text.opacityVertexArray),e.hasIconData()&&e.icon.opacityVertexBuffer&&e.icon.opacityVertexBuffer.updateData(e.icon.opacityVertexArray),e.hasIconCollisionBoxData()&&e.iconCollisionBox.collisionVertexBuffer&&e.iconCollisionBox.collisionVertexBuffer.updateData(e.iconCollisionBox.collisionVertexArray),e.hasTextCollisionBoxData()&&e.textCollisionBox.collisionVertexBuffer&&e.textCollisionBox.collisionVertexBuffer.updateData(e.textCollisionBox.collisionVertexArray),e.text.opacityVertexArray.length!==e.text.layoutVertexArray.length/4)throw new Error(`bucket.text.opacityVertexArray.length (= ${e.text.opacityVertexArray.length}) !== bucket.text.layoutVertexArray.length (= ${e.text.layoutVertexArray.length}) / 4`);if(e.icon.opacityVertexArray.length!==e.icon.layoutVertexArray.length/4)throw new Error(`bucket.icon.opacityVertexArray.length (= ${e.icon.opacityVertexArray.length}) !== bucket.icon.layoutVertexArray.length (= ${e.icon.layoutVertexArray.length}) / 4`);e.bucketInstanceId in this.collisionCircleArrays&&(e.collisionCircleArray=this.collisionCircleArrays[e.bucketInstanceId],delete this.collisionCircleArrays[e.bucketInstanceId]);}symbolFadeChange(e){return 0===this.fadeDuration?1:(e-this.commitTime)/this.fadeDuration+this.prevZoomAdjustment}zoomAdjustment(e){return Math.max(0,(this.transform.zoom-e)/1.5)}hasTransitions(e){return this.stale||e-this.lastPlacementChangeTimee}setStale(){this.stale=!0;}}function gt(e,t,i,o,a,r){o&&0!==o.length||(o=[0,0,0,0]);const s=o[0]-lt,n=o[1]-lt,l=o[2]-lt,c=o[3]-lt;e.emplaceBack(t?1:0,i?1:0,a||0,r||0,s,n),e.emplaceBack(t?1:0,i?1:0,a||0,r||0,l,n),e.emplaceBack(t?1:0,i?1:0,a||0,r||0,l,c),e.emplaceBack(t?1:0,i?1:0,a||0,r||0,s,c);}const vt=Math.pow(2,25),xt=Math.pow(2,24),bt=Math.pow(2,17),yt=Math.pow(2,16),wt=Math.pow(2,9),Tt=Math.pow(2,8),Pt=Math.pow(2,1);function Ct(e){if(0===e.opacity&&!e.placed)return 0;if(1===e.opacity&&e.placed)return 4294967295;const t=e.placed?1:0,i=Math.floor(127*e.opacity);return i*vt+t*xt+i*bt+t*yt+i*wt+t*Tt+i*Pt+t}const Mt=0;class It{constructor(e){this._sortAcrossTiles="viewport-y"!==e.layout.get("symbol-z-order")&&!e.layout.get("symbol-sort-key").isConstant(),this._currentTileIndex=0,this._currentPartIndex=0,this._seenCrossTileIDs={},this._bucketParts=[];}continuePlacement(e,t,i,o,a){const r=this._bucketParts;for(;this._currentTileIndexe.sortKey-t.sortKey)));this._currentPartIndex!this._forceFullPlacement&&c()-a>2;for(;this._currentPlacementIndex>=0;){const a=i[e[this._currentPlacementIndex]],s=this.placement.collisionIndex.transform.zoom;if(t.aQ(a)&&a.layout&&(!a.minzoom||a.minzoom<=s)&&(!a.maxzoom||a.maxzoom>s)){if(this._inProgressLayer||(this._inProgressLayer=new It(a)),this._inProgressLayer.continuePlacement(o[a.source],this.placement,this._showCollisionBoxes,a,r))return;delete this._inProgressLayer;}this._currentPlacementIndex--;}this._done=!0;}commit(e){return this.placement.commit(e),this.placement}}const St=512/t.a6/2;class zt{constructor(e,i,o){this.tileID=e,this.bucketInstanceId=o,this._symbolsByKey={};const a=new Map;for(let e=0;e({x:Math.floor(e.anchorX*St),y:Math.floor(e.anchorY*St)}))),crossTileIDs:i.map((e=>e.crossTileID))};if(o.positions.length>128){const e=new t.aR(o.positions.length,16,Uint16Array);for(const{x:t,y:i}of o.positions)e.add(t,i);e.finish(),delete o.positions,o.index=e;}this._symbolsByKey[e]=o;}}getScaledCoordinates(e,i){const{x:o,y:a,z:r}=this.tileID.canonical,{x:s,y:n,z:l}=i.canonical,c=St/Math.pow(2,l-r),h=(n*t.a6+e.anchorY)*c,u=a*t.a6*St;return {x:Math.floor((s*t.a6+e.anchorX)*c-o*t.a6*St),y:Math.floor(h-u)}}findMatches(e,t,i){const o=this.tileID.canonical.ze))}}class Rt{constructor(){this.maxCrossTileID=0;}generate(){return ++this.maxCrossTileID}}class Dt{constructor(){this.indexes={},this.usedCrossTileIDs={},this.lng=0;}handleWrapJump(e){const t=Math.round((e-this.lng)/360);if(0!==t)for(const e in this.indexes){const i=this.indexes[e],o={};for(const e in i){const a=i[e];a.tileID=a.tileID.unwrapTo(a.tileID.wrap+t),o[a.tileID.key]=a;}this.indexes[e]=o;}this.lng=e;}addBucket(e,t,i){var o,a,r;if(null===(o=this.indexes[e.overscaledZ])||void 0===o?void 0:o[e.key]){if(this.indexes[e.overscaledZ][e.key].bucketInstanceId===t.bucketInstanceId)return !1;this.removeBucketCrossTileIDs(e.overscaledZ,this.indexes[e.overscaledZ][e.key]);}for(let e=0;ee.overscaledZ)for(const i in o){const a=o[i];a.tileID.isChildOf(e)&&a.findMatches(t.symbolInstances,e,s);}else {const a=o[e.scaledTo(Number(i)).key];a&&a.findMatches(t.symbolInstances,e,s);}}for(let e=0;e 0.95) return 1.0;return (d+depthOpacity(frag+vec3(0.0,0.01,0.0)))/2.0;\n#else\nreturn 1.0;\n#endif\n}float ele(vec2 pos) {\n#ifdef TERRAIN3D\nvec4 rgb=(texture(u_terrain,pos)*255.0)*u_terrain_unpack;return rgb.r+rgb.g+rgb.b-u_terrain_unpack.a;\n#else\nreturn 0.0;\n#endif\n}float get_elevation(vec2 pos) {\n#ifdef TERRAIN3D\n#ifdef GLOBE\nif ((pos.y <-32767.5) || (pos.y > 32766.5)) {return 0.0;}\n#endif\nvec2 coord=(u_terrain_matrix*vec4(pos,0.0,1.0)).xy*u_terrain_dim+1.0;vec2 f=fract(coord);vec2 c=(floor(coord)+0.5)/(u_terrain_dim+2.0);float d=1.0/(u_terrain_dim+2.0);float tl=ele(c);float tr=ele(c+vec2(d,0.0));float bl=ele(c+vec2(0.0,d));float br=ele(c+vec2(d,d));float elevation=mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);return elevation*u_terrain_exaggeration;\n#else\nreturn 0.0;\n#endif\n}const float PI=3.141592653589793;uniform mat4 u_projection_matrix;"),projectionMercator:kt("","float projectLineThickness(float tileY) {return 1.0;}float projectCircleRadius(float tileY) {return 1.0;}vec4 projectTile(vec2 p) {vec4 result=u_projection_matrix*vec4(p,0.0,1.0);return result;}vec4 projectTile(vec2 p,vec2 rawPos) {vec4 result=u_projection_matrix*vec4(p,0.0,1.0);if (rawPos.y <-32767.5 || rawPos.y > 32766.5) {result.z=-10000000.0;}return result;}vec4 projectTileWithElevation(vec2 posInTile,float elevation) {return u_projection_matrix*vec4(posInTile,elevation,1.0);}vec4 projectTileFor3D(vec2 posInTile,float elevation) {return projectTileWithElevation(posInTile,elevation);}"),projectionGlobe:kt("","#define GLOBE_RADIUS 6371008.8\nuniform highp vec4 u_projection_tile_mercator_coords;uniform highp vec4 u_projection_clipping_plane;uniform highp float u_projection_transition;uniform mat4 u_projection_fallback_matrix;vec3 globeRotateVector(vec3 vec,vec2 angles) {vec3 axisRight=vec3(vec.z,0.0,-vec.x);vec3 axisUp=cross(axisRight,vec);axisRight=normalize(axisRight);axisUp=normalize(axisUp);vec2 t=tan(angles);return normalize(vec+axisRight*t.x+axisUp*t.y);}mat3 globeGetRotationMatrix(vec3 spherePos) {vec3 axisRight=vec3(spherePos.z,0.0,-spherePos.x);vec3 axisDown=cross(axisRight,spherePos);axisRight=normalize(axisRight);axisDown=normalize(axisDown);return mat3(axisRight,axisDown,spherePos\n);}float circumferenceRatioAtTileY(float tileY) {float mercator_pos_y=u_projection_tile_mercator_coords.y+u_projection_tile_mercator_coords.w*tileY;float spherical_y=2.0*atan(exp(PI-(mercator_pos_y*PI*2.0)))-PI*0.5;return cos(spherical_y);}float projectLineThickness(float tileY) {float thickness=1.0/circumferenceRatioAtTileY(tileY); \nif (u_projection_transition < 0.999) {return mix(1.0,thickness,u_projection_transition);} else {return thickness;}}vec3 projectToSphere(vec2 translatedPos,vec2 rawPos) {vec2 mercator_pos=u_projection_tile_mercator_coords.xy+u_projection_tile_mercator_coords.zw*translatedPos;vec2 spherical;spherical.x=mercator_pos.x*PI*2.0+PI;spherical.y=2.0*atan(exp(PI-(mercator_pos.y*PI*2.0)))-PI*0.5;float len=cos(spherical.y);vec3 pos=vec3(sin(spherical.x)*len,sin(spherical.y),cos(spherical.x)*len\n);if (rawPos.y <-32767.5) {pos=vec3(0.0,1.0,0.0);}if (rawPos.y > 32766.5) {pos=vec3(0.0,-1.0,0.0);}return pos;}vec3 projectToSphere(vec2 posInTile) {return projectToSphere(posInTile,vec2(0.0,0.0));}float globeComputeClippingZ(vec3 spherePos) {return (1.0-(dot(spherePos,u_projection_clipping_plane.xyz)+u_projection_clipping_plane.w));}vec4 interpolateProjection(vec2 posInTile,vec3 spherePos,float elevation) {vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);vec4 globePosition=u_projection_matrix*vec4(elevatedPos,1.0);globePosition.z=globeComputeClippingZ(elevatedPos)*globePosition.w;if (u_projection_transition > 0.999) {return globePosition;}vec4 flatPosition=u_projection_fallback_matrix*vec4(posInTile,elevation,1.0);const float z_globeness_threshold=0.2;vec4 result=globePosition;result.z=mix(0.0,globePosition.z,clamp((u_projection_transition-z_globeness_threshold)/(1.0-z_globeness_threshold),0.0,1.0));result.xyw=mix(flatPosition.xyw,globePosition.xyw,u_projection_transition);if ((posInTile.y <-32767.5) || (posInTile.y > 32766.5)) {result=globePosition;const float poles_hidden_anim_percentage=0.02;result.z=mix(globePosition.z,100.0,pow(max((1.0-u_projection_transition)/poles_hidden_anim_percentage,0.0),8.0));}return result;}vec4 interpolateProjectionFor3D(vec2 posInTile,vec3 spherePos,float elevation) {vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);vec4 globePosition=u_projection_matrix*vec4(elevatedPos,1.0);if (u_projection_transition > 0.999) {return globePosition;}vec4 fallbackPosition=u_projection_fallback_matrix*vec4(posInTile,elevation,1.0);return mix(fallbackPosition,globePosition,u_projection_transition);}vec4 projectTile(vec2 posInTile) {return interpolateProjection(posInTile,projectToSphere(posInTile),0.0);}vec4 projectTile(vec2 posInTile,vec2 rawPos) {return interpolateProjection(posInTile,projectToSphere(posInTile,rawPos),0.0);}vec4 projectTileWithElevation(vec2 posInTile,float elevation) {return interpolateProjection(posInTile,projectToSphere(posInTile),elevation);}vec4 projectTileFor3D(vec2 posInTile,float elevation) {vec3 spherePos=projectToSphere(posInTile,posInTile);return interpolateProjectionFor3D(posInTile,spherePos,elevation);}"),background:kt("uniform vec4 u_color;uniform float u_opacity;void main() {fragColor=u_color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}"),backgroundPattern:kt("uniform vec2 u_pattern_tl_a;uniform vec2 u_pattern_br_a;uniform vec2 u_pattern_tl_b;uniform vec2 u_pattern_br_b;uniform vec2 u_texsize;uniform float u_mix;uniform float u_opacity;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;void main() {vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(u_pattern_tl_a/u_texsize,u_pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(u_pattern_tl_b/u_texsize,u_pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);fragColor=mix(color1,color2,u_mix)*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_pattern_size_a;uniform vec2 u_pattern_size_b;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_scale_a;uniform float u_scale_b;uniform float u_tile_units_to_pixels;in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;void main() {gl_Position=projectTile(a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_a*u_pattern_size_a,u_tile_units_to_pixels,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_b*u_pattern_size_b,u_tile_units_to_pixels,a_pos);}"),circle:kt("in vec3 v_data;in float v_visibility;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define mediump float radius\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define highp vec4 stroke_color\n#pragma mapbox: define mediump float stroke_width\n#pragma mapbox: define lowp float stroke_opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize mediump float radius\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize highp vec4 stroke_color\n#pragma mapbox: initialize mediump float stroke_width\n#pragma mapbox: initialize lowp float stroke_opacity\nvec2 extrude=v_data.xy;float extrude_length=length(extrude);float antialiased_blur=v_data.z;float opacity_t=smoothstep(0.0,antialiased_blur,extrude_length-1.0);float color_t=stroke_width < 0.01 ? 0.0 : smoothstep(antialiased_blur,0.0,extrude_length-radius/(radius+stroke_width));fragColor=v_visibility*opacity_t*mix(color*opacity,stroke_color*stroke_opacity,color_t);const float epsilon=0.5/255.0;if (fragColor.r < epsilon && fragColor.g < epsilon && fragColor.b < epsilon && fragColor.a < epsilon) {discard;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform bool u_scale_with_map;uniform bool u_pitch_with_map;uniform vec2 u_extrude_scale;uniform highp float u_globe_extrude_scale;uniform lowp float u_device_pixel_ratio;uniform highp float u_camera_to_center_distance;uniform vec2 u_translate;in vec2 a_pos;out vec3 v_data;out float v_visibility;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define mediump float radius\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define highp vec4 stroke_color\n#pragma mapbox: define mediump float stroke_width\n#pragma mapbox: define lowp float stroke_opacity\nvoid main(void) {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize mediump float radius\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize highp vec4 stroke_color\n#pragma mapbox: initialize mediump float stroke_width\n#pragma mapbox: initialize lowp float stroke_opacity\nvec2 pos_raw=a_pos+32768.0;vec2 extrude=vec2(mod(pos_raw,8.0)/7.0*2.0-1.0);vec2 circle_center=floor(pos_raw/8.0)+u_translate;float ele=get_elevation(circle_center);v_visibility=calculate_visibility(projectTileWithElevation(circle_center,ele));if (u_pitch_with_map) {\n#ifdef GLOBE\nvec3 center_vector=projectToSphere(circle_center);\n#endif\nfloat angle_scale=u_globe_extrude_scale;vec2 corner_position=circle_center;if (u_scale_with_map) {angle_scale*=(radius+stroke_width);corner_position+=extrude*u_extrude_scale*(radius+stroke_width);} else {\n#ifdef GLOBE\nvec4 projected_center=interpolateProjection(circle_center,center_vector,ele);\n#else\nvec4 projected_center=projectTileWithElevation(circle_center,ele);\n#endif\ncorner_position+=extrude*u_extrude_scale*(radius+stroke_width)*(projected_center.w/u_camera_to_center_distance);angle_scale*=(radius+stroke_width)*(projected_center.w/u_camera_to_center_distance);}\n#ifdef GLOBE\nvec2 angles=extrude*angle_scale;vec3 corner_vector=globeRotateVector(center_vector,angles);gl_Position=interpolateProjection(corner_position,corner_vector,ele);\n#else\ngl_Position=projectTileWithElevation(corner_position,ele);\n#endif\n} else {gl_Position=projectTileWithElevation(circle_center,ele);if (gl_Position.z/gl_Position.w > 1.0) {gl_Position.xy=vec2(10000.0);}if (u_scale_with_map) {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*u_camera_to_center_distance;} else {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*gl_Position.w;}}float antialiasblur=-max(1.0/u_device_pixel_ratio/(radius+stroke_width),blur);v_data=vec3(extrude.x,extrude.y,antialiasblur);}"),clippingMask:kt(Lt,"in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}"),heatmap:kt("uniform highp float u_intensity;in vec2 v_extrude;\n#pragma mapbox: define highp float weight\n#define GAUSS_COEF 0.3989422804014327\nvoid main() {\n#pragma mapbox: initialize highp float weight\nfloat d=-0.5*3.0*3.0*dot(v_extrude,v_extrude);float val=weight*u_intensity*GAUSS_COEF*exp(d);fragColor=vec4(val,1.0,1.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform float u_extrude_scale;uniform float u_opacity;uniform float u_intensity;uniform highp float u_globe_extrude_scale;in vec2 a_pos;out vec2 v_extrude;\n#pragma mapbox: define highp float weight\n#pragma mapbox: define mediump float radius\nconst highp float ZERO=1.0/255.0/16.0;\n#define GAUSS_COEF 0.3989422804014327\nvoid main(void) {\n#pragma mapbox: initialize highp float weight\n#pragma mapbox: initialize mediump float radius\nvec2 pos_raw=a_pos+32768.0;vec2 unscaled_extrude=vec2(mod(pos_raw,8.0)/7.0*2.0-1.0);float S=sqrt(-2.0*log(ZERO/weight/u_intensity/GAUSS_COEF))/3.0;v_extrude=S*unscaled_extrude;vec2 extrude=v_extrude*radius*u_extrude_scale;vec2 circle_center=floor(pos_raw/8.0);\n#ifdef GLOBE\nvec2 angles=v_extrude*radius*u_globe_extrude_scale;vec3 center_vector=projectToSphere(circle_center);vec3 corner_vector=globeRotateVector(center_vector,angles);gl_Position=interpolateProjection(circle_center+extrude,corner_vector,0.0);\n#else\ngl_Position=projectTileFor3D(circle_center+extrude,get_elevation(circle_center));\n#endif\n}"),heatmapTexture:kt("uniform sampler2D u_image;uniform sampler2D u_color_ramp;uniform float u_opacity;in vec2 v_pos;void main() {float t=texture(u_image,v_pos).r;vec4 color=texture(u_color_ramp,vec2(t,0.5));fragColor=color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(0.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_world;in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos*u_world,0,1);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}"),collisionBox:kt("in float v_placed;in float v_notUsed;void main() {float alpha=0.5;fragColor=vec4(1.0,0.0,0.0,1.0)*alpha;if (v_placed > 0.5) {fragColor=vec4(0.0,0.0,1.0,0.5)*alpha;}if (v_notUsed > 0.5) {fragColor*=.1;}}","in vec2 a_anchor_pos;in vec2 a_placed;in vec2 a_box_real;uniform vec2 u_pixel_extrude_scale;out float v_placed;out float v_notUsed;void main() {gl_Position=projectTileWithElevation(a_anchor_pos,get_elevation(a_anchor_pos));gl_Position.xy=((a_box_real+0.5)*u_pixel_extrude_scale*2.0-1.0)*vec2(1.0,-1.0)*gl_Position.w;if (gl_Position.z/gl_Position.w < 1.1) {gl_Position.z=0.5;}v_placed=a_placed.x;v_notUsed=a_placed.y;}"),collisionCircle:kt("in float v_radius;in vec2 v_extrude;in float v_collision;void main() {float alpha=0.5;float stroke_radius=0.9;float distance_to_center=length(v_extrude);float distance_to_edge=abs(distance_to_center-v_radius);float opacity_t=smoothstep(-stroke_radius,0.0,-distance_to_edge);vec4 color=mix(vec4(0.0,0.0,1.0,0.5),vec4(1.0,0.0,0.0,1.0),v_collision);fragColor=color*alpha*opacity_t;}","in vec2 a_pos;in float a_radius;in vec2 a_flags;uniform vec2 u_viewport_size;out float v_radius;out vec2 v_extrude;out float v_collision;void main() {float radius=a_radius;float collision=a_flags.x;float vertexIdx=a_flags.y;vec2 quadVertexOffset=vec2(mix(-1.0,1.0,float(vertexIdx >=2.0)),mix(-1.0,1.0,float(vertexIdx >=1.0 && vertexIdx <=2.0)));vec2 quadVertexExtent=quadVertexOffset*radius;float padding_factor=1.2;v_radius=radius;v_extrude=quadVertexExtent*padding_factor;v_collision=collision;gl_Position=vec4((a_pos/u_viewport_size*2.0-1.0)*vec2(1.0,-1.0),0.0,1.0)+vec4(quadVertexExtent*padding_factor/u_viewport_size*2.0,0.0,0.0);}"),colorRelief:kt("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;uniform vec4 u_unpack;uniform sampler2D u_elevation_stops;uniform sampler2D u_color_stops;uniform int u_color_ramp_size;uniform float u_opacity;in vec2 v_pos;float getElevation(vec2 coord) {vec4 data=texture(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack);}float getElevationStop(int stop) {float x=(float(stop)+0.5)/float(u_color_ramp_size);vec4 data=texture(u_elevation_stops,vec2(x,0))*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {float el=getElevation(v_pos);int r=(u_color_ramp_size-1);int l=0;float el_l=getElevationStop(l);float el_r=getElevationStop(r);while(r-l > 1){int m=(r+l)/2;float el_m=getElevationStop(m);if(el < el_m){r=m;el_r=el_m;}else\n{l=m;el_l=el_m;}}float x=(float(l)+(el-el_l)/(el_r-el_l)+0.5)/float(u_color_ramp_size);fragColor=u_opacity*texture(u_color_stops,vec2(x,0));\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_dimension;in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_pos/8192.0)*scale+epsilon;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}"),debug:kt("uniform highp vec4 u_color;uniform sampler2D u_overlay;in vec2 v_uv;void main() {vec4 overlay_color=texture(u_overlay,v_uv);fragColor=mix(u_color,overlay_color,overlay_color.a);}","in vec2 a_pos;out vec2 v_uv;uniform float u_overlay_scale;void main() {v_uv=a_pos/8192.0;gl_Position=projectTileWithElevation(a_pos*u_overlay_scale,get_elevation(a_pos));}"),depth:kt(Lt,"in vec2 a_pos;void main() {\n#ifdef GLOBE\ngl_Position=projectTileFor3D(a_pos,0.0);\n#else\ngl_Position=u_projection_matrix*vec4(a_pos,0.0,1.0);\n#endif\n}"),fill:kt("#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float opacity\nfragColor=color*opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_fill_translate;in vec2 a_pos;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float opacity\ngl_Position=projectTile(a_pos+u_fill_translate,a_pos);}"),fillOutline:kt("in vec2 v_pos;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 outline_color\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);fragColor=outline_color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_world;uniform vec2 u_fill_translate;in vec2 a_pos;out vec2 v_pos;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 outline_color\n#pragma mapbox: initialize lowp float opacity\ngl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n}"),fillOutlinePattern:kt("uniform vec2 u_texsize;uniform sampler2D u_image;uniform float u_fade;in vec2 v_pos_a;in vec2 v_pos_b;in vec2 v_pos;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);float dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);fragColor=mix(color1,color2,u_fade)*alpha*opacity;\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_world;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;out vec2 v_pos;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n}"),fillPattern:kt("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);fragColor=mix(color1,color2,u_fade)*opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileZoomRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileZoomRatio,a_pos);}"),fillExtrusion:kt("in vec4 v_color;void main() {fragColor=v_color;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;in vec2 a_pos;in vec4 a_normal_ed;\n#ifdef TERRAIN3D\nin vec2 a_centroid;\n#endif\nout vec4 v_color;\n#pragma mapbox: define highp float base\n#pragma mapbox: define highp float height\n#pragma mapbox: define highp vec4 color\nvoid main() {\n#pragma mapbox: initialize highp float base\n#pragma mapbox: initialize highp float height\n#pragma mapbox: initialize highp vec4 color\nvec3 normal=a_normal_ed.xyz;\n#ifdef TERRAIN3D\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\n#else\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\n#endif\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=mod(normal.x,2.0);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\n#ifdef GLOBE\nvec3 spherePos=projectToSphere(posInTile,a_pos);gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\n#else\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\n#endif\nfloat colorvalue=color.r*0.2126+color.g*0.7152+color.b*0.0722;v_color=vec4(0.0,0.0,0.0,1.0);vec4 ambientlight=vec4(0.03,0.03,0.03,1.0);color+=ambientlight;vec3 normalForLighting=normal/16384.0;float directional=clamp(dot(normalForLighting,u_lightpos),0.0,1.0);\n#ifdef GLOBE\nmat3 rotMatrix=globeGetRotationMatrix(spherePos);normalForLighting=rotMatrix*normalForLighting;directional=mix(directional,clamp(dot(normalForLighting,u_lightpos_globe),0.0,1.0),u_projection_transition);\n#endif\ndirectional=mix((1.0-u_lightintensity),max((1.0-colorvalue+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_color.r+=clamp(color.r*directional*u_lightcolor.r,mix(0.0,0.3,1.0-u_lightcolor.r),1.0);v_color.g+=clamp(color.g*directional*u_lightcolor.g,mix(0.0,0.3,1.0-u_lightcolor.g),1.0);v_color.b+=clamp(color.b*directional*u_lightcolor.b,mix(0.0,0.3,1.0-u_lightcolor.b),1.0);v_color*=u_opacity;}"),fillExtrusionPattern:kt("uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;in vec4 v_lighting;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float base\n#pragma mapbox: initialize lowp float height\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);vec4 mixedColor=mix(color1,color2,u_fade);fragColor=mixedColor*v_lighting;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_height_factor;uniform vec3 u_scale;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;in vec2 a_pos;in vec4 a_normal_ed;\n#ifdef TERRAIN3D\nin vec2 a_centroid;\n#endif\n#ifdef GLOBE\nout vec3 v_sphere_pos;\n#endif\nout vec2 v_pos_a;out vec2 v_pos_b;out vec4 v_lighting;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float base\n#pragma mapbox: initialize lowp float height\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec3 normal=a_normal_ed.xyz;float edgedistance=a_normal_ed.w;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;\n#ifdef TERRAIN3D\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\n#else\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\n#endif\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=mod(normal.x,2.0);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\n#ifdef GLOBE\nvec3 spherePos=projectToSphere(posInTile,a_pos);vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);v_sphere_pos=elevatedPos;gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\n#else\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\n#endif\nvec2 pos=normal.x==1.0 && normal.y==0.0 && normal.z==16384.0\n? a_pos\n: vec2(edgedistance,elevation*u_height_factor);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,pos);v_lighting=vec4(0.0,0.0,0.0,1.0);float directional=clamp(dot(normal/16383.0,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((0.5+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_lighting.rgb+=clamp(directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_lighting*=u_opacity;}"),hillshadePrepare:kt("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(vec2 coord,float bias) {vec4 data=texture(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {vec2 epsilon=1.0/u_dimension;float tileSize=u_dimension.x-2.0;float a=getElevation(v_pos+vec2(-epsilon.x,-epsilon.y),0.0);float b=getElevation(v_pos+vec2(0,-epsilon.y),0.0);float c=getElevation(v_pos+vec2(epsilon.x,-epsilon.y),0.0);float d=getElevation(v_pos+vec2(-epsilon.x,0),0.0);float e=getElevation(v_pos,0.0);float f=getElevation(v_pos+vec2(epsilon.x,0),0.0);float g=getElevation(v_pos+vec2(-epsilon.x,epsilon.y),0.0);float h=getElevation(v_pos+vec2(0,epsilon.y),0.0);float i=getElevation(v_pos+vec2(epsilon.x,epsilon.y),0.0);float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2((c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c))*tileSize/pow(2.0,exaggeration+(28.2562-u_zoom));fragColor=clamp(vec4(deriv.x/8.0+0.5,deriv.y/8.0+0.5,1.0,1.0),0.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_dimension;in vec2 a_pos;in vec2 a_texture_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}"),hillshade:kt("uniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_latrange;uniform float u_exaggeration;uniform vec4 u_accent;uniform int u_method;uniform float u_altitudes[NUM_ILLUMINATION_SOURCES];uniform float u_azimuths[NUM_ILLUMINATION_SOURCES];uniform vec4 u_shadows[NUM_ILLUMINATION_SOURCES];uniform vec4 u_highlights[NUM_ILLUMINATION_SOURCES];\n#define PI 3.141592653589793\n#define STANDARD 0\n#define COMBINED 1\n#define IGOR 2\n#define MULTIDIRECTIONAL 3\n#define BASIC 4\nfloat get_aspect(vec2 deriv){return deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);}void igor_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float aspect=get_aspect(deriv);float azimuth=u_azimuths[0]+PI;float slope_stength=atan(length(deriv))*2.0/PI;float aspect_strength=1.0-abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);float shadow_strength=slope_stength*aspect_strength;float highlight_strength=slope_stength*(1.0-aspect_strength);fragColor=u_shadows[0]*shadow_strength+u_highlights[0]*highlight_strength;}void standard_hillshade(vec2 deriv){float azimuth=u_azimuths[0]+PI;float slope=atan(0.625*length(deriv));float aspect=get_aspect(deriv);float intensity=u_exaggeration;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadows[0],u_highlights[0],shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);fragColor=accent_color*(1.0-shade_color.a)+shade_color;}void basic_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor=u_highlights[0]*(2.0*shade-1.0);}else\n{fragColor=u_shadows[0]*(1.0-2.0*shade);}}void multidirectional_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;fragColor=vec4(0,0,0,0);for(int i=0; i < NUM_ILLUMINATION_SOURCES; i++){float cos_alt=cos(u_altitudes[i]);float sin_alt=sin(u_altitudes[i]);float cos_az=-cos(u_azimuths[i]);float sin_az=-sin(u_azimuths[i]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor+=u_highlights[i]*(2.0*shade-1.0)/float(NUM_ILLUMINATION_SOURCES);}else\n{fragColor+=u_shadows[i]*(1.0-2.0*shade)/float(NUM_ILLUMINATION_SOURCES);}}}void combined_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=acos((sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv)));cang=clamp(cang,0.0,PI/2.0);float shade=cang*atan(length(deriv))*4.0/PI/PI;float highlight=(PI/2.0-cang)*atan(length(deriv))*4.0/PI/PI;fragColor=u_shadows[0]*shade+u_highlights[0]*highlight;}void main() {vec4 pixel=texture(u_image,v_pos);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));vec2 deriv=((pixel.rg*8.0)-4.0)/scaleFactor;if (u_method==BASIC) {basic_hillshade(deriv);} else if (u_method==COMBINED) {combined_hillshade(deriv);} else if (u_method==IGOR) {igor_hillshade(deriv);} else if (u_method==MULTIDIRECTIONAL) {multidirectional_hillshade(deriv);} else if (u_method==STANDARD) {standard_hillshade(deriv);} else {standard_hillshade(deriv);}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);v_pos=a_pos/8192.0;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}"),line:kt("uniform lowp float u_device_pixel_ratio;in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform vec2 u_units_to_pixels;uniform lowp float u_device_pixel_ratio;out vec2 v_normal;out vec2 v_width2;out float v_gamma_scale;out highp float v_linesofar;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;v_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*2.0;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_width2=vec2(outset,inset);}"),lineGradient:kt("uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;in highp vec2 v_uv;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\nin vec2 a_pos_normal;in vec4 a_data;in float a_uv_x;in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;out vec2 v_normal;out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;highp float texel_height=1.0/u_image_height;highp float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_width2=vec2(outset,inset);}"),linePattern:kt("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform lowp float u_device_pixel_ratio;uniform vec2 u_texsize;uniform float u_fade;uniform mediump vec3 u_scale;uniform sampler2D u_image;in vec2 v_normal;in vec2 v_width2;in float v_linesofar;in float v_gamma_scale;in float v_width;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;vec2 pattern_size_a=vec2(display_size_a.x*fromScale/tileZoomRatio,display_size_a.y);vec2 pattern_size_b=vec2(display_size_b.x*toScale/tileZoomRatio,display_size_b.y);float aspect_a=display_size_a.y/v_width;float aspect_b=display_size_b.y/v_width;float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float x_a=mod(v_linesofar/pattern_size_a.x*aspect_a,1.0);float x_b=mod(v_linesofar/pattern_size_b.x*aspect_b,1.0);float y=0.5*v_normal.y+0.5;vec2 texel_size=1.0/u_texsize;vec2 pos_a=mix(pattern_tl_a*texel_size-texel_size,pattern_br_a*texel_size+texel_size,vec2(x_a,y));vec2 pos_b=mix(pattern_tl_b*texel_size-texel_size,pattern_br_b*texel_size+texel_size,vec2(x_b,y));vec4 color=mix(texture(u_image,pos_a),texture(u_image,pos_b),u_fade);fragColor=color*alpha*opacity;\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform vec2 u_units_to_pixels;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;out vec2 v_normal;out vec2 v_width2;out float v_linesofar;out float v_gamma_scale;out float v_width;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_linesofar=a_linesofar;v_width2=vec2(outset,inset);v_width=floorwidth;}"),lineSDF:kt("uniform lowp float u_device_pixel_ratio;uniform lowp float u_lineatlas_width;uniform sampler2D u_image;uniform float u_mix;in vec2 v_normal;in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define mediump vec4 dasharray_from\n#pragma mapbox: define mediump vec4 dasharray_to\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dasharray_from\n#pragma mapbox: initialize mediump vec4 dasharray_to\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture(u_image,v_tex_a).a;float sdfdist_b=texture(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0/u_device_pixel_ratio)/min(dasharray_from.w,dasharray_to.w);alpha*=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;out vec2 v_width2;out vec2 v_tex_a;out vec2 v_tex_b;out float v_gamma_scale;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define mediump vec4 dasharray_from\n#pragma mapbox: define mediump vec4 dasharray_to\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dasharray_from\n#pragma mapbox: initialize mediump vec4 dasharray_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}"),lineGradientSDF:kt("uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform sampler2D u_image_dash;uniform float u_mix;uniform lowp float u_lineatlas_width;in vec2 v_normal;in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;in highp vec2 v_uv;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define mediump vec4 dasharray_from\n#pragma mapbox: define mediump vec4 dasharray_to\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dasharray_from\n#pragma mapbox: initialize mediump vec4 dasharray_to\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);float sdfdist_a=texture(u_image_dash,v_tex_a).a;float sdfdist_b=texture(u_image_dash,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0)/min(dasharray_from.w,dasharray_to.w);float dash_alpha=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*dash_alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nin vec2 a_pos_normal;in vec4 a_data;in float a_uv_x;in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;out vec2 v_tex_a;out vec2 v_tex_b;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define mediump vec4 dasharray_from\n#pragma mapbox: define mediump vec4 dasharray_to\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dasharray_from\n#pragma mapbox: initialize mediump vec4 dasharray_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;float texel_height=1.0/u_image_height;float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}"),raster:kt("uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;in vec2 v_pos0;in vec2 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture(u_image0,v_pos0);vec4 color1=texture(u_image1,v_pos1);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(dot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);fragColor=vec4(mix(u_high_vec,u_low_vec,rgb)*color.a,color.a);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;uniform vec4 u_coords_top;uniform vec4 u_coords_bottom;in vec2 a_pos;out vec2 v_pos0;out vec2 v_pos1;void main() {vec2 fractionalPos=a_pos/8192.0;vec2 position=mix(mix(u_coords_top.xy,u_coords_top.zw,fractionalPos.x),mix(u_coords_bottom.xy,u_coords_bottom.zw,fractionalPos.x),fractionalPos.y);gl_Position=projectTile(position,position);v_pos0=((fractionalPos-0.5)/u_buffer_scale)+0.5;\n#ifdef GLOBE\nif (a_pos.y <-32767.5) {v_pos0.y=0.0;}if (a_pos.y > 32766.5) {v_pos0.y=1.0;}\n#endif\nv_pos1=(v_pos0*u_scale_parent)+u_tl_parent;}"),symbolIcon:kt("uniform sampler2D u_texture;in vec2 v_tex;in float v_total_opacity;void main() {fragColor=texture(u_texture,v_tex)*v_total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","in vec4 a_pos_offset;in vec4 a_data;in vec4 a_pixeloffset;in vec3 a_projected_pos;in float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;out vec2 v_tex;out float v_total_opacity;\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_total_opacity=opacity*max(0.0,min(visibility,fade_opacity[0]+fade_change));if (v_total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;}"),symbolSDF:kt("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform bool u_is_plain;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float total_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out_text,color_alpha_out_halo;if (u_is_plain){highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);color_alpha_out_text=total_opacity*alpha*fill_color;}if (u_is_halo) {float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);float inner_edge_halo=inner_edge+gamma_halo*gamma_scale;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(inner_edge_halo-gamma_scaled_halo,inner_edge_halo+gamma_scaled_halo,dist);highp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha_halo= min(smoothstep(halo_edge-gamma_scaled_halo,halo_edge+gamma_scaled_halo,dist),1.0-alpha_halo);color_alpha_out_halo=total_opacity*alpha_halo*halo_color;}if (u_is_plain && u_is_halo) {fragColor=color_alpha_out_text+(1.-color_alpha_out_text.a)*color_alpha_out_halo;} else if (u_is_halo){fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out_text;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","in vec4 a_pos_offset;in vec4 a_data;in vec4 a_pixeloffset;in vec3 a_projected_pos;in float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;out vec2 v_data0;out vec3 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy/16.0;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,total_opacity);}"),symbolTextAndIcon:kt("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform bool u_is_text;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;in vec4 v_data0;in vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nfloat total_opacity=v_data1[2];if (v_data1.w==ICON) {vec2 tex_icon=v_data0.zw;fragColor=texture(u_texture_icon,tex_icon)*total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out,color_alpha_out_halo;if (u_is_text) {highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);color_alpha_out=fill_color*(alpha*total_opacity);}if (u_is_halo) {highp float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);lowp float buff_halo=(6.0-halo_width/fontScale)/SDF_PX;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(buff_halo-gamma_scaled_halo,buff_halo+gamma_scaled_halo,dist);color_alpha_out_halo=halo_color*(alpha_halo*total_opacity);}if (u_is_text && u_is_halo) {fragColor=color_alpha_out+(1.-color_alpha_out.a)*color_alpha_out_halo;} else if (u_is_halo) {fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}","in vec4 a_pos_offset;in vec4 a_data;in vec3 a_projected_pos;in float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;out vec4 v_data0;out vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);float is_sdf=a_size[0]-2.0*a_size_min;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec4(gamma_scale,size,total_opacity,is_sdf);}"),terrain:kt("uniform sampler2D u_texture;uniform vec4 u_fog_color;uniform vec4 u_horizon_color;uniform float u_fog_ground_blend;uniform float u_fog_ground_blend_opacity;uniform float u_horizon_fog_blend;uniform bool u_is_globe_mode;in vec2 v_texture_pos;in float v_fog_depth;const float gamma=2.2;vec4 gammaToLinear(vec4 color) {return pow(color,vec4(gamma));}vec4 linearToGamma(vec4 color) {return pow(color,vec4(1.0/gamma));}void main() {vec4 surface_color=texture(u_texture,vec2(v_texture_pos.x,1.0-v_texture_pos.y));if (!u_is_globe_mode && u_fog_ground_blend_opacity > 0.0 && v_fog_depth > u_fog_ground_blend) {vec4 surface_color_linear=gammaToLinear(surface_color);float blend_color=smoothstep(0.0,1.0,max((v_fog_depth-u_horizon_fog_blend)/(1.0-u_horizon_fog_blend),0.0));vec4 fog_horizon_color_linear=mix(gammaToLinear(u_fog_color),gammaToLinear(u_horizon_color),blend_color);float factor_fog=max(v_fog_depth-u_fog_ground_blend,0.0)/(1.0-u_fog_ground_blend);fragColor=linearToGamma(mix(surface_color_linear,fog_horizon_color_linear,pow(factor_fog,2.0)*u_fog_ground_blend_opacity));} else {fragColor=surface_color;}}","in vec3 a_pos3d;uniform mat4 u_fog_matrix;uniform float u_ele_delta;out vec2 v_texture_pos;out float v_fog_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);vec4 pos=u_fog_matrix*vec4(a_pos3d.xy,ele,1.0);v_fog_depth=pos.z/pos.w*0.5+0.5;}"),terrainDepth:kt("in float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {fragColor=pack(v_depth);}","in vec3 a_pos3d;uniform float u_ele_delta;out float v_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);v_depth=gl_Position.z/gl_Position.w;}"),terrainCoords:kt("precision mediump float;uniform sampler2D u_texture;uniform float u_terrain_coords_id;in vec2 v_texture_pos;void main() {vec4 rgba=texture(u_texture,v_texture_pos);fragColor=vec4(rgba.r,rgba.g,rgba.b,u_terrain_coords_id);}","in vec3 a_pos3d;uniform float u_ele_delta;out vec2 v_texture_pos;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);}"),projectionErrorMeasurement:kt("in vec4 v_output_error_encoded;void main() {fragColor=v_output_error_encoded;}","in vec2 a_pos;uniform highp float u_input;uniform highp float u_output_expected;out vec4 v_output_error_encoded;void main() {float real_output=2.0*atan(exp(PI-(u_input*PI*2.0)))-PI*0.5;float error=real_output-u_output_expected;float abs_error=abs(error)*128.0;v_output_error_encoded.x=min(floor(abs_error*256.0),255.0)/255.0;abs_error-=v_output_error_encoded.x;v_output_error_encoded.y=min(floor(abs_error*65536.0),255.0)/255.0;abs_error-=v_output_error_encoded.x/255.0;v_output_error_encoded.z=min(floor(abs_error*16777216.0),255.0)/255.0;v_output_error_encoded.w=error >=0.0 ? 1.0 : 0.0;gl_Position=vec4(a_pos,0.0,1.0);}"),atmosphere:kt("#ifdef GL_ES\nprecision highp float;\n#endif\nin vec3 view_direction;uniform vec3 u_sun_pos;uniform vec3 u_globe_position;uniform float u_globe_radius;uniform float u_atmosphere_blend;/**Shader use from https:*Made some change to adapt to MapLibre Globe geometry*/const float PI=3.141592653589793;const int iSteps=5;const int jSteps=3;/*radius of the planet*/const float EARTH_RADIUS=6371e3;/*radius of the atmosphere*/const float ATMOS_RADIUS=6471e3;vec2 rsi(vec3 r0,vec3 rd,float sr) {float a=dot(rd,rd);float b=2.0*dot(rd,r0);float c=dot(r0,r0)-(sr*sr);float d=(b*b)-4.0*a*c;if (d < 0.0) return vec2(1e5,-1e5);return vec2((-b-sqrt(d))/(2.0*a),(-b+sqrt(d))/(2.0*a));}vec4 atmosphere(vec3 r,vec3 r0,vec3 pSun,float iSun,float rPlanet,float rAtmos,vec3 kRlh,float kMie,float shRlh,float shMie,float g) {pSun=normalize(pSun);r=normalize(r);vec2 p=rsi(r0,r,rAtmos);if (p.x > p.y) {return vec4(0.0,0.0,0.0,1.0);}if (p.x < 0.0) {p.x=0.0;}vec3 pos=r0+r*p.x;vec2 p2=rsi(r0,r,rPlanet);if (p2.x <=p2.y && p2.x > 0.0) {p.y=min(p.y,p2.x);}float iStepSize=(p.y-p.x)/float(iSteps);float iTime=p.x+iStepSize*0.5;vec3 totalRlh=vec3(0,0,0);vec3 totalMie=vec3(0,0,0);float iOdRlh=0.0;float iOdMie=0.0;float mu=dot(r,pSun);float mumu=mu*mu;float gg=g*g;float pRlh=3.0/(16.0*PI)*(1.0+mumu);float pMie=3.0/(8.0*PI)*((1.0-gg)*(mumu+1.0))/(pow(1.0+gg-2.0*mu*g,1.5)*(2.0+gg));for (int i=0; i < iSteps; i++) {vec3 iPos=r0+r*iTime;float iHeight=length(iPos)-rPlanet;float odStepRlh=exp(-iHeight/shRlh)*iStepSize;float odStepMie=exp(-iHeight/shMie)*iStepSize;iOdRlh+=odStepRlh;iOdMie+=odStepMie;float jStepSize=rsi(iPos,pSun,rAtmos).y/float(jSteps);float jTime=jStepSize*0.5;float jOdRlh=0.0;float jOdMie=0.0;for (int j=0; j < jSteps; j++) {vec3 jPos=iPos+pSun*jTime;float jHeight=length(jPos)-rPlanet;jOdRlh+=exp(-jHeight/shRlh)*jStepSize;jOdMie+=exp(-jHeight/shMie)*jStepSize;jTime+=jStepSize;}vec3 attn=exp(-(kMie*(iOdMie+jOdMie)+kRlh*(iOdRlh+jOdRlh)));totalRlh+=odStepRlh*attn;totalMie+=odStepMie*attn;iTime+=iStepSize;}float opacity=exp(-(length(kRlh)*length(totalRlh)+kMie*length(totalMie)));vec3 color=iSun*(pRlh*kRlh*totalRlh+pMie*kMie*totalMie);return vec4(color,opacity);}void main() {vec3 scale_camera_pos=-u_globe_position*EARTH_RADIUS/u_globe_radius;vec4 color=atmosphere(normalize(view_direction),scale_camera_pos,u_sun_pos,22.0,EARTH_RADIUS,ATMOS_RADIUS,vec3(5.5e-6,13.0e-6,22.4e-6),21e-6,8e3,1.2e3,0.758\n);color.rgb=1.0-exp(-1.0*color.rgb);color=pow(color,vec4(1.0/2.2));fragColor=vec4(color.rgb,1.0-color.a)*u_atmosphere_blend;}","in vec2 a_pos;uniform mat4 u_inv_proj_matrix;out vec3 view_direction;void main() {view_direction=(u_inv_proj_matrix*vec4(a_pos,0.0,1.0)).xyz;gl_Position=vec4(a_pos,0.0,1.0);}"),sky:kt("uniform vec4 u_sky_color;uniform vec4 u_horizon_color;uniform vec2 u_horizon;uniform vec2 u_horizon_normal;uniform float u_sky_horizon_blend;uniform float u_sky_blend;void main() {float x=gl_FragCoord.x;float y=gl_FragCoord.y;float blend=(y-u_horizon.y)*u_horizon_normal.y+(x-u_horizon.x)*u_horizon_normal.x;if (blend > 0.0) {if (blend < u_sky_horizon_blend) {fragColor=mix(u_sky_color,u_horizon_color,pow(1.0-blend/u_sky_horizon_blend,2.0));} else {fragColor=u_sky_color;}}fragColor=mix(fragColor,vec4(vec3(0.0),0.0),u_sky_blend);}","in vec2 a_pos;void main() {gl_Position=vec4(a_pos,1.0,1.0);}")};function kt(e,t){const i=/#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g,o=t.match(/in ([\w]+) ([\w]+)/g),a=e.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g),r=t.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g),s=r?r.concat(a):a,n={};return {fragmentSource:e=e.replace(i,((e,t,i,o,a)=>(n[a]=!0,"define"===t?`\n#ifndef HAS_UNIFORM_u_${a}\nin ${i} ${o} ${a};\n#else\nuniform ${i} ${o} u_${a};\n#endif\n`:`\n#ifdef HAS_UNIFORM_u_${a}\n ${i} ${o} ${a} = u_${a};\n#endif\n`))),vertexSource:t=t.replace(i,((e,t,i,o,a)=>{const r="float"===o?"vec2":"vec4",s=a.match(/color/)?"color":r;return n[a]?"define"===t?`\n#ifndef HAS_UNIFORM_u_${a}\nuniform lowp float u_${a}_t;\nin ${i} ${r} a_${a};\nout ${i} ${o} ${a};\n#else\nuniform ${i} ${o} u_${a};\n#endif\n`:"vec4"===s?`\n#ifndef HAS_UNIFORM_u_${a}\n ${a} = a_${a};\n#else\n ${i} ${o} ${a} = u_${a};\n#endif\n`:`\n#ifndef HAS_UNIFORM_u_${a}\n ${a} = unpack_mix_${s}(a_${a}, u_${a}_t);\n#else\n ${i} ${o} ${a} = u_${a};\n#endif\n`:"define"===t?`\n#ifndef HAS_UNIFORM_u_${a}\nuniform lowp float u_${a}_t;\nin ${i} ${r} a_${a};\n#else\nuniform ${i} ${o} u_${a};\n#endif\n`:"vec4"===s?`\n#ifndef HAS_UNIFORM_u_${a}\n ${i} ${o} ${a} = a_${a};\n#else\n ${i} ${o} ${a} = u_${a};\n#endif\n`:`\n#ifndef HAS_UNIFORM_u_${a}\n ${i} ${o} ${a} = unpack_mix_${s}(a_${a}, u_${a}_t);\n#else\n ${i} ${o} ${a} = u_${a};\n#endif\n`})),staticAttributes:o,staticUniforms:s}}class Bt{constructor(e,t,i){this.vertexBuffer=e,this.indexBuffer=t,this.segments=i;}destroy(){this.vertexBuffer.destroy(),this.indexBuffer.destroy(),this.segments.destroy(),this.vertexBuffer=null,this.indexBuffer=null,this.segments=null;}}var Ot=t.aS([{name:"a_pos",type:"Int16",components:2}]);const jt="#define PROJECTION_MERCATOR",Nt="mercator";class Zt{constructor(){this._cachedMesh=null;}get name(){return "mercator"}get useSubdivision(){return !1}get shaderVariantName(){return Nt}get shaderDefine(){return jt}get shaderPreludeCode(){return Ft.projectionMercator}get vertexShaderPreludeCode(){return Ft.projectionMercator.vertexSource}get subdivisionGranularity(){return t.aT.noSubdivision}get useGlobeControls(){return !1}get transitionState(){return 0}get latitudeErrorCorrectionRadians(){return 0}destroy(){}updateGPUdependent(e){}getMeshFromTileID(e,i,o,a,r){if(this._cachedMesh)return this._cachedMesh;const s=new t.aU;s.emplaceBack(0,0),s.emplaceBack(t.a6,0),s.emplaceBack(0,t.a6),s.emplaceBack(t.a6,t.a6);const n=e.createVertexBuffer(s,Ot.members),l=t.aV.simpleSegment(0,0,4,2),c=new t.aW;c.emplaceBack(1,0,2),c.emplaceBack(1,2,3);const h=e.createIndexBuffer(c);return this._cachedMesh=new Bt(n,h,l),this._cachedMesh}recalculate(){}hasTransition(){return !1}setErrorQueryLatitudeDegrees(e){}}class Ut{constructor(e=0,t=0,i=0,o=0){if(isNaN(e)||e<0||isNaN(t)||t<0||isNaN(i)||i<0||isNaN(o)||o<0)throw new Error("Invalid value for edge-insets, top, bottom, left and right must all be numbers");this.top=e,this.bottom=t,this.left=i,this.right=o;}interpolate(e,i,o){return null!=i.top&&null!=e.top&&(this.top=t.H.number(e.top,i.top,o)),null!=i.bottom&&null!=e.bottom&&(this.bottom=t.H.number(e.bottom,i.bottom,o)),null!=i.left&&null!=e.left&&(this.left=t.H.number(e.left,i.left,o)),null!=i.right&&null!=e.right&&(this.right=t.H.number(e.right,i.right,o)),this}getCenter(e,i){const o=t.al((this.left+e-this.right)/2,0,e),a=t.al((this.top+i-this.bottom)/2,0,i);return new t.P(o,a)}equals(e){return this.top===e.top&&this.bottom===e.bottom&&this.left===e.left&&this.right===e.right}clone(){return new Ut(this.top,this.bottom,this.left,this.right)}toJSON(){return {top:this.top,bottom:this.bottom,left:this.left,right:this.right}}}function Gt(e,t){if(!e.renderWorldCopies||e.lngRange)return;const i=t.lng-e.center.lng;t.lng+=i>180?-360:i<-180?360:0;}function Vt(e){return Math.max(0,Math.floor(e))}class Wt{constructor(e,i){var o;this.applyConstrain=(e,t)=>null!==this._constrainOverride?this._constrainOverride(e,t):this._callbacks.defaultConstrain(e,t),this._callbacks=e,this._tileSize=512,this._renderWorldCopies=void 0===(null==i?void 0:i.renderWorldCopies)||!!(null==i?void 0:i.renderWorldCopies),this._minZoom=(null==i?void 0:i.minZoom)||0,this._maxZoom=(null==i?void 0:i.maxZoom)||22,this._minPitch=null==(null==i?void 0:i.minPitch)?0:null==i?void 0:i.minPitch,this._maxPitch=null==(null==i?void 0:i.maxPitch)?60:null==i?void 0:i.maxPitch,this._constrainOverride=null!==(o=null==i?void 0:i.constrainOverride)&&void 0!==o?o:null,this.setMaxBounds(),this._width=0,this._height=0,this._center=new t.W(0,0),this._elevation=0,this._zoom=0,this._tileZoom=Vt(this._zoom),this._scale=t.ao(this._zoom),this._bearingInRadians=0,this._fovInRadians=.6435011087932844,this._pitchInRadians=0,this._rollInRadians=0,this._unmodified=!0,this._edgeInsets=new Ut,this._minElevationForCurrentTile=0,this._autoCalculateNearFarZ=!0;}apply(e,i,o){this._constrainOverride=e.constrainOverride,this._latRange=e.latRange,this._lngRange=e.lngRange,this._width=e.width,this._height=e.height,this._center=e.center,this._elevation=e.elevation,this._minElevationForCurrentTile=e.minElevationForCurrentTile,this._zoom=e.zoom,this._tileZoom=Vt(this._zoom),this._scale=t.ao(this._zoom),this._bearingInRadians=e.bearingInRadians,this._fovInRadians=e.fovInRadians,this._pitchInRadians=e.pitchInRadians,this._rollInRadians=e.rollInRadians,this._unmodified=e.unmodified,this._edgeInsets=new Ut(e.padding.top,e.padding.bottom,e.padding.left,e.padding.right),this._minZoom=e.minZoom,this._maxZoom=e.maxZoom,this._minPitch=e.minPitch,this._maxPitch=e.maxPitch,this._renderWorldCopies=e.renderWorldCopies,this._cameraToCenterDistance=e.cameraToCenterDistance,this._nearZ=e.nearZ,this._farZ=e.farZ,this._autoCalculateNearFarZ=!o&&e.autoCalculateNearFarZ,i&&this.constrainInternal(),this._calcMatrices();}get pixelsToClipSpaceMatrix(){return this._pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._clipSpaceToPixelsMatrix}get minElevationForCurrentTile(){return this._minElevationForCurrentTile}setMinElevationForCurrentTile(e){this._minElevationForCurrentTile=e;}get tileSize(){return this._tileSize}get tileZoom(){return this._tileZoom}get scale(){return this._scale}get width(){return this._width}get height(){return this._height}get bearingInRadians(){return this._bearingInRadians}get lngRange(){return this._lngRange}get latRange(){return this._latRange}get pixelsToGLUnits(){return this._pixelsToGLUnits}get minZoom(){return this._minZoom}setMinZoom(e){this._minZoom!==e&&(this._minZoom=e,this.setZoom(this.applyConstrain(this._center,this.zoom).zoom));}get maxZoom(){return this._maxZoom}setMaxZoom(e){this._maxZoom!==e&&(this._maxZoom=e,this.setZoom(this.applyConstrain(this._center,this.zoom).zoom));}get minPitch(){return this._minPitch}setMinPitch(e){this._minPitch!==e&&(this._minPitch=e,this.setPitch(Math.max(this.pitch,e)));}get maxPitch(){return this._maxPitch}setMaxPitch(e){this._maxPitch!==e&&(this._maxPitch=e,this.setPitch(Math.min(this.pitch,e)));}get renderWorldCopies(){return this._renderWorldCopies}setRenderWorldCopies(e){void 0===e?e=!0:null===e&&(e=!1),this._renderWorldCopies=e;}get constrainOverride(){return this._constrainOverride}setConstrainOverride(e){void 0===e&&(e=null),this._constrainOverride!==e&&(this._constrainOverride=e,this.constrainInternal(),this._calcMatrices());}get worldSize(){return this._tileSize*this._scale}get centerOffset(){return this.centerPoint._sub(this.size._div(2))}get size(){return new t.P(this._width,this._height)}get bearing(){return this._bearingInRadians/Math.PI*180}setBearing(e){const i=t.X(e,-180,180)*Math.PI/180;var a,r,s,n,l,c,h,u,d;this._bearingInRadians!==i&&(this._unmodified=!1,this._bearingInRadians=i,this._calcMatrices(),this._rotationMatrix=o(),a=this._rotationMatrix,s=-this._bearingInRadians,n=(r=this._rotationMatrix)[0],l=r[1],c=r[2],h=r[3],u=Math.sin(s),d=Math.cos(s),a[0]=n*d+c*u,a[1]=l*d+h*u,a[2]=n*-u+c*d,a[3]=l*-u+h*d);}get rotationMatrix(){return this._rotationMatrix}get pitchInRadians(){return this._pitchInRadians}get pitch(){return this._pitchInRadians/Math.PI*180}setPitch(e){const i=t.al(e,this.minPitch,this.maxPitch)/180*Math.PI;this._pitchInRadians!==i&&(this._unmodified=!1,this._pitchInRadians=i,this._calcMatrices());}get rollInRadians(){return this._rollInRadians}get roll(){return this._rollInRadians/Math.PI*180}setRoll(e){const t=e/180*Math.PI;this._rollInRadians!==t&&(this._unmodified=!1,this._rollInRadians=t,this._calcMatrices());}get fovInRadians(){return this._fovInRadians}get fov(){return t.aX(this._fovInRadians)}setFov(e){e=t.al(e,.1,150),this.fov!==e&&(this._unmodified=!1,this._fovInRadians=t.an(e),this._calcMatrices());}get zoom(){return this._zoom}setZoom(e){const i=this.applyConstrain(this._center,e).zoom;this._zoom!==i&&(this._unmodified=!1,this._zoom=i,this._tileZoom=Math.max(0,Math.floor(i)),this._scale=t.ao(i),this.constrainInternal(),this._calcMatrices());}get center(){return this._center}setCenter(e){e.lat===this._center.lat&&e.lng===this._center.lng||(this._unmodified=!1,this._center=e,this.constrainInternal(),this._calcMatrices());}get elevation(){return this._elevation}setElevation(e){e!==this._elevation&&(this._elevation=e,this.constrainInternal(),this._calcMatrices());}get padding(){return this._edgeInsets.toJSON()}setPadding(e){this._edgeInsets.equals(e)||(this._unmodified=!1,this._edgeInsets.interpolate(this._edgeInsets,e,1),this._calcMatrices());}get centerPoint(){return this._edgeInsets.getCenter(this._width,this._height)}get pixelsPerMeter(){return this._pixelPerMeter}get unmodified(){return this._unmodified}get cameraToCenterDistance(){return this._cameraToCenterDistance}get nearZ(){return this._nearZ}get farZ(){return this._farZ}get autoCalculateNearFarZ(){return this._autoCalculateNearFarZ}overrideNearFarZ(e,t){this._autoCalculateNearFarZ=!1,this._nearZ=e,this._farZ=t,this._calcMatrices();}clearNearFarZOverride(){this._autoCalculateNearFarZ=!0,this._calcMatrices();}isPaddingEqual(e){return this._edgeInsets.equals(e)}interpolatePadding(e,t,i){this._unmodified=!1,this._edgeInsets.interpolate(e,t,i),this.constrainInternal(),this._calcMatrices();}resize(e,t,i=!0){this._width=e,this._height=t,i&&this.constrainInternal(),this._calcMatrices();}getMaxBounds(){var e,t;return 2!==(null===(e=this._latRange)||void 0===e?void 0:e.length)||2!==(null===(t=this._lngRange)||void 0===t?void 0:t.length)?null:new G([this._lngRange[0],this._latRange[0]],[this._lngRange[1],this._latRange[1]])}setMaxBounds(e){e?(this._lngRange=[e.getWest(),e.getEast()],this._latRange=[e.getSouth(),e.getNorth()],this.constrainInternal()):(this._lngRange=null,this._latRange=[-t.am,t.am]);}getCameraQueryGeometry(e,i){if(1===i.length)return [i[0],e];{const{minX:o,minY:a,maxX:r,maxY:s}=t.a8.fromPoints(i).extend(e);return [new t.P(o,a),new t.P(r,a),new t.P(r,s),new t.P(o,s),new t.P(o,a)]}}constrainInternal(){if(!this.center||!this._width||!this._height||this._constraining)return;this._constraining=!0;const e=this._unmodified,{center:t,zoom:i}=this.applyConstrain(this.center,this.zoom);this.setCenter(t),this.setZoom(i),this._unmodified=e,this._constraining=!1;}_calcMatrices(){if(this._width&&this._height){this._pixelsToGLUnits=[2/this._width,-2/this._height];let e=t.ap(new Float64Array(16));t.S(e,e,[this._width/2,-this._height/2,1]),t.Q(e,e,[1,-1,0]),this._clipSpaceToPixelsMatrix=e,e=t.ap(new Float64Array(16)),t.S(e,e,[1,-1,1]),t.Q(e,e,[-1,-1,0]),t.S(e,e,[2/this._width,2/this._height,1]),this._pixelsToClipSpaceMatrix=e,this._cameraToCenterDistance=.5/Math.tan(this.fovInRadians/2)*this._height;}this._callbacks.calcMatrices();}calculateCenterFromCameraLngLatAlt(e,i,o,a){const r=void 0!==o?o:this.bearing,s=a=void 0!==a?a:this.pitch,{distanceToCenter:n,clampedElevation:l}=this._distanceToCenterFromAltElevationPitch(i,this.elevation,s),{x:c,y:h}=be(s,r),u=t.a7.fromLngLat(e,i);let d,_,p=t.aY(1,u.y),m=0;do{if(m+=1,m>10)break;_=n/p,d=new t.a7(u.x+c*_,u.y+h*_),p=1/d.meterInMercatorCoordinateUnits();}while(Math.abs(n-_*p)>1e-12);return {center:d.toLngLat(),elevation:l,zoom:t.ar(this.height/2/Math.tan(this.fovInRadians/2)/_/this.tileSize)}}recalculateZoomAndCenter(e){if(this.elevation-e==0)return;const i=1/this.worldSize,o=t.aq(1,this.center.lat)*this.worldSize,a=t.a7.fromLngLat(this.center,this.elevation),r=a.x/i,s=a.y/i,n=a.z/i,l=this.pitch,c=this.bearing,{x:h,y:u,z:d}=be(l,c),_=this.cameraToCenterDistance,p=r+_*-h,m=s+_*-u,f=n+_*d,{distanceToCenter:g,clampedElevation:v}=this._distanceToCenterFromAltElevationPitch(f/o,e,l),x=g*o,b=new t.a7((p+h*x)*i,(m+u*x)*i,0).toLngLat(),y=t.aq(1,b.lat),w=t.ar(this.height/2/Math.tan(this.fovInRadians/2)/g/y/this.tileSize);this._elevation=v,this._center=b,this.setZoom(w);}_distanceToCenterFromAltElevationPitch(e,i,o){const a=-Math.cos(t.an(o)),r=e-i;let s,n=i;return a*r>=0||Math.abs(a)<.1?(s=1e4,n=e+s*a):s=-r/a,{distanceToCenter:s,clampedElevation:n}}getCameraPoint(){const e=Math.tan(this.pitchInRadians)*(this.cameraToCenterDistance||1);return this.centerPoint.add(new t.P(e*Math.sin(this.rollInRadians),e*Math.cos(this.rollInRadians)))}getCameraAltitude(){return Math.cos(this.pitchInRadians)*this._cameraToCenterDistance/this._pixelPerMeter+this.elevation}getCameraLngLat(){const e=t.aq(1,this.center.lat)*this.worldSize;return xe(this.center,this.elevation,this.pitch,this.bearing,this.cameraToCenterDistance/e).toLngLat()}getMercatorTileCoordinates(e){if(!e)return [0,0,1,1];const i=e.canonical.z>=0?1<this.max[0]||e.aabb.min[1]>this.max[1]||e.aabb.min[2]>this.max[2]||e.aabb.max[0]0?(t+=e[o]*this.min[o],i+=e[o]*this.max[o]):(i+=e[o]*this.min[o],t+=e[o]*this.max[o]);return t>=0?2:i<0?0:1}}class $t{distanceToTile2d(e,t,i,o){const a=o,r=a.distanceX([e,t]),s=a.distanceY([e,t]);return Math.hypot(r,s)}getWrap(e,t,i){return i}getTileBoundingVolume(e,i,o,a){var r,s;let n=0,l=0;if(null==a?void 0:a.terrain){const c=new t.a3(e.z,i,e.z,e.x,e.y),h=a.terrain.getMinMaxElevation(c);n=null!==(r=h.minElevation)&&void 0!==r?r:Math.min(0,o),l=null!==(s=h.maxElevation)&&void 0!==s?s:Math.max(0,o);}const c=1<a}allowWorldCopies(){return !0}prepareNextFrame(){}}class Ht{constructor(e,t,i){this.points=e,this.planes=t,this.aabb=i;}static fromInvProjectionMatrix(e,i=1,o=0,a,r){const s=r?[[6,5,4],[0,1,2],[0,3,7],[2,1,5],[3,2,6],[0,4,5]]:[[0,1,2],[6,5,4],[0,3,7],[2,1,5],[3,2,6],[0,4,5]],n=Math.pow(2,o),l=[[-1,1,-1,1],[1,1,-1,1],[1,-1,-1,1],[-1,-1,-1,1],[-1,1,1,1],[1,1,1,1],[1,-1,1,1],[-1,-1,1,1]].map((o=>function(e,i,o,a){const r=t.aE([],e,i),s=1/r[3]/o*a;return t.b4(r,r,[s,s,1/r[3],s])}(o,e,i,n)));a&&function(e,i,o,a){const r=a?4:0,s=a?0:4;let n=0;const l=[],c=[];for(let i=0;i<4;i++){const o=t.b0([],e[i+s],e[i+r]),a=t.b5(o);t.aZ(o,o,1/a),l.push(a),c.push(o);}for(let i=0;i<4;i++){const a=t.b6(e[i+r],c[i],o);n=null!==a&&a>=0?Math.max(n,a):Math.max(n,l[i]);}const h=function(e,i){const o=t.b0([],e[i[0]],e[i[1]]),a=t.b0([],e[i[2]],e[i[1]]),r=[0,0,0,0];return t.b1(r,t.b2([],o,a)),r[3]=-t.b3(r,e[i[0]]),r}(e,i),u=function(e,i){const o=t.b7(e),a=t.b8([],e,1/o),r=t.b0([],i,t.aZ([],a,t.b3(i,a))),s=t.b7(r);if(s>0){const e=Math.sqrt(1-a[3]*a[3]),o=t.aZ([],a,-a[3]),n=t.a_([],o,t.aZ([],r,e/s));return t.b9(i,n)}return null}(o,h);if(null!==u){const e=u/t.b3(c[0],h);n=Math.min(n,e);}for(let t=0;t<4;t++){const i=Math.min(n,l[t]);e[t+s]=[e[t+r][0]+c[t][0]*i,e[t+r][1]+c[t][1]*i,e[t+r][2]+c[t][2]*i,1];}}(l,s[0],a,r);const c=s.map((e=>{const i=t.b0([],l[e[0]],l[e[1]]),o=t.b0([],l[e[2]],l[e[1]]),a=t.b1([],t.b2([],i,o)),r=-t.b3(a,l[e[1]]);return a.concat(r)})),h=[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY],u=[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];for(const e of l)for(let t=0;t<3;t++)h[t]=Math.min(h[t],e[t]),u[t]=Math.max(u[t],e[t]);return new Ht(l,c,new qt(h,u))}}class Xt{get pixelsToClipSpaceMatrix(){return this._helper.pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._helper.clipSpaceToPixelsMatrix}get pixelsToGLUnits(){return this._helper.pixelsToGLUnits}get centerOffset(){return this._helper.centerOffset}get size(){return this._helper.size}get rotationMatrix(){return this._helper.rotationMatrix}get centerPoint(){return this._helper.centerPoint}get pixelsPerMeter(){return this._helper.pixelsPerMeter}setMinZoom(e){this._helper.setMinZoom(e);}setMaxZoom(e){this._helper.setMaxZoom(e);}setMinPitch(e){this._helper.setMinPitch(e);}setMaxPitch(e){this._helper.setMaxPitch(e);}setRenderWorldCopies(e){this._helper.setRenderWorldCopies(e);}setBearing(e){this._helper.setBearing(e);}setPitch(e){this._helper.setPitch(e);}setRoll(e){this._helper.setRoll(e);}setFov(e){this._helper.setFov(e);}setZoom(e){this._helper.setZoom(e);}setCenter(e){this._helper.setCenter(e);}setElevation(e){this._helper.setElevation(e);}setMinElevationForCurrentTile(e){this._helper.setMinElevationForCurrentTile(e);}setPadding(e){this._helper.setPadding(e);}interpolatePadding(e,t,i){this._helper.interpolatePadding(e,t,i);}isPaddingEqual(e){return this._helper.isPaddingEqual(e)}resize(e,t,i=!0){this._helper.resize(e,t,i);}getMaxBounds(){return this._helper.getMaxBounds()}setMaxBounds(e){this._helper.setMaxBounds(e);}setConstrainOverride(e){this._helper.setConstrainOverride(e);}overrideNearFarZ(e,t){this._helper.overrideNearFarZ(e,t);}clearNearFarZOverride(){this._helper.clearNearFarZOverride();}getCameraQueryGeometry(e){return this._helper.getCameraQueryGeometry(this.getCameraPoint(),e)}get tileSize(){return this._helper.tileSize}get tileZoom(){return this._helper.tileZoom}get scale(){return this._helper.scale}get worldSize(){return this._helper.worldSize}get width(){return this._helper.width}get height(){return this._helper.height}get lngRange(){return this._helper.lngRange}get latRange(){return this._helper.latRange}get minZoom(){return this._helper.minZoom}get maxZoom(){return this._helper.maxZoom}get zoom(){return this._helper.zoom}get center(){return this._helper.center}get minPitch(){return this._helper.minPitch}get maxPitch(){return this._helper.maxPitch}get pitch(){return this._helper.pitch}get pitchInRadians(){return this._helper.pitchInRadians}get roll(){return this._helper.roll}get rollInRadians(){return this._helper.rollInRadians}get bearing(){return this._helper.bearing}get bearingInRadians(){return this._helper.bearingInRadians}get fov(){return this._helper.fov}get fovInRadians(){return this._helper.fovInRadians}get elevation(){return this._helper.elevation}get minElevationForCurrentTile(){return this._helper.minElevationForCurrentTile}get padding(){return this._helper.padding}get unmodified(){return this._helper.unmodified}get renderWorldCopies(){return this._helper.renderWorldCopies}get cameraToCenterDistance(){return this._helper.cameraToCenterDistance}get constrainOverride(){return this._helper.constrainOverride}get nearZ(){return this._helper.nearZ}get farZ(){return this._helper.farZ}get autoCalculateNearFarZ(){return this._helper.autoCalculateNearFarZ}setTransitionState(e,t){}constructor(e){this._posMatrixCache=new Map,this._alignedPosMatrixCache=new Map,this._fogMatrixCacheF32=new Map,this.defaultConstrain=(e,i)=>{i=t.al(+i,this.minZoom,this.maxZoom);const o={center:new t.W(e.lng,e.lat),zoom:i};let a=this._helper._lngRange;if(!this._helper._renderWorldCopies&&null===a){const e=180-1e-10;a=[-e,e];}const r=this.tileSize*t.ao(o.zoom);let s=0,n=r,l=0,c=r,h=0,u=0;const{x:d,y:_}=this.size;if(this._helper._latRange){const e=this._helper._latRange;s=t.Y(e[1])*r,n=t.Y(e[0])*r,n-s<_&&(h=_/(n-s));}a&&(l=t.X(t.Z(a[0])*r,0,r),c=t.X(t.Z(a[1])*r,0,r),cn&&(g=n-e);}if(a){const e=(l+c)/2;let i=p;this._helper._renderWorldCopies&&(i=t.X(p,e-r/2,e+r/2));const o=d/2;i-oc&&(f=c-o);}if(void 0!==f||void 0!==g){const e=new t.P(null!=f?f:p,null!=g?g:m);o.center=fe(r,e).wrap();}return o},this.applyConstrain=(e,t)=>this._helper.applyConstrain(e,t),this._helper=new Wt({calcMatrices:()=>this._calcMatrices(),defaultConstrain:(e,t)=>this.defaultConstrain(e,t)},e),this._coveringTilesDetailsProvider=new $t;}clone(){const e=new Xt;return e.apply(this,!1),e}apply(e,t,i){this._helper.apply(e,t,i);}get cameraPosition(){return this._cameraPosition}get projectionMatrix(){return this._projectionMatrix}get modelViewProjectionMatrix(){return this._viewProjMatrix}get inverseProjectionMatrix(){return this._invProjMatrix}get mercatorMatrix(){return this._mercatorMatrix}getVisibleUnwrappedCoordinates(e){const i=[new t.ba(0,e)];if(this._helper._renderWorldCopies){const o=this.screenPointToMercatorCoordinate(new t.P(0,0)),a=this.screenPointToMercatorCoordinate(new t.P(this._helper._width,0)),r=this.screenPointToMercatorCoordinate(new t.P(this._helper._width,this._helper._height)),s=this.screenPointToMercatorCoordinate(new t.P(0,this._helper._height)),n=Math.floor(Math.min(o.x,a.x,r.x,s.x)),l=Math.floor(Math.max(o.x,a.x,r.x,s.x)),c=1;for(let o=n-c;o<=l+c;o++)0!==o&&i.push(new t.ba(o,e));}return i}getCameraFrustum(){return Ht.fromInvProjectionMatrix(this._invViewProjMatrix,this.worldSize)}getClippingPlane(){return null}getCoveringTilesDetailsProvider(){return this._coveringTilesDetailsProvider}recalculateZoomAndCenter(e){const t=this.screenPointToLocation(this.centerPoint,e),i=e?e.getElevationForLngLatZoom(t,this._helper._tileZoom):0;this._helper.recalculateZoomAndCenter(i);}setLocationAtPoint(e,i){const o=t.aq(this.elevation,this.center.lat),a=this.screenPointToMercatorCoordinateAtZ(i,o),r=this.screenPointToMercatorCoordinateAtZ(this.centerPoint,o),s=t.a7.fromLngLat(e),n=new t.a7(s.x-(a.x-r.x),s.y-(a.y-r.y));this.setCenter(null==n?void 0:n.toLngLat()),this._helper._renderWorldCopies&&this.setCenter(this.center.wrap());}locationToScreenPoint(e,i){return i?this.coordinatePoint(t.a7.fromLngLat(e),i.getElevationForLngLat(e,this),this._pixelMatrix3D):this.coordinatePoint(t.a7.fromLngLat(e))}screenPointToLocation(e,t){var i;return null===(i=this.screenPointToMercatorCoordinate(e,t))||void 0===i?void 0:i.toLngLat()}screenPointToMercatorCoordinate(e,t){if(t){const i=t.pointCoordinate(e);if(null!=i)return i}return this.screenPointToMercatorCoordinateAtZ(e)}screenPointToMercatorCoordinateAtZ(e,i){const o=i||0,a=[e.x,e.y,0,1],r=[e.x,e.y,1,1];t.aE(a,a,this._pixelMatrixInverse),t.aE(r,r,this._pixelMatrixInverse);const s=a[3],n=r[3],l=a[1]/s,c=r[1]/n,h=a[2]/s,u=r[2]/n,d=h===u?0:(o-h)/(u-h);return new t.a7(t.H.number(a[0]/s,r[0]/n,d)/this.worldSize,t.H.number(l,c,d)/this.worldSize,o)}coordinatePoint(e,i=0,o=this._pixelMatrix){const a=[e.x*this.worldSize,e.y*this.worldSize,i,1];return t.aE(a,a,o),new t.P(a[0]/a[3],a[1]/a[3])}getBounds(){const e=Math.max(0,this._helper._height/2-ge(this));return (new G).extend(this.screenPointToLocation(new t.P(0,e))).extend(this.screenPointToLocation(new t.P(this._helper._width,e))).extend(this.screenPointToLocation(new t.P(this._helper._width,this._helper._height))).extend(this.screenPointToLocation(new t.P(0,this._helper._height)))}isPointOnMapSurface(e,t){return t?null!=t.pointCoordinate(e):e.y>this.height/2-ge(this)}calculatePosMatrix(e,i=!1,o){var a;const r=null!==(a=e.key)&&void 0!==a?a:t.bb(e.wrap,e.canonical.z,e.canonical.z,e.canonical.x,e.canonical.y),s=i?this._alignedPosMatrixCache:this._posMatrixCache;if(s.has(r)){const e=s.get(r);return o?e.f32:e.f64}const n=ve(e,this.worldSize);t.U(n,i?this._alignedProjMatrix:this._viewProjMatrix,n);const l={f64:n,f32:new Float32Array(n)};return s.set(r,l),o?l.f32:l.f64}calculateFogMatrix(e){const i=e.key,o=this._fogMatrixCacheF32;if(o.has(i))return o.get(i);const a=ve(e,this.worldSize);return t.U(a,this._fogMatrix,a),o.set(i,new Float32Array(a)),o.get(i)}calculateCenterFromCameraLngLatAlt(e,t,i,o){return this._helper.calculateCenterFromCameraLngLatAlt(e,t,i,o)}_calculateNearFarZIfNeeded(e,i,o){if(!this._helper.autoCalculateNearFarZ)return;const a=Math.min(this.elevation,this.minElevationForCurrentTile,this.getCameraAltitude()-100),r=e-a*this._helper._pixelPerMeter/Math.cos(i),s=a<0?r:e,n=Math.PI/2+this.pitchInRadians,l=t.an(this.fov)*(Math.abs(Math.cos(t.an(this.roll)))*this.height+Math.abs(Math.sin(t.an(this.roll)))*this.width)/this.height*(.5+o.y/this.height),c=Math.sin(l)*s/Math.sin(t.al(Math.PI-n-l,.01,Math.PI-.01)),h=ge(this),u=Math.atan(h/this._helper.cameraToCenterDistance),d=t.an(.75),_=u>d?2*u*(.5+o.y/(2*h)):d,p=Math.sin(_)*s/Math.sin(t.al(Math.PI-n-_,.01,Math.PI-.01)),m=Math.min(c,p);this._helper._farZ=1.01*(Math.cos(Math.PI/2-i)*m+s),this._helper._nearZ=this._helper._height/50;}_calcMatrices(){if(!this._helper._height)return;const e=this.centerOffset,i=me(this.worldSize,this.center),o=i.x,a=i.y;this._helper._pixelPerMeter=t.aq(1,this.center.lat)*this.worldSize;const r=t.an(Math.min(this.pitch,pe)),s=Math.max(this._helper.cameraToCenterDistance/2,this._helper.cameraToCenterDistance+this._helper._elevation*this._helper._pixelPerMeter/Math.cos(r));let n;var l,c;this._calculateNearFarZIfNeeded(s,r,e),n=new Float64Array(16),t.bc(n,this.fovInRadians,this._helper._width/this._helper._height,this._helper._nearZ,this._helper._farZ),this._invProjMatrix=new Float64Array(16),(l=this._invProjMatrix)[0]=1/(c=n)[0],l[1]=0,l[2]=0,l[3]=0,l[4]=0,l[5]=1/c[5],l[6]=0,l[7]=0,l[8]=0,l[9]=0,l[10]=0,l[11]=1/c[14],l[12]=0,l[13]=0,l[14]=-1,l[15]=c[10]/c[14],n[8]=2*-e.x/this._helper._width,n[9]=2*e.y/this._helper._height,this._projectionMatrix=t.bd(n),t.S(n,n,[1,-1,1]),t.Q(n,n,[0,0,-this._helper.cameraToCenterDistance]),t.be(n,n,-this.rollInRadians),t.bf(n,n,this.pitchInRadians),t.be(n,n,-this.bearingInRadians),t.Q(n,n,[-o,-a,0]),this._mercatorMatrix=t.S([],n,[this.worldSize,this.worldSize,this.worldSize]),t.S(n,n,[1,1,this._helper._pixelPerMeter]),this._pixelMatrix=t.U(new Float64Array(16),this.clipSpaceToPixelsMatrix,n),t.Q(n,n,[0,0,-this.elevation]),this._viewProjMatrix=n,this._invViewProjMatrix=t.bg([],n);const h=[0,0,-1,1];t.aE(h,h,this._invViewProjMatrix),this._cameraPosition=[h[0]/h[3],h[1]/h[3],h[2]/h[3]],this._fogMatrix=new Float64Array(16),t.bc(this._fogMatrix,this.fovInRadians,this.width/this.height,s,this._helper._farZ),this._fogMatrix[8]=2*-e.x/this.width,this._fogMatrix[9]=2*e.y/this.height,t.S(this._fogMatrix,this._fogMatrix,[1,-1,1]),t.Q(this._fogMatrix,this._fogMatrix,[0,0,-this.cameraToCenterDistance]),t.be(this._fogMatrix,this._fogMatrix,-this.rollInRadians),t.bf(this._fogMatrix,this._fogMatrix,this.pitchInRadians),t.be(this._fogMatrix,this._fogMatrix,-this.bearingInRadians),t.Q(this._fogMatrix,this._fogMatrix,[-o,-a,0]),t.S(this._fogMatrix,this._fogMatrix,[1,1,this._helper._pixelPerMeter]),t.Q(this._fogMatrix,this._fogMatrix,[0,0,-this.elevation]),this._pixelMatrix3D=t.U(new Float64Array(16),this.clipSpaceToPixelsMatrix,n);const u=this._helper._width%2/2,d=this._helper._height%2/2,_=Math.cos(this.bearingInRadians),p=Math.sin(-this.bearingInRadians),m=o-Math.round(o)+_*u+p*d,f=a-Math.round(a)+_*d+p*u,g=new Float64Array(n);if(t.Q(g,g,[m>.5?m-1:m,f>.5?f-1:f,0]),this._alignedProjMatrix=g,n=t.bg(new Float64Array(16),this._pixelMatrix),!n)throw new Error("failed to invert matrix");this._pixelMatrixInverse=n,this._clearMatrixCaches();}_clearMatrixCaches(){this._posMatrixCache.clear(),this._alignedPosMatrixCache.clear(),this._fogMatrixCacheF32.clear();}maxPitchScaleFactor(){if(!this._pixelMatrixInverse)return 1;const e=this.screenPointToMercatorCoordinate(new t.P(0,0)),i=[e.x*this.worldSize,e.y*this.worldSize,0,1];return t.aE(i,i,this._pixelMatrix)[3]/this._helper.cameraToCenterDistance}getCameraPoint(){return this._helper.getCameraPoint()}getCameraAltitude(){return this._helper.getCameraAltitude()}getCameraLngLat(){const e=t.aq(1,this.center.lat)*this.worldSize;return xe(this.center,this.elevation,this.pitch,this.bearing,this._helper.cameraToCenterDistance/e).toLngLat()}lngLatToCameraDepth(e,i){const o=t.a7.fromLngLat(e),a=[o.x*this.worldSize,o.y*this.worldSize,i,1];return t.aE(a,a,this._viewProjMatrix),a[2]/a[3]}getProjectionData(e){const{overscaledTileID:i,aligned:o,applyTerrainMatrix:a}=e,r=this._helper.getMercatorTileCoordinates(i),s=i?this.calculatePosMatrix(i,o,!0):null;let n;return n=(null==i?void 0:i.terrainRttPosMatrix32f)&&a?i.terrainRttPosMatrix32f:s||t.bh(),{mainMatrix:n,tileMercatorCoords:r,clippingPlane:[0,0,0,0],projectionTransition:0,fallbackMatrix:n}}isLocationOccluded(e){return !1}getPixelScale(){return 1}getCircleRadiusCorrection(){return 1}getPitchedTextCorrection(e,t,i){return 1}transformLightDirection(e){return t.a$(e)}getRayDirectionFromPixel(e){throw new Error("Not implemented.")}projectTileCoordinates(e,i,o,a){const r=this.calculatePosMatrix(o);let s;a?(s=[e,i,a(e,i),1],t.aE(s,s,r)):(s=[e,i,0,1],nt(s,s,r));const n=s[3];return {point:new t.P(s[0]/n,s[1]/n),signedDistanceFromCamera:n,isOccluded:!1}}populateCache(e){for(const t of e)this.calculatePosMatrix(t);}getMatrixForModel(e,i){const o=t.a7.fromLngLat(e,i),a=o.meterInMercatorCoordinateUnits(),r=t.bi();return t.Q(r,r,[o.x,o.y,o.z]),t.be(r,r,Math.PI),t.bf(r,r,Math.PI/2),t.S(r,r,[-a,a,a]),r}getProjectionDataForCustomLayer(e=!0){const i=new t.a3(0,0,0,0,0),o=this.getProjectionData({overscaledTileID:i,applyGlobeMatrix:e}),a=ve(i,this.worldSize);t.U(a,this._viewProjMatrix,a),o.tileMercatorCoords=[0,0,1,1];const r=[t.a6,t.a6,this.worldSize/this._helper.pixelsPerMeter],s=t.bj();return t.S(s,a,r),o.fallbackMatrix=s,o.mainMatrix=s,o}getFastPathSimpleProjectionMatrix(e){return this.calculatePosMatrix(e)}}function Kt(){t.w("Map cannot fit within canvas with the given bounds, padding, and/or offset.");}function Yt(e){if(e.useSlerp)if(e.k<1){const i=t.bk(e.startEulerAngles.roll,e.startEulerAngles.pitch,e.startEulerAngles.bearing),o=t.bk(e.endEulerAngles.roll,e.endEulerAngles.pitch,e.endEulerAngles.bearing),a=new Float64Array(4);t.bl(a,i,o,e.k);const r=t.bm(a);e.tr.setRoll(r.roll),e.tr.setPitch(r.pitch),e.tr.setBearing(r.bearing);}else e.tr.setRoll(e.endEulerAngles.roll),e.tr.setPitch(e.endEulerAngles.pitch),e.tr.setBearing(e.endEulerAngles.bearing);else e.tr.setRoll(t.H.number(e.startEulerAngles.roll,e.endEulerAngles.roll,e.k)),e.tr.setPitch(t.H.number(e.startEulerAngles.pitch,e.endEulerAngles.pitch,e.k)),e.tr.setBearing(t.H.number(e.startEulerAngles.bearing,e.endEulerAngles.bearing,e.k));}function Qt(e,i,o,a,r){const s=r.padding,n=me(r.worldSize,o.getNorthWest()),l=me(r.worldSize,o.getNorthEast()),c=me(r.worldSize,o.getSouthEast()),h=me(r.worldSize,o.getSouthWest()),u=t.an(-a),d=n.rotate(u),_=l.rotate(u),p=c.rotate(u),m=h.rotate(u),f=new t.P(Math.max(d.x,_.x,m.x,p.x),Math.max(d.y,_.y,m.y,p.y)),g=new t.P(Math.min(d.x,_.x,m.x,p.x),Math.min(d.y,_.y,m.y,p.y)),v=f.sub(g),x=(r.width-(s.left+s.right+i.left+i.right))/v.x,b=(r.height-(s.top+s.bottom+i.top+i.bottom))/v.y;if(b<0||x<0)return void Kt();const y=Math.min(t.ar(r.scale*Math.min(x,b)),e.maxZoom),w=t.P.convert(e.offset),T=new t.P((i.left-i.right)/2,(i.top-i.bottom)/2).rotate(t.an(a)),P=w.add(T).mult(r.scale/t.ao(y));return {center:fe(r.worldSize,n.add(c).div(2).sub(P)),zoom:y,bearing:a}}class Jt{get useGlobeControls(){return !1}handlePanInertia(e,t){const i=e.mag(),o=Math.abs(ge(t));return {easingOffset:e.mult(Math.min(.75*o/i,1)),easingCenter:t.center}}handleMapControlsRollPitchBearingZoom(e,t){e.bearingDelta&&t.setBearing(t.bearing+e.bearingDelta),e.pitchDelta&&t.setPitch(t.pitch+e.pitchDelta),e.rollDelta&&t.setRoll(t.roll+e.rollDelta),e.zoomDelta&&t.setZoom(t.zoom+e.zoomDelta);}handleMapControlsPan(e,t,i){e.around.distSqr(t.centerPoint)<.01||t.setLocationAtPoint(i,e.around);}cameraForBoxAndBearing(e,t,i,o,a){return Qt(e,t,i,o,a)}handleJumpToCenterZoom(e,i){e.zoom!==(void 0!==i.zoom?+i.zoom:e.zoom)&&e.setZoom(+i.zoom),void 0!==i.center&&e.setCenter(t.W.convert(i.center));}handleEaseTo(e,i){const o=e.zoom,a=e.padding,r={roll:e.roll,pitch:e.pitch,bearing:e.bearing},s={roll:void 0===i.roll?e.roll:i.roll,pitch:void 0===i.pitch?e.pitch:i.pitch,bearing:void 0===i.bearing?e.bearing:i.bearing},n=void 0!==i.zoom,l=!e.isPaddingEqual(i.padding);let c=!1;const h=n?+i.zoom:e.zoom;let u=e.centerPoint.add(i.offsetAsPoint);const d=e.screenPointToLocation(u),{center:_,zoom:p}=e.applyConstrain(t.W.convert(i.center||d),null!=h?h:o);Gt(e,_);const m=me(e.worldSize,d),f=me(e.worldSize,_).sub(m),g=t.ao(p-o);return c=p!==o,{easeFunc:n=>{if(c&&e.setZoom(t.H.number(o,p,n)),t.bn(r,s)||Yt({startEulerAngles:r,endEulerAngles:s,tr:e,k:n,useSlerp:r.roll!=s.roll}),l&&(e.interpolatePadding(a,i.padding,n),u=e.centerPoint.add(i.offsetAsPoint)),i.around)e.setLocationAtPoint(i.around,i.aroundPoint);else {const i=t.ao(e.zoom-o),a=p>o?Math.min(2,g):Math.max(.5,g),r=Math.pow(a,1-n),s=fe(e.worldSize,m.add(f.mult(n*r)).mult(i));e.setLocationAtPoint(e.renderWorldCopies?s.wrap():s,u);}},isZooming:c,elevationCenter:_}}handleFlyTo(e,i){const o=void 0!==i.zoom,a=e.zoom,r=e.applyConstrain(t.W.convert(i.center||i.locationAtOffset),o?+i.zoom:a),s=r.center,n=r.zoom;Gt(e,s);const l=me(e.worldSize,i.locationAtOffset),c=me(e.worldSize,s).sub(l),h=c.mag(),u=t.ao(n-a);let d;if(void 0!==i.minZoom){const o=Math.min(+i.minZoom,a,n),r=e.applyConstrain(s,o).zoom;d=t.ao(r-a);}return {easeFunc:(i,o,r,h)=>{e.setZoom(1===i?n:a+t.ar(o));const u=1===i?s:fe(e.worldSize,l.add(c.mult(r)).mult(o));e.setLocationAtPoint(e.renderWorldCopies?u.wrap():u,h);},scaleOfZoom:u,targetCenter:s,scaleOfMinZoom:d,pixelPathLength:h}}}class ei{constructor(e,t,i){this.blendFunction=e,this.blendColor=t,this.mask=i;}}ei.Replace=[1,0],ei.disabled=new ei(ei.Replace,t.bo.transparent,[!1,!1,!1,!1]),ei.unblended=new ei(ei.Replace,t.bo.transparent,[!0,!0,!0,!0]),ei.alphaBlended=new ei([1,771],t.bo.transparent,[!0,!0,!0,!0]);const ti=2305;class ii{constructor(e,t,i){this.enable=e,this.mode=t,this.frontFace=i;}}ii.disabled=new ii(!1,1029,ti),ii.backCCW=new ii(!0,1029,ti),ii.frontCCW=new ii(!0,1028,ti);class oi{constructor(e,t,i){this.func=e,this.mask=t,this.range=i;}}oi.ReadOnly=!1,oi.ReadWrite=!0,oi.disabled=new oi(519,oi.ReadOnly,[0,1]);const ai=7680;class ri{constructor(e,t,i,o,a,r){this.test=e,this.ref=t,this.mask=i,this.fail=o,this.depthFail=a,this.pass=r;}}function si(e){return "undefined"!=typeof WebGL2RenderingContext&&e instanceof WebGL2RenderingContext}ri.disabled=new ri({func:519,mask:0},0,0,ai,ai,ai);class ni{get awaitingQuery(){return !!this._readbackQueue}constructor(e){this._readbackWaitFrames=4,this._measureWaitFrames=6,this._texWidth=1,this._texHeight=1,this._measuredError=0,this._updateCount=0,this._lastReadbackFrame=-1e3,this._readbackQueue=null,this._cachedRenderContext=e;const i=e.context,o=i.gl;this._texFormat=o.RGBA,this._texType=o.UNSIGNED_BYTE;const a=new t.aU;a.emplaceBack(-1,-1),a.emplaceBack(2,-1),a.emplaceBack(-1,2);const r=new t.aW;r.emplaceBack(0,1,2),this._fullscreenTriangle=new Bt(i.createVertexBuffer(a,Ot.members),i.createIndexBuffer(r),t.aV.simpleSegment(0,0,a.length,r.length)),this._resultBuffer=new Uint8Array(4),i.activeTexture.set(o.TEXTURE1);const s=o.createTexture();o.bindTexture(o.TEXTURE_2D,s),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.NEAREST),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.NEAREST),o.texImage2D(o.TEXTURE_2D,0,this._texFormat,this._texWidth,this._texHeight,0,this._texFormat,this._texType,null),this._fbo=i.createFramebuffer(this._texWidth,this._texHeight,!1,!1),this._fbo.colorAttachment.set(s),si(o)&&(this._pbo=o.createBuffer(),o.bindBuffer(o.PIXEL_PACK_BUFFER,this._pbo),o.bufferData(o.PIXEL_PACK_BUFFER,4,o.STREAM_READ),o.bindBuffer(o.PIXEL_PACK_BUFFER,null));}destroy(){const e=this._cachedRenderContext.context.gl;this._fullscreenTriangle.destroy(),this._fbo.destroy(),e.deleteBuffer(this._pbo),this._fullscreenTriangle=null,this._fbo=null,this._pbo=null,this._resultBuffer=null;}updateErrorLoop(e,t){const i=this._updateCount;return this._readbackQueue?i>=this._readbackQueue.frameNumberIssued+this._readbackWaitFrames&&this._tryReadback():i>=this._lastReadbackFrame+this._measureWaitFrames&&this._renderErrorTexture(e,t),this._updateCount++,this._measuredError}_bindFramebuffer(){const e=this._cachedRenderContext.context,t=e.gl;e.activeTexture.set(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,this._fbo.colorAttachment.get()),e.bindFramebuffer.set(this._fbo.framebuffer);}_renderErrorTexture(e,i){const o=this._cachedRenderContext.context,a=o.gl;if(this._bindFramebuffer(),o.viewport.set([0,0,this._texWidth,this._texHeight]),o.clear({color:t.bo.transparent}),this._cachedRenderContext.useProgram("projectionErrorMeasurement").draw(o,a.TRIANGLES,oi.disabled,ri.disabled,ei.unblended,ii.disabled,((e,t)=>({u_input:e,u_output_expected:t}))(e,i),null,null,"$clipping",this._fullscreenTriangle.vertexBuffer,this._fullscreenTriangle.indexBuffer,this._fullscreenTriangle.segments),this._pbo&&si(a)){a.bindBuffer(a.PIXEL_PACK_BUFFER,this._pbo),a.readBuffer(a.COLOR_ATTACHMENT0),a.readPixels(0,0,this._texWidth,this._texHeight,this._texFormat,this._texType,0),a.bindBuffer(a.PIXEL_PACK_BUFFER,null);const e=a.fenceSync(a.SYNC_GPU_COMMANDS_COMPLETE,0);a.flush(),this._readbackQueue={frameNumberIssued:this._updateCount,sync:e};}else this._readbackQueue={frameNumberIssued:this._updateCount,sync:null};}_tryReadback(){const e=this._cachedRenderContext.context.gl;if(this._pbo&&this._readbackQueue&&si(e)){const i=e.clientWaitSync(this._readbackQueue.sync,0,0);if(i===e.WAIT_FAILED)return t.w("WebGL2 clientWaitSync failed."),this._readbackQueue=null,void(this._lastReadbackFrame=this._updateCount);if(i===e.TIMEOUT_EXPIRED)return;e.bindBuffer(e.PIXEL_PACK_BUFFER,this._pbo),e.getBufferSubData(e.PIXEL_PACK_BUFFER,0,this._resultBuffer,0,4),e.bindBuffer(e.PIXEL_PACK_BUFFER,null);}else this._bindFramebuffer(),e.readPixels(0,0,this._texWidth,this._texHeight,this._texFormat,this._texType,this._resultBuffer);this._readbackQueue=null,this._measuredError=ni._parseRGBA8float(this._resultBuffer),this._lastReadbackFrame=this._updateCount;}static _parseRGBA8float(e){let t=0;return t+=e[0]/256,t+=e[1]/65536,t+=e[2]/16777216,e[3]<127&&(t=-t),t/128}}const li=t.a6/128;function ci(e,i){const o=void 0!==e.granularity?Math.max(e.granularity,1):1,a=o+(e.generateBorders?2:0),r=o+(e.extendToNorthPole||e.generateBorders?1:0)+(e.extendToSouthPole||e.generateBorders?1:0),s=a+1,n=r+1,l=e.generateBorders?-1:0,c=e.generateBorders||e.extendToNorthPole?-1:0,h=o+(e.generateBorders?1:0),u=o+(e.generateBorders||e.extendToSouthPole?1:0),d=s*n,_=a*r*6,p=s*n>65536;if(p&&"16bit"===i)throw new Error("Granularity is too large and meshes would not fit inside 16 bit vertex indices.");const m=p||"32bit"===i,f=new Int16Array(2*d);let g=0;for(let i=c;i<=u;i++)for(let a=l;a<=h;a++){let r=a/o*t.a6;-1===a&&(r=-li),a===o+1&&(r=t.a6+li);let s=i/o*t.a6;-1===i&&(s=e.extendToNorthPole?t.bq:-li),i===o+1&&(s=e.extendToSouthPole?t.br:t.a6+li),f[g++]=r,f[g++]=s;}const v=m?new Uint32Array(_):new Uint16Array(_);let x=0;for(let e=0;e0}get latitudeErrorCorrectionRadians(){return this._verticalPerspectiveProjection.latitudeErrorCorrectionRadians}get currentProjection(){return this.useGlobeRendering?this._verticalPerspectiveProjection:this._mercatorProjection}get name(){return "globe"}get useSubdivision(){return this.currentProjection.useSubdivision}get shaderVariantName(){return this.currentProjection.shaderVariantName}get shaderDefine(){return this.currentProjection.shaderDefine}get shaderPreludeCode(){return this.currentProjection.shaderPreludeCode}get vertexShaderPreludeCode(){return this.currentProjection.vertexShaderPreludeCode}get subdivisionGranularity(){return this.currentProjection.subdivisionGranularity}get useGlobeControls(){return this.transitionState>0}destroy(){this._mercatorProjection.destroy(),this._verticalPerspectiveProjection.destroy();}updateGPUdependent(e){this._mercatorProjection.updateGPUdependent(e),this._verticalPerspectiveProjection.updateGPUdependent(e);}getMeshFromTileID(e,t,i,o,a){return this.currentProjection.getMeshFromTileID(e,t,i,o,a)}setProjection(e){this._transitionable.setValue("type",(null==e?void 0:e.type)||"mercator");}updateTransitions(e){this._transitioning=this._transitionable.transitioned(e,this._transitioning);}hasTransition(){return this._transitioning.hasTransition()||this.currentProjection.hasTransition()}recalculate(e){this.properties=this._transitioning.possiblyEvaluate(e);}setErrorQueryLatitudeDegrees(e){this._verticalPerspectiveProjection.setErrorQueryLatitudeDegrees(e),this._mercatorProjection.setErrorQueryLatitudeDegrees(e);}}function pi(e){const t=gi(e.worldSize,e.center.lat);return 2*Math.PI*t}function mi(e,i,o,a,r){const s=1/(1<1e-6){const a=e[0]/o,r=Math.acos(e[2]/o),s=(a>0?r:-r)/Math.PI*180;return new t.W(t.X(s,-180,180),i)}return new t.W(0,i)}function xi(e){return Math.cos(e*Math.PI/180)}function bi(e,i){const o=xi(e),a=xi(i);return t.ar(a/o)}function yi(e,i){const o=e.rotate(i.bearingInRadians),a=i.zoom+bi(i.center.lat,0),r=t.bt(1/xi(i.center.lat),1/xi(Math.min(Math.abs(i.center.lat),60)),t.bw(a,7,3,0,1)),s=360/pi({worldSize:i.worldSize,center:{lat:i.center.lat}});return new t.W(i.center.lng-o.x*s*r,t.al(i.center.lat+o.y*s,-t.am,t.am))}function wi(e){const t=.5*e,i=Math.sin(t),o=Math.cos(t);return Math.log(i+o)-Math.log(o-i)}function Ti(e,i,o,a){const r=e.lat+o*a;if(Math.abs(o)>1){const s=(Math.sign(e.lat+o)!==Math.sign(e.lat)?-Math.abs(e.lat):Math.abs(e.lat))*Math.PI/180,n=Math.abs(e.lat+o)*Math.PI/180,l=wi(s+a*(n-s)),c=wi(s),h=wi(n);return new t.W(e.lng+i*((l-c)/(h-c)),r)}return new t.W(e.lng+i*a,r)}class Pi{constructor(e){this._cachePrevious=new Map,this._cache=new Map,this._hadAnyChanges=!1,this._boundingVolumeFactory=e;}swapBuffers(){if(!this._hadAnyChanges)return;const e=this._cachePrevious;this._cachePrevious=this._cache,this._cache=e,this._cache.clear(),this._hadAnyChanges=!1;}getTileBoundingVolume(e,t,i,o){const a=`${e.z}_${e.x}_${e.y}_${(null==o?void 0:o.terrain)?"t":""}`,r=this._cache.get(a);if(r)return r;const s=this._cachePrevious.get(a);if(s)return this._cache.set(a,s),s;const n=this._boundingVolumeFactory(e,t,i,o);return this._cache.set(a,n),this._hadAnyChanges=!0,n}}class Ci{constructor(e,t,i,o){this.min=i,this.max=o,this.points=e,this.planes=t;}static fromAabb(e,t){const i=[];for(let o=0;o<8;o++)i.push([1&~o?e[0]:t[0],1==(o>>1&1)?t[1]:e[1],1==(o>>2&1)?t[2]:e[2]]);return new Ci(i,[[-1,0,0,t[0]],[1,0,0,-e[0]],[0,-1,0,t[1]],[0,1,0,-e[1]],[0,0,-1,t[2]],[0,0,1,-e[2]]],e,t)}static fromCenterSizeAngles(e,i,o){const a=t.bA([],o[0],o[1],o[2]),r=t.bB([],[i[0],0,0],a),s=t.bB([],[0,i[1],0],a),n=t.bB([],[0,0,i[2]],a),l=[...e],c=[...e];for(let t=0;t<8;t++)for(let i=0;i<3;i++){const o=e[i]+r[i]*(1&~t?-1:1)+s[i]*(1==(t>>1&1)?1:-1)+n[i]*(1==(t>>2&1)?1:-1);l[i]=Math.min(l[i],o),c[i]=Math.max(c[i],o);}const h=[];for(let i=0;i<8;i++){const o=[...e];t.a_(o,o,t.aZ([],r,1&~i?-1:1)),t.a_(o,o,t.aZ([],s,1==(i>>1&1)?1:-1)),t.a_(o,o,t.aZ([],n,1==(i>>2&1)?1:-1)),h.push(o);}return new Ci(h,[[...r,-t.b3(r,h[0])],[...s,-t.b3(s,h[0])],[...n,-t.b3(n,h[0])],[-r[0],-r[1],-r[2],-t.b3(r,h[7])],[-s[0],-s[1],-s[2],-t.b3(s,h[7])],[-n[0],-n[1],-n[2],-t.b3(n,h[7])]],l,c)}intersectsFrustum(e){let t=!0;const i=this.points.length,o=this.planes.length,a=e.planes.length,r=e.points.length;for(let o=0;o=0&&r++;}if(0===r)return 0;r=0&&o++;}if(0===o)return 0}return 1}intersectsPlane(e){const t=this.points.length;let i=0;for(let o=0;o=0&&i++;}return i===t?2:0===i?0:1}}function Mi(e,t,i){const o=e-t;return o<0?-o:Math.max(0,o-i)}function Ii(e,t,i,o,a){const r=e-i;let s;return s=r<0?Math.min(-r,1+r-a):r>a?Math.min(Math.max(r-a,0),1-r):0,Math.max(s,Mi(t,o,a))}class Ei{constructor(){this._boundingVolumeCache=new Pi(this._computeTileBoundingVolume);}prepareNextFrame(){this._boundingVolumeCache.swapBuffers();}distanceToTile2d(e,t,i,o){const a=1<4}allowWorldCopies(){return !1}getTileBoundingVolume(e,t,i,o){return this._boundingVolumeCache.getTileBoundingVolume(e,t,i,o)}_computeTileBoundingVolume(e,i,o,a){var r,s;let n=0,l=0;if(null==a?void 0:a.terrain){const c=new t.a3(e.z,i,e.z,e.x,e.y),h=a.terrain.getMinMaxElevation(c);n=null!==(r=h.minElevation)&&void 0!==r?r:Math.min(0,o),l=null!==(s=h.maxElevation)&&void 0!==s?s:Math.max(0,o);}if(n/=t.bD,l/=t.bD,n+=1,l+=1,e.z<=0)return Ci.fromAabb([-l,-l,-l],[l,l,l]);if(1===e.z)return Ci.fromAabb([0===e.x?-l:0,0===e.y?0:-l,-l],[0===e.x?0:l,0===e.y?l:0,l]);{const i=[mi(0,0,e.x,e.y,e.z),mi(t.a6,0,e.x,e.y,e.z),mi(t.a6,t.a6,e.x,e.y,e.z),mi(0,t.a6,e.x,e.y,e.z)],o=[];for(const e of i)o.push(t.aZ([],e,l));if(l!==n)for(const e of i)o.push(t.aZ([],e,n));0===e.y&&o.push([0,1,0]),e.y===(1<=(1<{const o=t.al(e.lat,-t.am,t.am),a=t.al(+i,this.minZoom+bi(0,o),this.maxZoom);return {center:new t.W(e.lng,o),zoom:a}},this.applyConstrain=(e,t)=>this._helper.applyConstrain(e,t),this._helper=new Wt({calcMatrices:()=>this._calcMatrices(),defaultConstrain:(e,t)=>this.defaultConstrain(e,t)},e),this._coveringTilesDetailsProvider=new Ei;}clone(){const e=new zi;return e.apply(this,!1),e}apply(e,t,i){this._globeLatitudeErrorCorrectionRadians=i||0,this._helper.apply(e,t);}get projectionMatrix(){return this._projectionMatrix}get modelViewProjectionMatrix(){return this._globeViewProjMatrixNoCorrection}get inverseProjectionMatrix(){return this._globeProjMatrixInverted}get cameraPosition(){const e=t.bz();return e[0]=this._cameraPosition[0],e[1]=this._cameraPosition[1],e[2]=this._cameraPosition[2],e}get cameraToCenterDistance(){return this._helper.cameraToCenterDistance}getProjectionData(e){const{overscaledTileID:t,applyGlobeMatrix:i}=e,o=this._helper.getMercatorTileCoordinates(t);return {mainMatrix:this._globeViewProjMatrix32f,tileMercatorCoords:o,clippingPlane:this._cachedClippingPlane,projectionTransition:i?1:0,fallbackMatrix:this._globeViewProjMatrix32f}}_computeClippingPlane(e){const i=this.pitchInRadians,o=this.cameraToCenterDistance/e,a=Math.sin(i)*o,r=Math.cos(i)*o+1,s=1/Math.sqrt(a*a+r*r)*1;let n=-a,l=r;const c=Math.sqrt(n*n+l*l);n/=c,l/=c;const h=[0,n,l];t.bF(h,h,[0,0,0],-this.bearingInRadians),t.bG(h,h,[0,0,0],-1*this.center.lat*Math.PI/180),t.bH(h,h,[0,0,0],this.center.lng*Math.PI/180);const u=1/t.b5(h);return t.aZ(h,h,u),[...h,-s*u]}isLocationOccluded(e){return !this.isSurfacePointVisible(fi(e))}transformLightDirection(e){const i=this._helper._center.lng*Math.PI/180,o=this._helper._center.lat*Math.PI/180,a=Math.cos(o),r=[Math.sin(i)*a,Math.sin(o),Math.cos(i)*a],s=[r[2],0,-r[0]],n=[0,0,0];t.b2(n,s,r),t.b1(s,s),t.b1(n,n);const l=[0,0,0];return t.b1(l,[s[0]*e[0]+n[0]*e[1]+r[0]*e[2],s[1]*e[0]+n[1]*e[1]+r[1]*e[2],s[2]*e[0]+n[2]*e[1]+r[2]*e[2]]),l}getPixelScale(){return 1/Math.cos(this._helper._center.lat*Math.PI/180)}getCircleRadiusCorrection(){return Math.cos(this._helper._center.lat*Math.PI/180)}getPitchedTextCorrection(e,i,o){const a=function(e,i,o){const a=1/(1<r&&(r=i),on&&(n=o);}const h=[c.lng+s,c.lat+l,c.lng+r,c.lat+n];return this.isSurfacePointOnScreen([0,1,0])&&(h[3]=90,h[0]=-180,h[2]=180),this.isSurfacePointOnScreen([0,-1,0])&&(h[1]=-90,h[0]=-180,h[2]=180),new G(h)}calculateCenterFromCameraLngLatAlt(e,t,i,o){return this._helper.calculateCenterFromCameraLngLatAlt(e,t,i,o)}setLocationAtPoint(e,i){const o=fi(this.unprojectScreenPoint(i)),a=fi(e),r=t.bz();t.bK(r);const s=t.bz();t.bH(s,o,r,-this.center.lng*Math.PI/180),t.bG(s,s,r,this.center.lat*Math.PI/180);const n=a[0]*a[0]+a[2]*a[2],l=s[0]*s[0];if(n=-g&&p<=g,x=f>=-g&&f<=g;let b,y;if(v&&x){const e=this.center.lng*Math.PI/180,i=this.center.lat*Math.PI/180;t.bM(u,e)+t.bM(p,i)=0}isSurfacePointOnScreen(e){if(!this.isSurfacePointVisible(e))return !1;const i=t.bE();return t.aE(i,[...e,1],this._globeViewProjMatrixNoCorrection),i[0]/=i[3],i[1]/=i[3],i[2]/=i[3],i[0]>-1&&i[0]<1&&i[1]>-1&&i[1]<1&&i[2]>-1&&i[2]<1}rayPlanetIntersection(e,i){const o=t.b3(e,i),a=t.bz(),r=t.bz();t.aZ(r,i,o),t.b0(a,e,r);const s=1-t.b3(a,a);if(s<0)return null;const n=t.b3(e,e)-1,l=-o+(o<0?1:-1)*Math.sqrt(s),c=n/l,h=l;return {tMin:Math.min(c,h),tMax:Math.max(c,h)}}unprojectScreenPoint(e){const i=this._cameraPosition,o=this.getRayDirectionFromPixel(e),a=this.rayPlanetIntersection(i,o);if(a){const e=t.bz();t.a_(e,i,[o[0]*a.tMin,o[1]*a.tMin,o[2]*a.tMin]);const r=t.bz();return t.b1(r,e),vi(r)}const r=this._cachedClippingPlane,s=r[0]*o[0]+r[1]*o[1]+r[2]*o[2],n=-t.b9(r,i)/s,l=t.bz();if(n>0)t.a_(l,i,[o[0]*n,o[1]*n,o[2]*n]);else {const e=t.bz();t.a_(e,i,[2*o[0],2*o[1],2*o[2]]);const a=t.b9(this._cachedClippingPlane,e);t.b0(l,e,[this._cachedClippingPlane[0]*a,this._cachedClippingPlane[1]*a,this._cachedClippingPlane[2]*a]);}const c=function(e){const i=t.bz();return i[0]=e[0]*-e[3],i[1]=e[1]*-e[3],i[2]=e[2]*-e[3],{center:i,radius:Math.sqrt(1-e[3]*e[3])}}(r);return vi(function(e,i,o){const a=t.bz();t.b0(a,o,e);const r=t.bz();return t.bx(r,e,a,i/t.b7(a)),r}(c.center,c.radius,l))}getMatrixForModel(e,i){const o=t.W.convert(e),a=1/t.bD,r=t.bi();return t.bI(r,r,o.lng/180*Math.PI),t.bf(r,r,-o.lat/180*Math.PI),t.Q(r,r,[0,0,1+i/t.bD]),t.bf(r,r,.5*Math.PI),t.S(r,r,[a,a,a]),r}getProjectionDataForCustomLayer(e=!0){const i=this.getProjectionData({overscaledTileID:new t.a3(0,0,0,0,0),applyGlobeMatrix:e});return i.tileMercatorCoords=[0,0,1,1],i}getFastPathSimpleProjectionMatrix(e){}}class Ri{get pixelsToClipSpaceMatrix(){return this._helper.pixelsToClipSpaceMatrix}get clipSpaceToPixelsMatrix(){return this._helper.clipSpaceToPixelsMatrix}get pixelsToGLUnits(){return this._helper.pixelsToGLUnits}get centerOffset(){return this._helper.centerOffset}get size(){return this._helper.size}get rotationMatrix(){return this._helper.rotationMatrix}get centerPoint(){return this._helper.centerPoint}get pixelsPerMeter(){return this._helper.pixelsPerMeter}setMinZoom(e){this._helper.setMinZoom(e);}setMaxZoom(e){this._helper.setMaxZoom(e);}setMinPitch(e){this._helper.setMinPitch(e);}setMaxPitch(e){this._helper.setMaxPitch(e);}setRenderWorldCopies(e){this._helper.setRenderWorldCopies(e);}setBearing(e){this._helper.setBearing(e);}setPitch(e){this._helper.setPitch(e);}setRoll(e){this._helper.setRoll(e);}setFov(e){this._helper.setFov(e);}setZoom(e){this._helper.setZoom(e);}setCenter(e){this._helper.setCenter(e);}setElevation(e){this._helper.setElevation(e);}setMinElevationForCurrentTile(e){this._helper.setMinElevationForCurrentTile(e);}setPadding(e){this._helper.setPadding(e);}interpolatePadding(e,t,i){this._helper.interpolatePadding(e,t,i);}isPaddingEqual(e){return this._helper.isPaddingEqual(e)}resize(e,t,i=!0){this._helper.resize(e,t,i);}getMaxBounds(){return this._helper.getMaxBounds()}setMaxBounds(e){this._helper.setMaxBounds(e);}setConstrainOverride(e){this._helper.setConstrainOverride(e);}overrideNearFarZ(e,t){this._helper.overrideNearFarZ(e,t);}clearNearFarZOverride(){this._helper.clearNearFarZOverride();}getCameraQueryGeometry(e){return this._helper.getCameraQueryGeometry(this.getCameraPoint(),e)}get tileSize(){return this._helper.tileSize}get tileZoom(){return this._helper.tileZoom}get scale(){return this._helper.scale}get worldSize(){return this._helper.worldSize}get width(){return this._helper.width}get height(){return this._helper.height}get lngRange(){return this._helper.lngRange}get latRange(){return this._helper.latRange}get minZoom(){return this._helper.minZoom}get maxZoom(){return this._helper.maxZoom}get zoom(){return this._helper.zoom}get center(){return this._helper.center}get minPitch(){return this._helper.minPitch}get maxPitch(){return this._helper.maxPitch}get pitch(){return this._helper.pitch}get pitchInRadians(){return this._helper.pitchInRadians}get roll(){return this._helper.roll}get rollInRadians(){return this._helper.rollInRadians}get bearing(){return this._helper.bearing}get bearingInRadians(){return this._helper.bearingInRadians}get fov(){return this._helper.fov}get fovInRadians(){return this._helper.fovInRadians}get elevation(){return this._helper.elevation}get minElevationForCurrentTile(){return this._helper.minElevationForCurrentTile}get padding(){return this._helper.padding}get unmodified(){return this._helper.unmodified}get renderWorldCopies(){return this._helper.renderWorldCopies}get cameraToCenterDistance(){return this._helper.cameraToCenterDistance}get constrainOverride(){return this._helper.constrainOverride}get nearZ(){return this._helper.nearZ}get farZ(){return this._helper.farZ}get autoCalculateNearFarZ(){return this._helper.autoCalculateNearFarZ}get isGlobeRendering(){return this._globeness>0}setTransitionState(e,t){this._globeness=e,this._globeLatitudeErrorCorrectionRadians=t,this._calcMatrices(),this._verticalPerspectiveTransform.getCoveringTilesDetailsProvider().prepareNextFrame(),this._mercatorTransform.getCoveringTilesDetailsProvider().prepareNextFrame();}get currentTransform(){return this.isGlobeRendering?this._verticalPerspectiveTransform:this._mercatorTransform}constructor(e){this._globeLatitudeErrorCorrectionRadians=0,this._globeness=1,this.defaultConstrain=(e,t)=>this.currentTransform.defaultConstrain(e,t),this.applyConstrain=(e,t)=>this._helper.applyConstrain(e,t),this._helper=new Wt({calcMatrices:()=>this._calcMatrices(),defaultConstrain:(e,t)=>this.defaultConstrain(e,t)},e),this._globeness=1,this._mercatorTransform=new Xt,this._verticalPerspectiveTransform=new zi;}clone(){const e=new Ri;return e._globeness=this._globeness,e._globeLatitudeErrorCorrectionRadians=this._globeLatitudeErrorCorrectionRadians,e.apply(this,!1),e}apply(e,t){this._helper.apply(e,t),this._mercatorTransform.apply(this,!1),this._verticalPerspectiveTransform.apply(this,!1,this._globeLatitudeErrorCorrectionRadians);}get projectionMatrix(){return this.currentTransform.projectionMatrix}get modelViewProjectionMatrix(){return this.currentTransform.modelViewProjectionMatrix}get inverseProjectionMatrix(){return this.currentTransform.inverseProjectionMatrix}get cameraPosition(){return this.currentTransform.cameraPosition}getProjectionData(e){const t=this._mercatorTransform.getProjectionData(e),i=this._verticalPerspectiveTransform.getProjectionData(e);return {mainMatrix:this.isGlobeRendering?i.mainMatrix:t.mainMatrix,clippingPlane:i.clippingPlane,tileMercatorCoords:i.tileMercatorCoords,projectionTransition:e.applyGlobeMatrix?this._globeness:0,fallbackMatrix:t.fallbackMatrix}}isLocationOccluded(e){return this.currentTransform.isLocationOccluded(e)}transformLightDirection(e){return this.currentTransform.transformLightDirection(e)}getPixelScale(){return t.bt(this._mercatorTransform.getPixelScale(),this._verticalPerspectiveTransform.getPixelScale(),this._globeness)}getCircleRadiusCorrection(){return t.bt(this._mercatorTransform.getCircleRadiusCorrection(),this._verticalPerspectiveTransform.getCircleRadiusCorrection(),this._globeness)}getPitchedTextCorrection(e,i,o){const a=this._mercatorTransform.getPitchedTextCorrection(e,i,o),r=this._verticalPerspectiveTransform.getPitchedTextCorrection(e,i,o);return t.bt(a,r,this._globeness)}projectTileCoordinates(e,t,i,o){return this.currentTransform.projectTileCoordinates(e,t,i,o)}_calcMatrices(){this._helper._width&&this._helper._height&&(this._verticalPerspectiveTransform.apply(this,!1,this._globeLatitudeErrorCorrectionRadians),this._helper._nearZ=this._verticalPerspectiveTransform.nearZ,this._helper._farZ=this._verticalPerspectiveTransform.farZ,this._mercatorTransform.apply(this,!0,this.isGlobeRendering),this._helper._nearZ=this._mercatorTransform.nearZ,this._helper._farZ=this._mercatorTransform.farZ);}calculateFogMatrix(e){return this.currentTransform.calculateFogMatrix(e)}getVisibleUnwrappedCoordinates(e){return this.currentTransform.getVisibleUnwrappedCoordinates(e)}getCameraFrustum(){return this.currentTransform.getCameraFrustum()}getClippingPlane(){return this.currentTransform.getClippingPlane()}getCoveringTilesDetailsProvider(){return this.currentTransform.getCoveringTilesDetailsProvider()}recalculateZoomAndCenter(e){this._mercatorTransform.recalculateZoomAndCenter(e),this._verticalPerspectiveTransform.recalculateZoomAndCenter(e);}maxPitchScaleFactor(){return this._mercatorTransform.maxPitchScaleFactor()}getCameraPoint(){return this._helper.getCameraPoint()}getCameraAltitude(){return this._helper.getCameraAltitude()}getCameraLngLat(){return this._helper.getCameraLngLat()}lngLatToCameraDepth(e,t){return this.currentTransform.lngLatToCameraDepth(e,t)}populateCache(e){this._mercatorTransform.populateCache(e),this._verticalPerspectiveTransform.populateCache(e);}getBounds(){return this.currentTransform.getBounds()}calculateCenterFromCameraLngLatAlt(e,t,i,o){return this._helper.calculateCenterFromCameraLngLatAlt(e,t,i,o)}setLocationAtPoint(e,t){if(!this.isGlobeRendering)return this._mercatorTransform.setLocationAtPoint(e,t),void this.apply(this._mercatorTransform,!1);this._verticalPerspectiveTransform.setLocationAtPoint(e,t),this.apply(this._verticalPerspectiveTransform,!1);}locationToScreenPoint(e,t){return this.currentTransform.locationToScreenPoint(e,t)}screenPointToMercatorCoordinate(e,t){return this.currentTransform.screenPointToMercatorCoordinate(e,t)}screenPointToLocation(e,t){return this.currentTransform.screenPointToLocation(e,t)}isPointOnMapSurface(e,t){return this.currentTransform.isPointOnMapSurface(e,t)}getRayDirectionFromPixel(e){return this._verticalPerspectiveTransform.getRayDirectionFromPixel(e)}getMatrixForModel(e,t){return this.currentTransform.getMatrixForModel(e,t)}getProjectionDataForCustomLayer(e=!0){const t=this._mercatorTransform.getProjectionDataForCustomLayer(e);if(!this.isGlobeRendering)return t;const i=this._verticalPerspectiveTransform.getProjectionDataForCustomLayer(e);return i.fallbackMatrix=t.mainMatrix,i}getFastPathSimpleProjectionMatrix(e){return this.currentTransform.getFastPathSimpleProjectionMatrix(e)}}class Di{get useGlobeControls(){return !0}handlePanInertia(e,i){const o=yi(e,i);return Math.abs(o.lng-i.center.lng)>180&&(o.lng=i.center.lng+179.5*Math.sign(o.lng-i.center.lng)),{easingCenter:o,easingOffset:new t.P(0,0)}}handleMapControlsRollPitchBearingZoom(e,i){const o=e.around,a=i.screenPointToLocation(o);e.bearingDelta&&i.setBearing(i.bearing+e.bearingDelta),e.pitchDelta&&i.setPitch(i.pitch+e.pitchDelta),e.rollDelta&&i.setRoll(i.roll+e.rollDelta);const r=i.zoom;e.zoomDelta&&i.setZoom(i.zoom+e.zoomDelta);const s=i.zoom-r;if(0===s)return;const n=t.bJ(i.center.lng,a.lng),l=n/(Math.abs(n/180)+1),c=t.bJ(i.center.lat,a.lat),h=i.getRayDirectionFromPixel(o),u=i.cameraPosition,d=-1*t.b3(u,h),_=t.bz();t.a_(_,u,[h[0]*d,h[1]*d,h[2]*d]);const p=t.b5(_)-1,m=Math.exp(.5*-Math.max(p-.3,0)),f=gi(i.worldSize,i.center.lat)/Math.min(i.width,i.height),g=t.bw(f,.9,.5,1,.25),v=(1-t.ao(-s))*Math.min(m,g),x=i.center.lat,b=i.zoom,y=new t.W(i.center.lng+l*v,t.al(i.center.lat+c*v,-t.am,t.am));i.setLocationAtPoint(a,o);const w=i.center,T=t.bw(Math.abs(n),45,85,0,1),P=t.bw(f,.75,.35,0,1),C=Math.pow(Math.max(T,P),.25),M=t.bJ(w.lng,y.lng),I=t.bJ(w.lat,y.lat);i.setCenter(new t.W(w.lng+M*C,w.lat+I*C).wrap()),i.setZoom(b+bi(x,i.center.lat));}handleMapControlsPan(e,t,i){if(!e.panDelta)return;const o=t.center.lat,a=t.zoom;t.setCenter(yi(e.panDelta,t).wrap()),t.setZoom(a+bi(o,t.center.lat));}cameraForBoxAndBearing(e,i,o,a,r){const s=Qt(e,i,o,a,r),n=i.left/r.width*2-1,l=(r.width-i.right)/r.width*2-1,c=i.top/r.height*-2+1,h=(r.height-i.bottom)/r.height*-2+1,u=t.bJ(o.getWest(),o.getEast())<0,d=u?o.getEast():o.getWest(),_=u?o.getWest():o.getEast(),p=Math.max(o.getNorth(),o.getSouth()),m=Math.min(o.getNorth(),o.getSouth()),f=d+.5*t.bJ(d,_),g=p+.5*t.bJ(p,m),v=r.clone();v.setCenter(s.center),v.setBearing(s.bearing),v.setPitch(0),v.setRoll(0),v.setZoom(s.zoom);const x=v.modelViewProjectionMatrix,b=[fi(o.getNorthWest()),fi(o.getNorthEast()),fi(o.getSouthWest()),fi(o.getSouthEast()),fi(new t.W(_,g)),fi(new t.W(d,g)),fi(new t.W(f,p)),fi(new t.W(f,m))],y=fi(s.center);let w=Number.POSITIVE_INFINITY;for(const e of b)n<0&&(w=Di.getLesserNonNegativeNonNull(w,Di.solveVectorScale(e,y,x,"x",n))),l>0&&(w=Di.getLesserNonNegativeNonNull(w,Di.solveVectorScale(e,y,x,"x",l))),c>0&&(w=Di.getLesserNonNegativeNonNull(w,Di.solveVectorScale(e,y,x,"y",c))),h<0&&(w=Di.getLesserNonNegativeNonNull(w,Di.solveVectorScale(e,y,x,"y",h)));if(Number.isFinite(w)&&0!==w)return s.zoom=Math.min(v.zoom+t.ar(w),e.maxZoom),s;Kt();}handleJumpToCenterZoom(e,i){const o=e.center.lat,a=e.applyConstrain(i.center?t.W.convert(i.center):e.center,e.zoom).center;e.setCenter(a.wrap());const r=void 0!==i.zoom?+i.zoom:e.zoom+bi(o,a.lat);e.zoom!==r&&e.setZoom(r);}handleEaseTo(e,i){const o=e.zoom,a=e.center,r=e.padding,s={roll:e.roll,pitch:e.pitch,bearing:e.bearing},n={roll:void 0===i.roll?e.roll:i.roll,pitch:void 0===i.pitch?e.pitch:i.pitch,bearing:void 0===i.bearing?e.bearing:i.bearing},l=void 0!==i.zoom,c=!e.isPaddingEqual(i.padding);let h=!1;const u=i.center?t.W.convert(i.center):a,d=e.applyConstrain(u,o).center;Gt(e,d);const _=e.clone();_.setCenter(d),_.setZoom(l?+i.zoom:o+bi(a.lat,u.lat)),_.setBearing(i.bearing);const p=new t.P(t.al(e.centerPoint.x+i.offsetAsPoint.x,0,e.width),t.al(e.centerPoint.y+i.offsetAsPoint.y,0,e.height));_.setLocationAtPoint(d,p);const m=(i.offset&&i.offsetAsPoint.mag())>0?_.center:d,f=l?+i.zoom:o+bi(a.lat,m.lat),g=o+bi(a.lat,0),v=f+bi(m.lat,0),x=t.bJ(a.lng,m.lng),b=t.bJ(a.lat,m.lat),y=t.ao(v-g);return h=f!==o,{easeFunc:o=>{if(t.bn(s,n)||Yt({startEulerAngles:s,endEulerAngles:n,tr:e,k:o,useSlerp:s.roll!=n.roll}),c&&e.interpolatePadding(r,i.padding,o),i.around)t.w("Easing around a point is not supported under globe projection."),e.setLocationAtPoint(i.around,i.aroundPoint);else {const t=v>g?Math.min(2,y):Math.max(.5,y),i=Math.pow(t,1-o),r=Ti(a,x,b,o*i);e.setCenter(r.wrap());}if(h){const i=t.H.number(g,v,o)+bi(0,e.center.lat);e.setZoom(i);}},isZooming:h,elevationCenter:m}}handleFlyTo(e,i){const o=void 0!==i.zoom,a=e.center,r=e.zoom,s=e.padding,n=!e.isPaddingEqual(i.padding),l=e.applyConstrain(t.W.convert(i.center||i.locationAtOffset),r).center,c=o?+i.zoom:e.zoom+bi(e.center.lat,l.lat),h=e.clone();h.setCenter(l),h.setZoom(c),h.setBearing(i.bearing);const u=new t.P(t.al(e.centerPoint.x+i.offsetAsPoint.x,0,e.width),t.al(e.centerPoint.y+i.offsetAsPoint.y,0,e.height));h.setLocationAtPoint(l,u);const d=h.center;Gt(e,d);const _=function(e,i,o){const a=fi(i),r=fi(o),s=t.b3(a,r),n=Math.acos(s),l=pi(e);return n/(2*Math.PI)*l}(e,a,d),p=r+bi(a.lat,0),m=c+bi(d.lat,0),f=t.ao(m-p);let g;if("number"==typeof i.minZoom){const o=+i.minZoom+bi(d.lat,0),a=Math.min(o,p,m)+bi(0,d.lat),r=e.applyConstrain(d,a).zoom+bi(d.lat,0);g=t.ao(r-p);}const v=t.bJ(a.lng,d.lng),x=t.bJ(a.lat,d.lat);return {easeFunc:(o,r,l,h)=>{const u=Ti(a,v,x,l);n&&e.interpolatePadding(s,i.padding,o);const _=1===o?d:u;e.setCenter(_.wrap());const m=p+t.ar(r);e.setZoom(1===o?c:m+bi(0,_.lat));},scaleOfZoom:f,targetCenter:d,scaleOfMinZoom:g,pixelPathLength:_}}static solveVectorScale(e,t,i,o,a){const r="x"===o?[i[0],i[4],i[8],i[12]]:[i[1],i[5],i[9],i[13]],s=[i[3],i[7],i[11],i[15]],n=e[0]*r[0]+e[1]*r[1]+e[2]*r[2],l=e[0]*s[0]+e[1]*s[1]+e[2]*s[2],c=t[0]*r[0]+t[1]*r[1]+t[2]*r[2],h=t[0]*s[0]+t[1]*s[1]+t[2]*s[2];return c+a*l===n+a*h||s[3]*(n-c)+r[3]*(h-l)+n*h==c*l?null:(c+r[3]-a*h-a*s[3])/(c-n-a*h+a*l)}static getLesserNonNegativeNonNull(e,t){return null!==t&&t>=0&&tt.C(e,null==i?void 0:i.filter((e=>"source.canvas"!==e.identifier))),Fi=t.bN();class ki extends t.E{constructor(e,i={}){var o,a;super(),this._rtlPluginLoaded=()=>{for(const e in this.tileManagers){const t=this.tileManagers[e].getSource().type;"vector"!==t&&"geojson"!==t||this.tileManagers[e].reload();}},this.map=e,this.dispatcher=new k(F(),e._getMapId()),this.dispatcher.registerMessageHandler("GG",((e,t)=>this.getGlyphs(e,t))),this.dispatcher.registerMessageHandler("GI",((e,t)=>this.getImages(e,t))),this.dispatcher.registerMessageHandler("GDA",((e,t)=>this.getDashes(e,t))),this.imageManager=new g,this.imageManager.setEventedParent(this);const r=(null===(o=e._container)||void 0===o?void 0:o.lang)||"undefined"!=typeof document&&(null===(a=document.documentElement)||void 0===a?void 0:a.lang)||void 0;this.glyphManager=new T(e._requestManager,i.localIdeographFontFamily,r),this.lineAtlas=new S(256,512),this.crossTileSymbolIndex=new At,this._setInitialValues(),this._resetUpdates(),this.dispatcher.broadcast("SR",t.bO()),ce().on(se,this._rtlPluginLoaded),this.on("data",(e=>{if("source"!==e.dataType||"metadata"!==e.sourceDataType)return;const t=this.tileManagers[e.sourceId];if(!t)return;const i=t.getSource();if(null==i?void 0:i.vectorLayerIds)for(const e in this._layers){const t=this._layers[e];t.source===i.id&&this._validateLayer(t);}}));}_setInitialValues(){var e;this._spritesImagesIds={},this._layers={},this._order=[],this.tileManagers={},this.zoomHistory=new t.bP,this._availableImages=[],this._globalState={},this._serializedLayers={},this.stylesheet=null,this.light=null,this.sky=null,this.projection&&(this.projection.destroy(),delete this.projection),this._loaded=!1,this._changed=!1,this._updatedLayers={},this._updatedSources={},this._changedImages={},this._glyphsDidChange=!1,this._updatedPaintProps={},this._layerOrderChanged=!1,this.crossTileSymbolIndex=new((null===(e=this.crossTileSymbolIndex)||void 0===e?void 0:e.constructor)||Object),this.pauseablePlacement=void 0,this.placement=void 0,this.z=0;}setGlobalStateProperty(e,i){var o,a,r;this._checkLoaded();const s=null===i?null!==(r=null===(a=null===(o=this.stylesheet.state)||void 0===o?void 0:o[e])||void 0===a?void 0:a.default)&&void 0!==r?r:null:i;if(t.bQ(s,this._globalState[e]))return this;this._globalState[e]=s,this._applyGlobalStateChanges([e]);}getGlobalState(){return this._globalState}setGlobalState(e){this._checkLoaded();const i=[];for(const o in e)!t.bQ(this._globalState[o],e[o].default)&&(i.push(o),this._globalState[o]=e[o].default);this._applyGlobalStateChanges(i);}_applyGlobalStateChanges(e){if(0===e.length)return;const t=new Set,i={};for(const o of e){i[o]=this._globalState[o];for(const e in this._layers){const i=this._layers[e],a=i.getLayoutAffectingGlobalStateRefs(),r=i.getPaintAffectingGlobalStateRefs(),s=i.getVisibilityAffectingGlobalStateRefs();if(a.has(o)&&t.add(i.source),r.has(o))for(const{name:e,value:t}of r.get(o))this._updatePaintProperty(i,e,t);(null==s?void 0:s.has(o))&&(i.recalculateVisibility(),this._updateLayer(i));}}this.dispatcher.broadcast("UGS",i);for(const e in this.tileManagers)t.has(e)&&(this._reloadSource(e),this._changed=!0);}loadURL(e){return t._(this,arguments,void 0,(function*(e,i={},o){this.fire(new t.n("dataloading",{dataType:"style"})),i.validate="boolean"!=typeof i.validate||i.validate,this._loadStyleRequest=new AbortController;const a=this._loadStyleRequest;try{const r=yield this.map._requestManager.transformRequest(e,"Style");t.bR(a.signal);const s=yield t.k(r,a);this._loadStyleRequest===a&&(this._loadStyleRequest=null),this._load(s.data,i,o);}catch(e){this._loadStyleRequest===a&&(this._loadStyleRequest=null),e&&!a.signal.aborted&&this.fire(new t.l(t.d(e)));}}))}loadJSON(e,i={},o){this.fire(new t.n("dataloading",{dataType:"style"})),this._frameRequest=new AbortController,n.frameAsync(this._frameRequest,this.map._ownerWindow).then((()=>{this._frameRequest=null,i.validate=!1!==i.validate,this._load(e,i,o);})).catch((()=>{}));}loadEmpty(){this.fire(new t.n("dataloading",{dataType:"style"})),this._load(Fi,{validate:!1});}_load(e,i,o){var a,r;let s=i.transformStyle?i.transformStyle(o,e):e;if(!i.validate||!Li(this,t.F(s))){s=Object.assign({},s),this._loaded=!0,this.stylesheet=s;for(const e in s.sources)this.addSource(e,s.sources[e],{validate:!1});s.sprite?this._loadSprite(s.sprite):this.imageManager.setLoaded(!0),this.glyphManager.setURL(s.glyphs),this._createLayers(),this.light=new M(this.stylesheet.light),this._setProjectionInternal((null===(a=this.stylesheet.projection)||void 0===a?void 0:a.type)||"mercator"),this.sky=new E(this.stylesheet.sky),this.map.setTerrain(null!==(r=this.stylesheet.terrain)&&void 0!==r?r:null),this.fire(new t.n("data",{dataType:"style"})),this.fire(new t.n("style.load"));}}_createLayers(){var e,i,o;const a=t.bS(this.stylesheet.layers);this.setGlobalState(null!==(e=this.stylesheet.state)&&void 0!==e?e:null),this.dispatcher.broadcast("SL",a),this._order=a.map((e=>e.id)),this._layers={},this._serializedLayers=null;for(const e of a){const a=t.bT(e,this._globalState);if(a.setEventedParent(this,{layer:{id:e.id}}),this._layers[e.id]=a,t.bU(a)&&this.tileManagers[a.source]){const t=null!==(o=null===(i=e.paint)||void 0===i?void 0:i["raster-fade-duration"])&&void 0!==o?o:a.paint.get("raster-fade-duration");this.tileManagers[a.source].setRasterFadeDuration(t);}}}_loadSprite(e,i=!1,o=void 0){this.imageManager.setLoaded(!1);const a=new AbortController;let r;this._spriteRequest=a,function(e,i,o,a){return t._(this,void 0,void 0,(function*(){const r=p(e),s=o>1?"@2x":"",l={},c={};for(const{id:e,url:o}of r){const r=yield i.transformRequest(m(o,s,".json"),"SpriteJSON");l[e]=t.k(r,a);const n=yield i.transformRequest(m(o,s,".png"),"SpriteImage");c[e]=u.getImage(n,a);}return yield Promise.all([...Object.values(l),...Object.values(c)]),function(e,i){return t._(this,void 0,void 0,(function*(){const t={};for(const o in e){t[o]={};const a=n.getImageCanvasContext((yield i[o]).data),r=(yield e[o]).data;for(const e in r){const{width:i,height:s,x:n,y:l,sdf:c,pixelRatio:h,stretchX:u,stretchY:d,content:_,textFitWidth:p,textFitHeight:m}=r[e];t[o][e]={data:null,pixelRatio:h,sdf:c,stretchX:u,stretchY:d,content:_,textFitWidth:p,textFitHeight:m,spriteData:{width:i,height:s,x:n,y:l,context:a}};}}return t}))}(l,c)}))}(e,this.map._requestManager,this.map.getPixelRatio(),this._spriteRequest).then((e=>{if(this._spriteRequest=null,e)for(const t in e){this._spritesImagesIds[t]=[];const o=this._spritesImagesIds[t]?this._spritesImagesIds[t].filter((t=>!(t in e))):[];for(const e of o)this.imageManager.removeImage(e),this._changedImages[e]=!0;for(const o in e[t]){const a="default"===t?o:`${t}:${o}`;this._spritesImagesIds[t].push(a),a in this.imageManager.images?this.imageManager.updateImage(a,e[t][o],!1):this.imageManager.addImage(a,e[t][o]),i&&(this._changedImages[a]=!0);}}})).catch((e=>{this._spriteRequest=null,r=e,a.signal.aborted||this.fire(new t.l(r));})).finally((()=>{this.imageManager.setLoaded(!0),this._availableImages=this.imageManager.listImages(),i&&(this._changed=!0),this.dispatcher.broadcast("SI",this._availableImages),this.fire(new t.n("data",{dataType:"style"})),o&&o(r);}));}_unloadSprite(){for(const e of Object.values(this._spritesImagesIds).flat())this.imageManager.removeImage(e),this._changedImages[e]=!0;this._spritesImagesIds={},this._availableImages=this.imageManager.listImages(),this._changed=!0,this.dispatcher.broadcast("SI",this._availableImages),this.fire(new t.n("data",{dataType:"style"}));}_validateLayer(e){const i=this.tileManagers[e.source];if(!i)return;const o=e.sourceLayer;if(!o)return;const a=i.getSource();("geojson"===a.type||a.vectorLayerIds&&!a.vectorLayerIds.includes(o))&&this.fire(new t.l(new Error(`Source layer "${o}" does not exist on source "${a.id}" as specified by style layer "${e.id}".`)));}loaded(){if(!this._loaded)return !1;if(Object.keys(this._updatedSources).length)return !1;for(const e in this.tileManagers)if(!this.tileManagers[e].loaded())return !1;return this.imageManager.isLoaded()}_serializeByIds(e,i=!1){const o=this._serializedAllLayers();if(!e||0===e.length)return Object.values(i?t.bV(o):o);const a=[];for(const r of e)if(o[r]){const e=i?t.bV(o[r]):o[r];a.push(e);}return a}_serializedAllLayers(){let e=this._serializedLayers;if(e)return e;e=this._serializedLayers={};const t=Object.keys(this._layers);for(const i of t){const t=this._layers[i];"custom"!==t.type&&(e[i]=t.serialize());}return e}hasTransitions(){var e,t,i;if(null===(e=this.light)||void 0===e?void 0:e.hasTransition())return !0;if(null===(t=this.sky)||void 0===t?void 0:t.hasTransition())return !0;if(null===(i=this.projection)||void 0===i?void 0:i.hasTransition())return !0;for(const e in this.tileManagers)if(this.tileManagers[e].hasTransition())return !0;for(const e in this._layers)if(this._layers[e].hasTransition())return !0;return !1}_checkLoaded(){if(!this._loaded)throw new Error("Style is not done loading.")}update(e){if(!this._loaded)return;const i=this._changed;if(i){const t=Object.keys(this._updatedLayers),i=Object.keys(this._removedLayers);(t.length||i.length)&&this._updateWorkerLayers(t,i);for(const e in this._updatedSources){const t=this._updatedSources[e];if("reload"===t)this._reloadSource(e);else {if("clear"!==t)throw new Error(`Invalid action ${t}`);this._clearSource(e);}}this._updateTilesForChangedImages(),this._updateTilesForChangedGlyphs();for(const t in this._updatedPaintProps)this._layers[t].updateTransitions(e);this.light.updateTransitions(e),this.sky.updateTransitions(e),this._resetUpdates();}const o={};for(const e in this.tileManagers){const t=this.tileManagers[e];o[e]=t.used,t.used=!1;}for(const t of this._order){const i=this._layers[t];i.recalculate(e,this._availableImages),!i.isHidden(e.zoom)&&i.source&&(this.tileManagers[i.source].used=!0);}for(const e in o){const i=this.tileManagers[e];!!o[e]!=!!i.used&&i.fire(new t.n("data",{sourceDataType:"visibility",dataType:"source",sourceId:e}));}this.light.recalculate(e),this.sky.recalculate(e),this.projection.recalculate(e),this.z=e.zoom,i&&this.fire(new t.n("data",{dataType:"style"}));}_updateTilesForChangedImages(){const e=Object.keys(this._changedImages);if(e.length){for(const t in this.tileManagers)this.tileManagers[t].reloadTilesForDependencies(["icons","patterns"],e);this._changedImages={};}}_updateTilesForChangedGlyphs(){if(this._glyphsDidChange){for(const e in this.tileManagers)this.tileManagers[e].reloadTilesForDependencies(["glyphs"],[""]);this._glyphsDidChange=!1;}}_updateWorkerLayers(e,t){this.dispatcher.broadcast("UL",{layers:this._serializeByIds(e,!1),removedIds:t});}_resetUpdates(){this._changed=!1,this._updatedLayers={},this._removedLayers={},this._updatedSources={},this._updatedPaintProps={},this._changedImages={},this._glyphsDidChange=!1;}setState(e,i={}){var o;this._checkLoaded();const a=this.serialize();if(e=i.transformStyle?i.transformStyle(a,e):e,(null===(o=i.validate)||void 0===o||o)&&Li(this,t.F(e)))return !1;(e=t.bV(e)).layers=t.bS(e.layers);const r=t.bW(a,e),s=this._getOperationsToPerform(r);if(s.unimplemented.length>0)throw new Error(`Unimplemented: ${s.unimplemented.join(", ")}.`);if(0===s.operations.length)return !1;for(const e of s.operations)e();return this.stylesheet=e,this._serializedLayers=null,this.fire(new t.n("style.load",{style:this})),!0}_getOperationsToPerform(e){const t=[],i=[];for(const o of e)switch(o.command){case "setCenter":case "setZoom":case "setBearing":case "setPitch":case "setRoll":continue;case "addLayer":t.push((()=>this.addLayer.apply(this,o.args)));break;case "removeLayer":t.push((()=>this.removeLayer.apply(this,o.args)));break;case "setPaintProperty":t.push((()=>this.setPaintProperty.apply(this,o.args)));break;case "setLayoutProperty":t.push((()=>this.setLayoutProperty.apply(this,o.args)));break;case "setFilter":t.push((()=>this.setFilter.apply(this,o.args)));break;case "addSource":t.push((()=>this.addSource.apply(this,o.args)));break;case "removeSource":t.push((()=>this.removeSource.apply(this,o.args)));break;case "setLayerZoomRange":t.push((()=>this.setLayerZoomRange.apply(this,o.args)));break;case "setLight":t.push((()=>this.setLight.apply(this,o.args)));break;case "setGeoJSONSourceData":t.push((()=>this.setGeoJSONSourceData.apply(this,o.args)));break;case "setGlyphs":t.push((()=>this.setGlyphs.apply(this,o.args)));break;case "setSprite":t.push((()=>this.setSprite.apply(this,o.args)));break;case "setTerrain":t.push((()=>this.map.setTerrain.apply(this,o.args)));break;case "setSky":t.push((()=>this.setSky.apply(this,o.args)));break;case "setProjection":this.setProjection.apply(this,o.args);break;case "setGlobalState":t.push((()=>this.setGlobalState.apply(this,o.args)));break;case "setTransition":t.push((()=>{}));break;default:i.push(o.command);}return {operations:t,unimplemented:i}}addImage(e,i){if(this.getImage(e))return this.fire(new t.l(new Error(`An image named "${e}" already exists.`)));this.imageManager.addImage(e,i),this._afterImageUpdated(e);}updateImage(e,t){this.imageManager.updateImage(e,t);}getImage(e){return this.imageManager.getImage(e)}removeImage(e){if(!this.getImage(e))return this.fire(new t.l(new Error(`An image named "${e}" does not exist.`)));this.imageManager.removeImage(e),this._afterImageUpdated(e);}_afterImageUpdated(e){this._availableImages=this.imageManager.listImages(),this._changedImages[e]=!0,this._changed=!0,this.dispatcher.broadcast("SI",this._availableImages),this.fire(new t.n("data",{dataType:"style"}));}listImages(){return this._checkLoaded(),this.imageManager.listImages()}addSource(e,i,o={}){var a;if(this._checkLoaded(),void 0!==this.tileManagers[e])throw new Error(`Source "${e}" already exists.`);if(!i.type)throw new Error(`The type property must be defined, but only the following properties were given: ${Object.keys(i).join(", ")}.`);if(["vector","raster","geojson","video","image"].includes(i.type)&&this._validate(t.F.source,`sources.${e}`,i,null,o))return;(null===(a=this.map)||void 0===a?void 0:a._collectResourceTiming)&&(i.collectResourceTiming=!0);const r=this.tileManagers[e]=new Fe(e,i,this.dispatcher);r.style=this,r.setEventedParent(this,(()=>({isSourceLoaded:r.loaded(),source:r.serialize(),sourceId:e}))),r.onAdd(this.map),this._changed=!0;}removeSource(e){if(this._checkLoaded(),void 0===this.tileManagers[e])throw new Error(`There is no source with this ID=${e}`);for(const i in this._layers)if(this._layers[i].source===e)return this.fire(new t.l(new Error(`Source "${e}" cannot be removed while layer "${i}" is using it.`)));const i=this.tileManagers[e];delete this.tileManagers[e],delete this._updatedSources[e],i.fire(new t.n("data",{sourceDataType:"metadata",dataType:"source",sourceId:e})),i.setEventedParent(null),i.onRemove(this.map),this._changed=!0;}setGeoJSONSourceData(e,t){if(this._checkLoaded(),void 0===this.tileManagers[e])throw new Error(`There is no source with this ID=${e}`);const i=this.tileManagers[e].getSource();if("geojson"!==i.type)throw new Error(`geojsonSource.type is ${i.type}, which is !== 'geojson`);i.setData(t),this._changed=!0;}getSource(e){var t;return null===(t=this.tileManagers[e])||void 0===t?void 0:t.getSource()}addLayer(e,i,o={}){this._checkLoaded();const a=e.id;if(this.getLayer(a))return void this.fire(new t.l(new Error(`Layer "${a}" already exists on this map.`)));let r;if("custom"===e.type){if(Li(this,t.bX(e)))return;r=t.bT(e,this._globalState);}else {if("source"in e&&"object"==typeof e.source&&(this.addSource(a,e.source),e=t.bV(e),e=t.e(e,{source:a})),this._validate(t.F.layer,`layers.${a}`,e,{arrayIndex:-1},o))return;r=t.bT(e,this._globalState),this._validateLayer(r),r.setEventedParent(this,{layer:{id:a}});}const s=i?this._order.indexOf(i):this._order.length;if(i&&-1===s)this.fire(new t.l(new Error(`Cannot add layer "${a}" before non-existing layer "${i}".`)));else {if(this._order.splice(s,0,a),this._layerOrderChanged=!0,this._layers[a]=r,this._removedLayers[a]&&r.source&&"custom"!==r.type){const e=this._removedLayers[a];delete this._removedLayers[a],e.type!==r.type?this._updatedSources[r.source]="clear":(this._updatedSources[r.source]="reload",this.tileManagers[r.source].pause());}this._updateLayer(r),r.onAdd&&r.onAdd(this.map);}}moveLayer(e,i){if(this._checkLoaded(),this._changed=!0,!this._layers[e])return void this.fire(new t.l(new Error(`The layer '${e}' does not exist in the map's style and cannot be moved.`)));if(e===i)return;const o=this._order.indexOf(e);this._order.splice(o,1);const a=i?this._order.indexOf(i):this._order.length;i&&-1===a?this.fire(new t.l(new Error(`Cannot move layer "${e}" before non-existing layer "${i}".`))):(this._order.splice(a,0,e),this._layerOrderChanged=!0);}removeLayer(e){this._checkLoaded();const i=this._layers[e];if(!i)return void this.fire(new t.l(new Error(`Cannot remove non-existing layer "${e}".`)));i.setEventedParent(null);const o=this._order.indexOf(e);this._order.splice(o,1),this._layerOrderChanged=!0,this._changed=!0,this._removedLayers[e]=i,delete this._layers[e],this._serializedLayers&&delete this._serializedLayers[e],delete this._updatedLayers[e],delete this._updatedPaintProps[e],i.onRemove&&i.onRemove(this.map);}getLayer(e){return this._layers[e]}getLayersOrder(){return [...this._order]}hasLayer(e){return e in this._layers}setLayerZoomRange(e,i,o){this._checkLoaded();const a=this.getLayer(e);a?a.minzoom===i&&a.maxzoom===o||(null!=i&&(a.minzoom=i),null!=o&&(a.maxzoom=o),this._updateLayer(a)):this.fire(new t.l(new Error(`Cannot set the zoom range of non-existing layer "${e}".`)));}setFilter(e,i,o={}){this._checkLoaded();const a=this.getLayer(e);if(a){if(!t.bQ(a.filter,i))return null==i?(a.setFilter(void 0),void this._updateLayer(a)):void(this._validate(t.F.filter,`layers.${a.id}.filter`,i,null,o)||(a.setFilter(t.bV(i)),this._updateLayer(a)))}else this.fire(new t.l(new Error(`Cannot filter non-existing layer "${e}".`)));}getFilter(e){return t.bV(this.getLayer(e).filter)}setLayoutProperty(e,i,o,a={}){this._checkLoaded();const r=this.getLayer(e);r?t.bQ(r.getLayoutProperty(i),o)||(r.setLayoutProperty(i,o,a),this._updateLayer(r)):this.fire(new t.l(new Error(`Cannot style non-existing layer "${e}".`)));}getLayoutProperty(e,i){const o=this.getLayer(e);if(o)return o.getLayoutProperty(i);this.fire(new t.l(new Error(`Cannot get style of non-existing layer "${e}".`)));}setPaintProperty(e,i,o,a={}){this._checkLoaded();const r=this.getLayer(e);r?t.bQ(r.getPaintProperty(i),o)||this._updatePaintProperty(r,i,o,a):this.fire(new t.l(new Error(`Cannot style non-existing layer "${e}".`)));}_updatePaintProperty(e,i,o,a={}){e.setPaintProperty(i,o,a)&&this._updateLayer(e),t.bU(e)&&"raster-fade-duration"===i&&this.tileManagers[e.source].setRasterFadeDuration(o),this._changed=!0,this._updatedPaintProps[e.id]=!0,this._serializedLayers=null;}getPaintProperty(e,t){return this.getLayer(e).getPaintProperty(t)}setFeatureState(e,i){this._checkLoaded();const o=e.source,a=e.sourceLayer,r=this.tileManagers[o];if(void 0===r)return void this.fire(new t.l(new Error(`The source '${o}' does not exist in the map's style.`)));const s=r.getSource().type;"geojson"===s&&a?this.fire(new t.l(new Error("GeoJSON sources cannot have a sourceLayer parameter."))):"vector"!==s||a?(void 0===e.id&&this.fire(new t.l(new Error("The feature id parameter must be provided."))),r.setFeatureState(a,e.id,i)):this.fire(new t.l(new Error("The sourceLayer parameter must be provided for vector source types.")));}removeFeatureState(e,i){this._checkLoaded();const o=e.source,a=this.tileManagers[o];if(void 0===a)return void this.fire(new t.l(new Error(`The source '${o}' does not exist in the map's style.`)));const r=a.getSource().type,s="vector"===r?e.sourceLayer:void 0;"vector"!==r||s?i&&"string"!=typeof e.id&&"number"!=typeof e.id?this.fire(new t.l(new Error("A feature id is required to remove its specific state property."))):a.removeFeatureState(s,e.id,i):this.fire(new t.l(new Error("The sourceLayer parameter must be provided for vector source types.")));}getFeatureState(e){this._checkLoaded();const i=e.source,o=e.sourceLayer,a=this.tileManagers[i];if(void 0!==a)return "vector"!==a.getSource().type||o?(void 0===e.id&&this.fire(new t.l(new Error("The feature id parameter must be provided."))),a.getFeatureState(o,e.id)):void this.fire(new t.l(new Error("The sourceLayer parameter must be provided for vector source types.")));this.fire(new t.l(new Error(`The source '${i}' does not exist in the map's style.`)));}getTransition(){var e;return t.e({duration:300,delay:0},null===(e=this.stylesheet)||void 0===e?void 0:e.transition)}serialize(){if(!this._loaded)return;const e=t.bY(this.tileManagers,(e=>e.serialize())),i=this._serializeByIds(this._order,!0),o=this.map.getTerrain()||void 0,a=this.stylesheet;return t.bZ({version:a.version,name:a.name,metadata:a.metadata,light:a.light,sky:a.sky,center:a.center,zoom:a.zoom,bearing:a.bearing,pitch:a.pitch,sprite:a.sprite,glyphs:a.glyphs,transition:a.transition,projection:a.projection,sources:e,layers:i,terrain:o},(e=>void 0!==e))}_updateLayer(e){this._updatedLayers[e.id]=!0,e.source&&!this._updatedSources[e.source]&&"raster"!==this.tileManagers[e.source].getSource().type&&(this._updatedSources[e.source]="reload",this.tileManagers[e.source].pause()),this._serializedLayers=null,this._changed=!0;}_flattenAndSortRenderedFeatures(e){const t=e=>"fill-extrusion"===this._layers[e].type,i={},o=[];for(let a=this._order.length-1;a>=0;a--){const r=this._order[a];if(t(r)){i[r]=a;for(const t of e){const e=t[r];if(e)for(const t of e)o.push(t);}}}o.sort(((e,t)=>t.intersectionZ-e.intersectionZ));const a=[];for(let r=this._order.length-1;r>=0;r--){const s=this._order[r];if(t(s))for(let e=o.length-1;e>=0;e--){const t=o[e].feature;if(i[t.layer.id]this.map.terrain.getElevation(e,t,i):void 0));return this.placement&&r.push(function(e,t,i,o,a,r,s){const n={},l=r.queryRenderedSymbols(o),c=[];for(const e of Object.keys(l).map(Number))c.push(s[e]);c.sort(N);for(const i of c){const o=i.featureIndex.lookupSymbolFeatures(l[i.bucketInstanceId],t,i.bucketIndex,i.sourceLayerIndex,{filterSpec:a.filter,globalState:a.globalState},a.layers,a.availableImages,e);for(const e in o){n[e]||(n[e]=[]);const t=o[e];t.sort(((e,t)=>{const o=i.featureSortOrder;if(o){const i=o.indexOf(e.featureIndex);return o.indexOf(t.featureIndex)-i}return t.featureIndex-e.featureIndex}));for(const i of t)n[e].push(i);}}return function(e,t,i){for(const o in e)for(const a of e[o])Z(a,i[t[o].source]);return e}(n,e,i)}(this._layers,s,this.tileManagers,e,l,this.placement.collisionIndex,this.placement.retainedQueryData)),this._flattenAndSortRenderedFeatures(r)}querySourceFeatures(e,i){(null==i?void 0:i.filter)&&this._validate(t.F.filter,"querySourceFeatures.filter",i.filter,null,i);const o=this.tileManagers[e];return o?function(e,t){const i=e.getRenderableIds().map((t=>e.getTileByID(t))),o=[],a={};for(const e of i){const i=e.tileID.canonical.key;a[i]||(a[i]=!0,e.querySourceFeatures(o,t));}return o}(o,i?Object.assign(Object.assign({},i),{globalState:this._globalState}):{globalState:this._globalState}):[]}getLight(){return this.light.getLight()}setLight(e,i={}){this._checkLoaded();const o=this.light.getLight();let a=!1;for(const i in e)if(!t.bQ(e[i],o[i])){a=!0;break}if(!a)return;const r={now:c(),transition:t.e({duration:300,delay:0},this.stylesheet.transition)};this.light.setLight(e,i),this.light.updateTransitions(r);}getProjection(){var e;return null===(e=this.stylesheet)||void 0===e?void 0:e.projection}setProjection(e){this._checkLoaded();const t=null!=e?e:{type:"mercator"};if(this.stylesheet.projection=e,this.projection){if(this.projection.name===t.type)return;this.projection.destroy(),delete this.projection;}this._setProjectionInternal(t.type);}getSky(){var e;return null===(e=this.stylesheet)||void 0===e?void 0:e.sky}setSky(e,i={}){this._checkLoaded();const o=this.getSky();let a=!1;if(!e&&!o)return;if(e&&!o)a=!0;else if(!e&&o)a=!0;else for(const i in e)if(!t.bQ(e[i],o[i])){a=!0;break}if(!a)return;const r={now:c(),transition:t.e({duration:300,delay:0},this.stylesheet.transition)};this.stylesheet.sky=e,this.sky.setSky(e,i),this.sky.updateTransitions(r);}_setProjectionInternal(e){const i=function(e,i){const o={constrainOverride:i};if(Array.isArray(e)){const t=new _i({type:e});return {projection:t,transform:new Ri(o),cameraHelper:new Ai(t)}}switch(e){case "mercator":return {projection:new Zt,transform:new Xt(o),cameraHelper:new Jt};case "globe":{const e=new _i({type:["interpolate",["linear"],["zoom"],11,"vertical-perspective",12,"mercator"]});return {projection:e,transform:new Ri(o),cameraHelper:new Ai(e)}}case "vertical-perspective":return {projection:new ui,transform:new zi(o),cameraHelper:new Di};default:return t.w(`Unknown projection name: ${e}. Falling back to mercator projection.`),{projection:new Zt,transform:new Xt(o),cameraHelper:new Jt}}}(e,this.map.transformConstrain);this.projection=i.projection,this.map.migrateProjection(i.transform,i.cameraHelper);for(const e in this.tileManagers)this.tileManagers[e].reload();}_validate(e,i,o,a,r={}){return !1!==(null==r?void 0:r.validate)&&Li(this,e.call(t.F,t.e({key:i,style:this.serialize(),value:o,styleSpec:t.x},a)))}_remove(e=!0){this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._loadStyleRequest&&(this._loadStyleRequest.abort(),this._loadStyleRequest=null),this._spriteRequest&&(this._spriteRequest.abort(),this._spriteRequest=null),ce().off(se,this._rtlPluginLoaded);for(const e in this._layers)this._layers[e].setEventedParent(null);for(const e in this.tileManagers){const t=this.tileManagers[e];t.setEventedParent(null),t.onRemove(this.map);}this.imageManager.setEventedParent(null),this.setEventedParent(null),e&&this.dispatcher.broadcast("RM",void 0),this.dispatcher.remove(e);}_clearSource(e){this.tileManagers[e].clearTiles();}_reloadSource(e){this.tileManagers[e].resume(),this.tileManagers[e].reload();}_updateSources(e){for(const t in this.tileManagers)this.tileManagers[t].update(e,this.map.terrain);}_generateCollisionBoxes(){for(const e in this.tileManagers)this._reloadSource(e);}_updatePlacement(e,t,i,o,a=!1){let r=!1,s=!1;const n={};for(const t of this._order){const i=this._layers[t];if("symbol"!==i.type)continue;if(!n[i.source]){const e=this.tileManagers[i.source];n[i.source]=e.getRenderableIds(!0).map((t=>e.getTileByID(t))).sort(((e,t)=>t.tileID.overscaledZ-e.tileID.overscaledZ||(e.tileID.isLessThan(t.tileID)?-1:1)));}const o=this.crossTileSymbolIndex.addLayer(i,n[i.source],e.center.lng);r||(r=o);}if(this.crossTileSymbolIndex.pruneUnusedLayers(this._order),a||(a=this._layerOrderChanged||0===i),(a||!this.pauseablePlacement||this.pauseablePlacement.isDone()&&!this.placement.stillRecent(c(),e.zoom))&&(this.pauseablePlacement=new Et(e,this.map.terrain,this._order,a,t,i,o,this.placement),this._layerOrderChanged=!1),this.pauseablePlacement.isDone()?this.placement.setStale():(this.pauseablePlacement.continuePlacement(this._order,this._layers,n),this.pauseablePlacement.isDone()&&(this.placement=this.pauseablePlacement.commit(c()),s=!0),r&&this.pauseablePlacement.placement.setStale()),s||r)for(const e of this._order){const t=this._layers[e];"symbol"===t.type&&this.placement.updateLayerOpacities(t,n[t.source]);}return !this.pauseablePlacement.isDone()||this.placement.hasTransitions(c())}_releaseSymbolFadeTiles(){for(const e in this.tileManagers)this.tileManagers[e].releaseSymbolFadeTiles();}getImages(e,i){return t._(this,void 0,void 0,(function*(){const e=yield this.imageManager.getImages(i.icons);this._updateTilesForChangedImages();const t=this.tileManagers[i.source];return t&&t.setDependencies(i.tileID.key,i.type,i.icons),e}))}getGlyphs(e,i){return t._(this,void 0,void 0,(function*(){const e=yield this.glyphManager.getGlyphs(i.stacks),t=this.tileManagers[i.source];return t&&t.setDependencies(i.tileID.key,i.type,[""]),e}))}getGlyphsUrl(){return this.stylesheet.glyphs||null}setGlyphs(e,i={}){this._checkLoaded(),e&&this._validate(t.F.glyphs,"glyphs",e,null,i)||(this._glyphsDidChange=!0,this.stylesheet.glyphs=e,this.glyphManager.entries={},this.glyphManager.setURL(e));}getDashes(e,i){return t._(this,void 0,void 0,(function*(){const e={};for(const[t,o]of Object.entries(i.dashes))e[t]=this.lineAtlas.getDash(o.dasharray,o.round);return e}))}addSprite(e,i,o={},a){this._checkLoaded();const r=[{id:e,url:i}],s=[...p(this.stylesheet.sprite),...r];this._validate(t.F.sprite,"sprite",s,null,o)||(this.stylesheet.sprite=s,this._loadSprite(r,!0,a));}removeSprite(e){this._checkLoaded();const i=p(this.stylesheet.sprite);if(i.find((t=>t.id===e))){if(this._spritesImagesIds[e])for(const t of this._spritesImagesIds[e])this.imageManager.removeImage(t),this._changedImages[t]=!0;i.splice(i.findIndex((t=>t.id===e)),1),this.stylesheet.sprite=i.length>0?i:void 0,delete this._spritesImagesIds[e],this._availableImages=this.imageManager.listImages(),this._changed=!0,this.dispatcher.broadcast("SI",this._availableImages),this.fire(new t.n("data",{dataType:"style"}));}else this.fire(new t.l(new Error(`Sprite "${e}" doesn't exists on this map.`)));}getSprite(){return p(this.stylesheet.sprite)}setSprite(e,i={},o){this._checkLoaded(),e&&this._validate(t.F.sprite,"sprite",e,null,i)||(this.stylesheet.sprite=e,e?this._loadSprite(e,!0,o):(this._unloadSprite(),o&&o(null)));}destroy(){this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._loadStyleRequest&&(this._loadStyleRequest.abort(),this._loadStyleRequest=null),this._spriteRequest&&(this._spriteRequest.abort(),this._spriteRequest=null);for(const e in this.tileManagers){const t=this.tileManagers[e];t.setEventedParent(null),t.onRemove(this.map);}this.tileManagers={},this.imageManager&&(this.imageManager.setEventedParent(null),this.imageManager.destroy(),this._availableImages=[],this._spritesImagesIds={}),this.glyphManager&&this.glyphManager.destroy();for(const e in this._layers){const t=this._layers[e];t.setEventedParent(null),t.onRemove&&t.onRemove(this.map);}this._setInitialValues(),this.setEventedParent(null),this.dispatcher.unregisterMessageHandler("GG"),this.dispatcher.unregisterMessageHandler("GI"),this.dispatcher.unregisterMessageHandler("GDA"),this.dispatcher.remove(!0),this._listeners={},this._oneTimeListeners={};}}var Bi=t.aS([{name:"a_pos",type:"Int16",components:2},{name:"a_texture_pos",type:"Int16",components:2}]);class Oi{constructor(){this.boundProgram=null,this.boundLayoutVertexBuffer=null,this.boundPaintVertexBuffers=[],this.boundIndexBuffer=null,this.boundVertexOffset=null,this.boundDynamicVertexBuffer=null,this.vao=null;}bind(e,t,i,o,a,r,s,n,l){this.context=e;let c=this.boundPaintVertexBuffers.length!==o.length;for(let e=0;!c&&e({u_texture:0,u_ele_delta:e,u_fog_matrix:i,u_fog_color:o?o.properties.get("fog-color"):t.bo.white,u_fog_ground_blend:o?o.properties.get("fog-ground-blend"):1,u_fog_ground_blend_opacity:r?0:o?o.calculateFogBlendOpacity(a):0,u_horizon_color:o?o.properties.get("horizon-color"):t.bo.white,u_horizon_fog_blend:o?o.properties.get("horizon-fog-blend"):1,u_is_globe_mode:r?1:0}),Ni={mainMatrix:"u_projection_matrix",tileMercatorCoords:"u_projection_tile_mercator_coords",clippingPlane:"u_projection_clipping_plane",projectionTransition:"u_projection_transition",fallbackMatrix:"u_projection_fallback_matrix"};function Zi(e){const t=[];for(const i of e){if(null===i)continue;const e=i.split(" ");t.push(e.pop());}return t}class Ui{constructor(e,i,o,a,r,s,n,l,c=[]){const h=e.gl;this.program=h.createProgram();const u=Zi(i.staticAttributes),d=o?o.getBinderAttributes():[],_=u.concat(d),p=Ft.prelude.staticUniforms?Zi(Ft.prelude.staticUniforms):[],m=n.staticUniforms?Zi(n.staticUniforms):[],f=i.staticUniforms?Zi(i.staticUniforms):[],g=o?o.getBinderUniforms():[],v=p.concat(m).concat(f).concat(g),x=[];for(const e of v)x.includes(e)||x.push(e);const b=o?o.defines():[];si(h)&&b.unshift("#version 300 es"),r&&b.push("#define OVERDRAW_INSPECTOR;"),s&&b.push("#define TERRAIN3D;"),l&&b.push(l),c&&b.push(...c);let y=b.concat(Ft.prelude.fragmentSource,n.fragmentSource,i.fragmentSource).join("\n"),w=b.concat(Ft.prelude.vertexSource,n.vertexSource,i.vertexSource).join("\n");si(h)||(y=function(e){return e.replace(/\bin\s/g,"varying ").replace("out highp vec4 fragColor;","").replace(/fragColor/g,"gl_FragColor").replace(/texture\(/g,"texture2D(")}(y),w=function(e){return e.replace(/\bin\s/g,"attribute ").replace(/\bout\s/g,"varying ").replace(/texture\(/g,"texture2D(")}(w));const T=h.createShader(h.FRAGMENT_SHADER);if(h.isContextLost())return void(this.failedToCreate=!0);if(h.shaderSource(T,y),h.compileShader(T),!h.getShaderParameter(T,h.COMPILE_STATUS))throw new Error(`Could not compile fragment shader: ${h.getShaderInfoLog(T)}`);h.attachShader(this.program,T);const P=h.createShader(h.VERTEX_SHADER);if(h.isContextLost())return void(this.failedToCreate=!0);if(h.shaderSource(P,w),h.compileShader(P),!h.getShaderParameter(P,h.COMPILE_STATUS))throw new Error(`Could not compile vertex shader: ${h.getShaderInfoLog(P)}`);h.attachShader(this.program,P),this.attributes={};const C={};this.numAttributes=_.length;for(let e=0;e({u_depth:new t.b_(e,i.u_depth),u_terrain:new t.b_(e,i.u_terrain),u_terrain_dim:new t.bp(e,i.u_terrain_dim),u_terrain_matrix:new t.c0(e,i.u_terrain_matrix),u_terrain_unpack:new t.c1(e,i.u_terrain_unpack),u_terrain_exaggeration:new t.bp(e,i.u_terrain_exaggeration)}))(e,C),this.projectionUniforms=((e,i)=>({u_projection_matrix:new t.c0(e,i.u_projection_matrix),u_projection_tile_mercator_coords:new t.c1(e,i.u_projection_tile_mercator_coords),u_projection_clipping_plane:new t.c1(e,i.u_projection_clipping_plane),u_projection_transition:new t.bp(e,i.u_projection_transition),u_projection_fallback_matrix:new t.c0(e,i.u_projection_fallback_matrix)}))(e,C),this.binderUniforms=o?o.getUniforms(e,C):[];}draw(e,t,i,o,a,r,s,n,l,c,h,u,d,_,p,m,f,g,v){var x;const b=e.gl;if(this.failedToCreate)return;if(e.program.set(this.program),e.setDepthMode(i),e.setStencilMode(o),e.setColorMode(a),e.setCullFace(r),n){e.activeTexture.set(b.TEXTURE2),b.bindTexture(b.TEXTURE_2D,n.depthTexture),e.activeTexture.set(b.TEXTURE3),b.bindTexture(b.TEXTURE_2D,n.texture);for(const e in this.terrainUniforms)this.terrainUniforms[e].set(n[e]);}if(l)for(const e in l)this.projectionUniforms[Ni[e]].set(l[e]);if(s)for(const e in this.fixedUniforms)this.fixedUniforms[e].set(s[e]);m&&m.setUniforms(e,this.binderUniforms,_,{zoom:p});let y=0;switch(t){case b.LINES:y=2;break;case b.TRIANGLES:y=3;break;case b.LINE_STRIP:y=1;}for(const i of d.get())i.vaos||(i.vaos={}),(x=i.vaos)[c]||(x[c]=new Oi),i.vaos[c].bind(e,this,h,m?m.getPaintVertexBuffers():[],u,i.vertexOffset,f,g,v),b.drawElements(t,i.primitiveLength*y,b.UNSIGNED_SHORT,i.primitiveOffset*y*2);}}function Gi(e,i,o){const a=1/t.aK(o,1,i.transform.tileZoom),r=Math.pow(2,o.tileID.overscaledZ),s=o.tileSize*Math.pow(2,i.transform.tileZoom)/r,n=s*(o.tileID.canonical.x+o.tileID.wrap*r),l=s*o.tileID.canonical.y;return {u_image:0,u_texsize:o.imageAtlasTexture.size,u_scale:[a,e.fromScale,e.toScale],u_fade:e.t,u_pixel_coord_upper:[n>>16,l>>16],u_pixel_coord_lower:[65535&n,65535&l]}}const Vi=(e,i,o,a)=>{const r=e.style.light,s=r.properties.get("position"),n=[s.x,s.y,s.z],l=t.c4();"viewport"===r.properties.get("anchor")&&t.c5(l,e.transform.bearingInRadians),t.c6(n,n,l);const c=e.transform.transformLightDirection(n),h=r.properties.get("color");return {u_lightpos:n,u_lightpos_globe:c,u_lightintensity:r.properties.get("intensity"),u_lightcolor:[h.r,h.g,h.b],u_vertical_gradient:+i,u_opacity:o,u_fill_translate:a}},Wi=(e,i,o,a,r,s,n)=>t.e(Vi(e,i,o,a),Gi(s,e,n),{u_height_factor:-Math.pow(2,r.overscaledZ)/n.tileSize/8}),qi=(e,i,o,a)=>t.e(Gi(i,e,o),{u_fill_translate:a}),$i=(e,t)=>({u_world:e,u_fill_translate:t}),Hi=(e,i,o,a,r)=>t.e(qi(e,i,o,r),{u_world:a}),Xi=(e,i,o,a,r)=>{const s=e.transform;let n,l,c=0;if("map"===o.paint.get("circle-pitch-alignment")){const e=t.aK(i,1,s.zoom);n=!0,l=[e,e],c=e/(t.a6*Math.pow(2,i.tileID.overscaledZ))*2*Math.PI*r;}else n=!1,l=s.pixelsToGLUnits;return {u_camera_to_center_distance:s.cameraToCenterDistance,u_scale_with_map:+("map"===o.paint.get("circle-pitch-scale")),u_pitch_with_map:+n,u_device_pixel_ratio:e.pixelRatio,u_extrude_scale:l,u_globe_extrude_scale:c,u_translate:a}},Ki=e=>({u_pixel_extrude_scale:[1/e.width,1/e.height]}),Yi=e=>({u_viewport_size:[e.width,e.height]}),Qi=(e,t=1)=>({u_color:e,u_overlay:0,u_overlay_scale:t}),Ji=(e,i,o,a)=>{const r=t.aK(e,1,i)/(t.a6*Math.pow(2,e.tileID.overscaledZ))*2*Math.PI*a;return {u_extrude_scale:t.aK(e,1,i),u_intensity:o,u_globe_extrude_scale:r}},eo=(e,i,o,a)=>{const r=t.O();t.c7(r,0,e.width,e.height,0,0,1);const s=e.context.gl;return {u_matrix:r,u_world:[s.drawingBufferWidth,s.drawingBufferHeight],u_image:o,u_color_ramp:a,u_opacity:i.paint.get("heatmap-opacity")}},to=(e,t,i)=>{const o=i.paint.get("hillshade-accent-color");let a;switch(i.paint.get("hillshade-method")){case "basic":a=4;break;case "combined":a=1;break;case "igor":a=2;break;case "multidirectional":a=3;break;default:a=0;}const r=i.getIlluminationProperties();for(let t=0;t{const o=i.stride,a=t.O();return t.c7(a,0,t.a6,-t.a6,0,0,1),t.Q(a,a,[0,-t.a6,0]),{u_matrix:a,u_image:1,u_dimension:[o,o],u_zoom:e.overscaledZ,u_unpack:i.getUnpackVector()}};function oo(e,i){const o=Math.pow(2,i.canonical.z),a=i.canonical.y;return [new t.a7(0,a/o).toLngLat().lat,new t.a7(0,(a+1)/o).toLngLat().lat]}const ao=(e,t,i=0)=>({u_image:0,u_unpack:t.getUnpackVector(),u_dimension:[t.stride,t.stride],u_elevation_stops:1,u_color_stops:4,u_color_ramp_size:i,u_opacity:e.paint.get("color-relief-opacity")}),ro=(e,i,o,a)=>{const r=e.transform;return {u_translation:uo(e,i,o),u_ratio:a/t.aK(i,1,r.zoom),u_device_pixel_ratio:e.pixelRatio,u_units_to_pixels:[1/r.pixelsToGLUnits[0],1/r.pixelsToGLUnits[1]]}},so=(e,i,o,a,r)=>t.e(ro(e,i,o,a),{u_image:0,u_image_height:r}),no=(e,i,o,a,r)=>{const s=e.transform,n=ho(i,s);return {u_translation:uo(e,i,o),u_texsize:i.imageAtlasTexture.size,u_ratio:a/t.aK(i,1,s.zoom),u_device_pixel_ratio:e.pixelRatio,u_image:0,u_scale:[n,r.fromScale,r.toScale],u_fade:r.t,u_units_to_pixels:[1/s.pixelsToGLUnits[0],1/s.pixelsToGLUnits[1]]}},lo=(e,i,o,a,r)=>{const s=ho(i,e.transform);return t.e(ro(e,i,o,a),{u_tileratio:s,u_crossfade_from:r.fromScale,u_crossfade_to:r.toScale,u_image:0,u_mix:r.t,u_lineatlas_width:e.lineAtlas.width,u_lineatlas_height:e.lineAtlas.height})},co=(e,i,o,a,r,s)=>{const n=ho(i,e.transform);return t.e(ro(e,i,o,a),{u_image:0,u_image_height:s,u_tileratio:n,u_crossfade_from:r.fromScale,u_crossfade_to:r.toScale,u_image_dash:1,u_mix:r.t,u_lineatlas_width:e.lineAtlas.width,u_lineatlas_height:e.lineAtlas.height})};function ho(e,i){return 1/t.aK(e,1,i.tileZoom)}function uo(e,i,o){return t.aL(e.transform,i,o.paint.get("line-translate"),o.paint.get("line-translate-anchor"))}const _o=(e,t,i,o,a)=>{return {u_tl_parent:e,u_scale_parent:t,u_buffer_scale:1,u_fade_t:i.mix,u_opacity:i.opacity*o.paint.get("raster-opacity"),u_image0:0,u_image1:1,u_brightness_low:o.paint.get("raster-brightness-min"),u_brightness_high:o.paint.get("raster-brightness-max"),u_saturation_factor:(s=o.paint.get("raster-saturation"),s>0?1-1/(1.001-s):-s),u_contrast_factor:(r=o.paint.get("raster-contrast"),r>0?1/(1-r):1+r),u_spin_weights:po(o.paint.get("raster-hue-rotate")),u_coords_top:[a[0].x,a[0].y,a[1].x,a[1].y],u_coords_bottom:[a[3].x,a[3].y,a[2].x,a[2].y]};var r,s;};function po(e){e*=Math.PI/180;const t=Math.sin(e),i=Math.cos(e);return [(2*i+1)/3,(-Math.sqrt(3)*t-i+1)/3,(Math.sqrt(3)*t-i+1)/3]}const mo=(e,t,i,o,a,r,s,n,l,c,h,u,d)=>{const _=s.transform;return {u_is_size_zoom_constant:+("constant"===e||"source"===e),u_is_size_feature_constant:+("constant"===e||"camera"===e),u_size_t:t?t.uSizeT:0,u_size:t?t.uSize:0,u_camera_to_center_distance:_.cameraToCenterDistance,u_pitch:_.pitch/360*2*Math.PI,u_rotate_symbol:+i,u_aspect_ratio:_.width/_.height,u_fade_change:s.options.fadeDuration?s.symbolFadeChange:1,u_label_plane_matrix:n,u_coord_matrix:l,u_is_text:+h,u_pitch_with_map:+o,u_is_along_line:a,u_is_variable_anchor:r,u_texsize:u,u_texture:0,u_translation:c,u_pitched_scale:d}},fo=(e,i,o,a,r,s,n,l,c,h,u,d,_,p)=>{const m=n.transform;return t.e(mo(e,i,o,a,r,s,n,l,c,h,u,d,p),{u_gamma_scale:a?Math.cos(m.pitch*Math.PI/180)*m.cameraToCenterDistance:1,u_device_pixel_ratio:n.pixelRatio,u_is_halo:_?1:0,u_is_plain:1})},go=(e,i,o,a,r,s,n,l,c,h,u,d,_)=>t.e(fo(e,i,o,a,r,s,n,l,c,h,!0,u,!0,_),{u_texsize_icon:d,u_texture_icon:1}),vo=(e,t)=>({u_opacity:e,u_color:t}),xo=(e,i,o,a,r)=>t.e(function(e,i,o,a){const r=o.imageManager.getPattern(e.from.toString()),s=o.imageManager.getPattern(e.to.toString()),{width:n,height:l}=o.imageManager.getPixelSize(),c=Math.pow(2,a.tileID.overscaledZ),h=a.tileSize*Math.pow(2,o.transform.tileZoom)/c,u=h*(a.tileID.canonical.x+a.tileID.wrap*c),d=h*a.tileID.canonical.y;return {u_image:0,u_pattern_tl_a:r.tl,u_pattern_br_a:r.br,u_pattern_tl_b:s.tl,u_pattern_br_b:s.br,u_texsize:[n,l],u_mix:i.t,u_pattern_size_a:r.displaySize,u_pattern_size_b:s.displaySize,u_scale_a:i.fromScale,u_scale_b:i.toScale,u_tile_units_to_pixels:1/t.aK(a,1,o.transform.tileZoom),u_pixel_coord_upper:[u>>16,d>>16],u_pixel_coord_lower:[65535&u,65535&d]}}(o,r,i,a),{u_opacity:e}),bo=(e,t)=>{},yo={fillExtrusion:(e,i)=>({u_lightpos:new t.c2(e,i.u_lightpos),u_lightpos_globe:new t.c2(e,i.u_lightpos_globe),u_lightintensity:new t.bp(e,i.u_lightintensity),u_lightcolor:new t.c2(e,i.u_lightcolor),u_vertical_gradient:new t.bp(e,i.u_vertical_gradient),u_opacity:new t.bp(e,i.u_opacity),u_fill_translate:new t.c3(e,i.u_fill_translate)}),fillExtrusionPattern:(e,i)=>({u_lightpos:new t.c2(e,i.u_lightpos),u_lightpos_globe:new t.c2(e,i.u_lightpos_globe),u_lightintensity:new t.bp(e,i.u_lightintensity),u_lightcolor:new t.c2(e,i.u_lightcolor),u_vertical_gradient:new t.bp(e,i.u_vertical_gradient),u_height_factor:new t.bp(e,i.u_height_factor),u_opacity:new t.bp(e,i.u_opacity),u_fill_translate:new t.c3(e,i.u_fill_translate),u_image:new t.b_(e,i.u_image),u_texsize:new t.c3(e,i.u_texsize),u_pixel_coord_upper:new t.c3(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.c3(e,i.u_pixel_coord_lower),u_scale:new t.c2(e,i.u_scale),u_fade:new t.bp(e,i.u_fade)}),fill:(e,i)=>({u_fill_translate:new t.c3(e,i.u_fill_translate)}),fillPattern:(e,i)=>({u_image:new t.b_(e,i.u_image),u_texsize:new t.c3(e,i.u_texsize),u_pixel_coord_upper:new t.c3(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.c3(e,i.u_pixel_coord_lower),u_scale:new t.c2(e,i.u_scale),u_fade:new t.bp(e,i.u_fade),u_fill_translate:new t.c3(e,i.u_fill_translate)}),fillOutline:(e,i)=>({u_world:new t.c3(e,i.u_world),u_fill_translate:new t.c3(e,i.u_fill_translate)}),fillOutlinePattern:(e,i)=>({u_world:new t.c3(e,i.u_world),u_image:new t.b_(e,i.u_image),u_texsize:new t.c3(e,i.u_texsize),u_pixel_coord_upper:new t.c3(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.c3(e,i.u_pixel_coord_lower),u_scale:new t.c2(e,i.u_scale),u_fade:new t.bp(e,i.u_fade),u_fill_translate:new t.c3(e,i.u_fill_translate)}),circle:(e,i)=>({u_camera_to_center_distance:new t.bp(e,i.u_camera_to_center_distance),u_scale_with_map:new t.b_(e,i.u_scale_with_map),u_pitch_with_map:new t.b_(e,i.u_pitch_with_map),u_extrude_scale:new t.c3(e,i.u_extrude_scale),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_globe_extrude_scale:new t.bp(e,i.u_globe_extrude_scale),u_translate:new t.c3(e,i.u_translate)}),collisionBox:(e,i)=>({u_pixel_extrude_scale:new t.c3(e,i.u_pixel_extrude_scale)}),collisionCircle:(e,i)=>({u_viewport_size:new t.c3(e,i.u_viewport_size)}),debug:(e,i)=>({u_color:new t.b$(e,i.u_color),u_overlay:new t.b_(e,i.u_overlay),u_overlay_scale:new t.bp(e,i.u_overlay_scale)}),depth:bo,clippingMask:bo,heatmap:(e,i)=>({u_extrude_scale:new t.bp(e,i.u_extrude_scale),u_intensity:new t.bp(e,i.u_intensity),u_globe_extrude_scale:new t.bp(e,i.u_globe_extrude_scale)}),heatmapTexture:(e,i)=>({u_matrix:new t.c0(e,i.u_matrix),u_world:new t.c3(e,i.u_world),u_image:new t.b_(e,i.u_image),u_color_ramp:new t.b_(e,i.u_color_ramp),u_opacity:new t.bp(e,i.u_opacity)}),hillshade:(e,i)=>({u_image:new t.b_(e,i.u_image),u_latrange:new t.c3(e,i.u_latrange),u_exaggeration:new t.bp(e,i.u_exaggeration),u_altitudes:new t.c9(e,i.u_altitudes),u_azimuths:new t.c9(e,i.u_azimuths),u_accent:new t.b$(e,i.u_accent),u_method:new t.b_(e,i.u_method),u_shadows:new t.c8(e,i.u_shadows),u_highlights:new t.c8(e,i.u_highlights)}),hillshadePrepare:(e,i)=>({u_matrix:new t.c0(e,i.u_matrix),u_image:new t.b_(e,i.u_image),u_dimension:new t.c3(e,i.u_dimension),u_zoom:new t.bp(e,i.u_zoom),u_unpack:new t.c1(e,i.u_unpack)}),colorRelief:(e,i)=>({u_image:new t.b_(e,i.u_image),u_unpack:new t.c1(e,i.u_unpack),u_dimension:new t.c3(e,i.u_dimension),u_elevation_stops:new t.b_(e,i.u_elevation_stops),u_color_stops:new t.b_(e,i.u_color_stops),u_color_ramp_size:new t.b_(e,i.u_color_ramp_size),u_opacity:new t.bp(e,i.u_opacity)}),line:(e,i)=>({u_translation:new t.c3(e,i.u_translation),u_ratio:new t.bp(e,i.u_ratio),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.c3(e,i.u_units_to_pixels)}),lineGradient:(e,i)=>({u_translation:new t.c3(e,i.u_translation),u_ratio:new t.bp(e,i.u_ratio),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.c3(e,i.u_units_to_pixels),u_image:new t.b_(e,i.u_image),u_image_height:new t.bp(e,i.u_image_height)}),linePattern:(e,i)=>({u_translation:new t.c3(e,i.u_translation),u_texsize:new t.c3(e,i.u_texsize),u_ratio:new t.bp(e,i.u_ratio),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_image:new t.b_(e,i.u_image),u_units_to_pixels:new t.c3(e,i.u_units_to_pixels),u_scale:new t.c2(e,i.u_scale),u_fade:new t.bp(e,i.u_fade)}),lineSDF:(e,i)=>({u_translation:new t.c3(e,i.u_translation),u_ratio:new t.bp(e,i.u_ratio),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.c3(e,i.u_units_to_pixels),u_image:new t.b_(e,i.u_image),u_mix:new t.bp(e,i.u_mix),u_tileratio:new t.bp(e,i.u_tileratio),u_crossfade_from:new t.bp(e,i.u_crossfade_from),u_crossfade_to:new t.bp(e,i.u_crossfade_to),u_lineatlas_width:new t.bp(e,i.u_lineatlas_width),u_lineatlas_height:new t.bp(e,i.u_lineatlas_height)}),lineGradientSDF:(e,i)=>({u_translation:new t.c3(e,i.u_translation),u_ratio:new t.bp(e,i.u_ratio),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.c3(e,i.u_units_to_pixels),u_image:new t.b_(e,i.u_image),u_image_height:new t.bp(e,i.u_image_height),u_tileratio:new t.bp(e,i.u_tileratio),u_crossfade_from:new t.bp(e,i.u_crossfade_from),u_crossfade_to:new t.bp(e,i.u_crossfade_to),u_image_dash:new t.b_(e,i.u_image_dash),u_mix:new t.bp(e,i.u_mix),u_lineatlas_width:new t.bp(e,i.u_lineatlas_width),u_lineatlas_height:new t.bp(e,i.u_lineatlas_height)}),raster:(e,i)=>({u_tl_parent:new t.c3(e,i.u_tl_parent),u_scale_parent:new t.bp(e,i.u_scale_parent),u_buffer_scale:new t.bp(e,i.u_buffer_scale),u_fade_t:new t.bp(e,i.u_fade_t),u_opacity:new t.bp(e,i.u_opacity),u_image0:new t.b_(e,i.u_image0),u_image1:new t.b_(e,i.u_image1),u_brightness_low:new t.bp(e,i.u_brightness_low),u_brightness_high:new t.bp(e,i.u_brightness_high),u_saturation_factor:new t.bp(e,i.u_saturation_factor),u_contrast_factor:new t.bp(e,i.u_contrast_factor),u_spin_weights:new t.c2(e,i.u_spin_weights),u_coords_top:new t.c1(e,i.u_coords_top),u_coords_bottom:new t.c1(e,i.u_coords_bottom)}),symbolIcon:(e,i)=>({u_is_size_zoom_constant:new t.b_(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.b_(e,i.u_is_size_feature_constant),u_size_t:new t.bp(e,i.u_size_t),u_size:new t.bp(e,i.u_size),u_camera_to_center_distance:new t.bp(e,i.u_camera_to_center_distance),u_pitch:new t.bp(e,i.u_pitch),u_rotate_symbol:new t.b_(e,i.u_rotate_symbol),u_aspect_ratio:new t.bp(e,i.u_aspect_ratio),u_fade_change:new t.bp(e,i.u_fade_change),u_label_plane_matrix:new t.c0(e,i.u_label_plane_matrix),u_coord_matrix:new t.c0(e,i.u_coord_matrix),u_is_text:new t.b_(e,i.u_is_text),u_pitch_with_map:new t.b_(e,i.u_pitch_with_map),u_is_along_line:new t.b_(e,i.u_is_along_line),u_is_variable_anchor:new t.b_(e,i.u_is_variable_anchor),u_texsize:new t.c3(e,i.u_texsize),u_texture:new t.b_(e,i.u_texture),u_translation:new t.c3(e,i.u_translation),u_pitched_scale:new t.bp(e,i.u_pitched_scale)}),symbolSDF:(e,i)=>({u_is_size_zoom_constant:new t.b_(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.b_(e,i.u_is_size_feature_constant),u_size_t:new t.bp(e,i.u_size_t),u_size:new t.bp(e,i.u_size),u_camera_to_center_distance:new t.bp(e,i.u_camera_to_center_distance),u_pitch:new t.bp(e,i.u_pitch),u_rotate_symbol:new t.b_(e,i.u_rotate_symbol),u_aspect_ratio:new t.bp(e,i.u_aspect_ratio),u_fade_change:new t.bp(e,i.u_fade_change),u_label_plane_matrix:new t.c0(e,i.u_label_plane_matrix),u_coord_matrix:new t.c0(e,i.u_coord_matrix),u_is_text:new t.b_(e,i.u_is_text),u_pitch_with_map:new t.b_(e,i.u_pitch_with_map),u_is_along_line:new t.b_(e,i.u_is_along_line),u_is_variable_anchor:new t.b_(e,i.u_is_variable_anchor),u_texsize:new t.c3(e,i.u_texsize),u_texture:new t.b_(e,i.u_texture),u_gamma_scale:new t.bp(e,i.u_gamma_scale),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_is_halo:new t.b_(e,i.u_is_halo),u_is_plain:new t.b_(e,i.u_is_plain),u_translation:new t.c3(e,i.u_translation),u_pitched_scale:new t.bp(e,i.u_pitched_scale)}),symbolTextAndIcon:(e,i)=>({u_is_size_zoom_constant:new t.b_(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.b_(e,i.u_is_size_feature_constant),u_size_t:new t.bp(e,i.u_size_t),u_size:new t.bp(e,i.u_size),u_camera_to_center_distance:new t.bp(e,i.u_camera_to_center_distance),u_pitch:new t.bp(e,i.u_pitch),u_rotate_symbol:new t.b_(e,i.u_rotate_symbol),u_aspect_ratio:new t.bp(e,i.u_aspect_ratio),u_fade_change:new t.bp(e,i.u_fade_change),u_label_plane_matrix:new t.c0(e,i.u_label_plane_matrix),u_coord_matrix:new t.c0(e,i.u_coord_matrix),u_is_text:new t.b_(e,i.u_is_text),u_pitch_with_map:new t.b_(e,i.u_pitch_with_map),u_is_along_line:new t.b_(e,i.u_is_along_line),u_is_variable_anchor:new t.b_(e,i.u_is_variable_anchor),u_texsize:new t.c3(e,i.u_texsize),u_texsize_icon:new t.c3(e,i.u_texsize_icon),u_texture:new t.b_(e,i.u_texture),u_texture_icon:new t.b_(e,i.u_texture_icon),u_gamma_scale:new t.bp(e,i.u_gamma_scale),u_device_pixel_ratio:new t.bp(e,i.u_device_pixel_ratio),u_is_halo:new t.b_(e,i.u_is_halo),u_translation:new t.c3(e,i.u_translation),u_pitched_scale:new t.bp(e,i.u_pitched_scale)}),background:(e,i)=>({u_opacity:new t.bp(e,i.u_opacity),u_color:new t.b$(e,i.u_color)}),backgroundPattern:(e,i)=>({u_opacity:new t.bp(e,i.u_opacity),u_image:new t.b_(e,i.u_image),u_pattern_tl_a:new t.c3(e,i.u_pattern_tl_a),u_pattern_br_a:new t.c3(e,i.u_pattern_br_a),u_pattern_tl_b:new t.c3(e,i.u_pattern_tl_b),u_pattern_br_b:new t.c3(e,i.u_pattern_br_b),u_texsize:new t.c3(e,i.u_texsize),u_mix:new t.bp(e,i.u_mix),u_pattern_size_a:new t.c3(e,i.u_pattern_size_a),u_pattern_size_b:new t.c3(e,i.u_pattern_size_b),u_scale_a:new t.bp(e,i.u_scale_a),u_scale_b:new t.bp(e,i.u_scale_b),u_pixel_coord_upper:new t.c3(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.c3(e,i.u_pixel_coord_lower),u_tile_units_to_pixels:new t.bp(e,i.u_tile_units_to_pixels)}),terrain:(e,i)=>({u_texture:new t.b_(e,i.u_texture),u_ele_delta:new t.bp(e,i.u_ele_delta),u_fog_matrix:new t.c0(e,i.u_fog_matrix),u_fog_color:new t.b$(e,i.u_fog_color),u_fog_ground_blend:new t.bp(e,i.u_fog_ground_blend),u_fog_ground_blend_opacity:new t.bp(e,i.u_fog_ground_blend_opacity),u_horizon_color:new t.b$(e,i.u_horizon_color),u_horizon_fog_blend:new t.bp(e,i.u_horizon_fog_blend),u_is_globe_mode:new t.bp(e,i.u_is_globe_mode)}),terrainDepth:(e,i)=>({u_ele_delta:new t.bp(e,i.u_ele_delta)}),terrainCoords:(e,i)=>({u_texture:new t.b_(e,i.u_texture),u_terrain_coords_id:new t.bp(e,i.u_terrain_coords_id),u_ele_delta:new t.bp(e,i.u_ele_delta)}),projectionErrorMeasurement:(e,i)=>({u_input:new t.bp(e,i.u_input),u_output_expected:new t.bp(e,i.u_output_expected)}),atmosphere:(e,i)=>({u_sun_pos:new t.c2(e,i.u_sun_pos),u_atmosphere_blend:new t.bp(e,i.u_atmosphere_blend),u_globe_position:new t.c2(e,i.u_globe_position),u_globe_radius:new t.bp(e,i.u_globe_radius),u_inv_proj_matrix:new t.c0(e,i.u_inv_proj_matrix)}),sky:(e,i)=>({u_sky_color:new t.b$(e,i.u_sky_color),u_horizon_color:new t.b$(e,i.u_horizon_color),u_horizon:new t.c3(e,i.u_horizon),u_horizon_normal:new t.c3(e,i.u_horizon_normal),u_sky_horizon_blend:new t.bp(e,i.u_sky_horizon_blend),u_sky_blend:new t.bp(e,i.u_sky_blend)})};class wo{constructor(e,t,i){this.context=e;const o=e.gl;this.buffer=o.createBuffer(),this.dynamicDraw=Boolean(i),this.context.unbindVAO(),e.bindElementBuffer.set(this.buffer),o.bufferData(o.ELEMENT_ARRAY_BUFFER,t.arrayBuffer,this.dynamicDraw?o.DYNAMIC_DRAW:o.STATIC_DRAW),this.dynamicDraw||t.freeBufferAfterUpload();}bind(){this.context.bindElementBuffer.set(this.buffer);}updateData(e){const t=this.context.gl;if(!this.dynamicDraw)throw new Error("Attempted to update data while not in dynamic mode.");this.context.unbindVAO(),this.bind(),t.bufferSubData(t.ELEMENT_ARRAY_BUFFER,0,e.arrayBuffer);}destroy(){this.buffer&&(this.context.gl.deleteBuffer(this.buffer),delete this.buffer);}}const To={Int8:"BYTE",Uint8:"UNSIGNED_BYTE",Int16:"SHORT",Uint16:"UNSIGNED_SHORT",Int32:"INT",Uint32:"UNSIGNED_INT",Float32:"FLOAT"};class Po{constructor(e,t,i,o){this.length=t.length,this.attributes=i,this.itemSize=t.bytesPerElement,this.dynamicDraw=o,this.context=e;const a=e.gl;this.buffer=a.createBuffer(),e.bindVertexBuffer.set(this.buffer),a.bufferData(a.ARRAY_BUFFER,t.arrayBuffer,this.dynamicDraw?a.DYNAMIC_DRAW:a.STATIC_DRAW),this.dynamicDraw||t.freeBufferAfterUpload();}bind(){this.context.bindVertexBuffer.set(this.buffer);}updateData(e){if(e.length!==this.length)throw new Error(`Length of new data is ${e.length}, which doesn't match current length of ${this.length}`);const t=this.context.gl;this.bind(),t.bufferSubData(t.ARRAY_BUFFER,0,e.arrayBuffer);}enableAttributes(e,t){for(const i of this.attributes){const o=t.attributes[i.name];void 0!==o&&e.enableVertexAttribArray(o);}}setVertexAttribPointers(e,t,i){for(const o of this.attributes){const a=t.attributes[o.name];void 0!==a&&e.vertexAttribPointer(a,o.components,e[To[o.type]],!1,this.itemSize,o.offset+this.itemSize*(i||0));}}destroy(){this.buffer&&(this.context.gl.deleteBuffer(this.buffer),delete this.buffer);}}class Co{constructor(e){this.gl=e.gl,this.default=this.getDefault(),this.current=this.default,this.dirty=!1;}get(){return this.current}set(e){}getDefault(){return this.default}setDefault(){this.set(this.default);}}class Mo extends Co{getDefault(){return t.bo.transparent}set(e){const t=this.current;(e.r!==t.r||e.g!==t.g||e.b!==t.b||e.a!==t.a||this.dirty)&&(this.gl.clearColor(e.r,e.g,e.b,e.a),this.current=e,this.dirty=!1);}}class Io extends Co{getDefault(){return 1}set(e){(e!==this.current||this.dirty)&&(this.gl.clearDepth(e),this.current=e,this.dirty=!1);}}class Eo extends Co{getDefault(){return 0}set(e){(e!==this.current||this.dirty)&&(this.gl.clearStencil(e),this.current=e,this.dirty=!1);}}class So extends Co{getDefault(){return [!0,!0,!0,!0]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||e[2]!==t[2]||e[3]!==t[3]||this.dirty)&&(this.gl.colorMask(e[0],e[1],e[2],e[3]),this.current=e,this.dirty=!1);}}class zo extends Co{getDefault(){return !0}set(e){(e!==this.current||this.dirty)&&(this.gl.depthMask(e),this.current=e,this.dirty=!1);}}class Ro extends Co{getDefault(){return 255}set(e){(e!==this.current||this.dirty)&&(this.gl.stencilMask(e),this.current=e,this.dirty=!1);}}class Do extends Co{getDefault(){return {func:this.gl.ALWAYS,ref:0,mask:255}}set(e){const t=this.current;(e.func!==t.func||e.ref!==t.ref||e.mask!==t.mask||this.dirty)&&(this.gl.stencilFunc(e.func,e.ref,e.mask),this.current=e,this.dirty=!1);}}class Ao extends Co{getDefault(){const e=this.gl;return [e.KEEP,e.KEEP,e.KEEP]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||e[2]!==t[2]||this.dirty)&&(this.gl.stencilOp(e[0],e[1],e[2]),this.current=e,this.dirty=!1);}}class Lo extends Co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.STENCIL_TEST):t.disable(t.STENCIL_TEST),this.current=e,this.dirty=!1;}}class Fo extends Co{getDefault(){return [0,1]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||this.dirty)&&(this.gl.depthRange(e[0],e[1]),this.current=e,this.dirty=!1);}}class ko extends Co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.DEPTH_TEST):t.disable(t.DEPTH_TEST),this.current=e,this.dirty=!1;}}class Bo extends Co{getDefault(){return this.gl.LESS}set(e){(e!==this.current||this.dirty)&&(this.gl.depthFunc(e),this.current=e,this.dirty=!1);}}class Oo extends Co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.BLEND):t.disable(t.BLEND),this.current=e,this.dirty=!1;}}class jo extends Co{getDefault(){const e=this.gl;return [e.ONE,e.ZERO]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||this.dirty)&&(this.gl.blendFunc(e[0],e[1]),this.current=e,this.dirty=!1);}}class No extends Co{getDefault(){return t.bo.transparent}set(e){const t=this.current;(e.r!==t.r||e.g!==t.g||e.b!==t.b||e.a!==t.a||this.dirty)&&(this.gl.blendColor(e.r,e.g,e.b,e.a),this.current=e,this.dirty=!1);}}class Zo extends Co{getDefault(){return this.gl.FUNC_ADD}set(e){(e!==this.current||this.dirty)&&(this.gl.blendEquation(e),this.current=e,this.dirty=!1);}}class Uo extends Co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;e?t.enable(t.CULL_FACE):t.disable(t.CULL_FACE),this.current=e,this.dirty=!1;}}class Go extends Co{getDefault(){return this.gl.BACK}set(e){(e!==this.current||this.dirty)&&(this.gl.cullFace(e),this.current=e,this.dirty=!1);}}class Vo extends Co{getDefault(){return this.gl.CCW}set(e){(e!==this.current||this.dirty)&&(this.gl.frontFace(e),this.current=e,this.dirty=!1);}}class Wo extends Co{getDefault(){return null}set(e){(e!==this.current||this.dirty)&&(this.gl.useProgram(e),this.current=e,this.dirty=!1);}}class qo extends Co{getDefault(){return this.gl.TEXTURE0}set(e){(e!==this.current||this.dirty)&&(this.gl.activeTexture(e),this.current=e,this.dirty=!1);}}class $o extends Co{getDefault(){const e=this.gl;return [0,0,e.drawingBufferWidth,e.drawingBufferHeight]}set(e){const t=this.current;(e[0]!==t[0]||e[1]!==t[1]||e[2]!==t[2]||e[3]!==t[3]||this.dirty)&&(this.gl.viewport(e[0],e[1],e[2],e[3]),this.current=e,this.dirty=!1);}}class Ho extends Co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindFramebuffer(t.FRAMEBUFFER,e),this.current=e,this.dirty=!1;}}class Xo extends Co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindRenderbuffer(t.RENDERBUFFER,e),this.current=e,this.dirty=!1;}}class Ko extends Co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindTexture(t.TEXTURE_2D,e),this.current=e,this.dirty=!1;}}class Yo extends Co{getDefault(){return null}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.bindBuffer(t.ARRAY_BUFFER,e),this.current=e,this.dirty=!1;}}class Qo extends Co{getDefault(){return null}set(e){const t=this.gl;t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,e),this.current=e,this.dirty=!1;}}class Jo extends Co{getDefault(){return null}set(e){var t;if(e===this.current&&!this.dirty)return;const i=this.gl;si(i)?i.bindVertexArray(e):null===(t=i.getExtension("OES_vertex_array_object"))||void 0===t||t.bindVertexArrayOES(e),this.current=e,this.dirty=!1;}}class ea extends Co{getDefault(){return 4}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.pixelStorei(t.UNPACK_ALIGNMENT,e),this.current=e,this.dirty=!1;}}class ta extends Co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,e),this.current=e,this.dirty=!1;}}class ia extends Co{getDefault(){return !1}set(e){if(e===this.current&&!this.dirty)return;const t=this.gl;t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,e),this.current=e,this.dirty=!1;}}class oa extends Co{constructor(e,t){super(e),this.context=e,this.parent=t;}getDefault(){return null}}class aa extends oa{setDirty(){this.dirty=!0;}set(e){if(e===this.current&&!this.dirty)return;this.context.bindFramebuffer.set(this.parent);const t=this.gl;t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,e,0),this.current=e,this.dirty=!1;}}class ra extends oa{set(e){if(e===this.current&&!this.dirty)return;this.context.bindFramebuffer.set(this.parent);const t=this.gl;t.framebufferRenderbuffer(t.FRAMEBUFFER,t.DEPTH_ATTACHMENT,t.RENDERBUFFER,e),this.current=e,this.dirty=!1;}}class sa extends oa{set(e){if(e===this.current&&!this.dirty)return;this.context.bindFramebuffer.set(this.parent);const t=this.gl;t.framebufferRenderbuffer(t.FRAMEBUFFER,t.DEPTH_STENCIL_ATTACHMENT,t.RENDERBUFFER,e),this.current=e,this.dirty=!1;}}const na="Framebuffer is not complete";class la{constructor(e,t,i,o,a){this.context=e,this.width=t,this.height=i;const r=e.gl,s=this.framebuffer=r.createFramebuffer();if(this.colorAttachment=new aa(e,s),o)this.depthAttachment=a?new sa(e,s):new ra(e,s);else if(a)throw new Error("Stencil cannot be set without depth");if(r.checkFramebufferStatus(r.FRAMEBUFFER)!==r.FRAMEBUFFER_COMPLETE)throw new Error(na)}destroy(){const e=this.context.gl,t=this.colorAttachment.get();if(t&&e.deleteTexture(t),this.depthAttachment){const t=this.depthAttachment.get();t&&e.deleteRenderbuffer(t);}e.deleteFramebuffer(this.framebuffer);}}class ca{constructor(e){var t,i;if(this.gl=e,this.clearColor=new Mo(this),this.clearDepth=new Io(this),this.clearStencil=new Eo(this),this.colorMask=new So(this),this.depthMask=new zo(this),this.stencilMask=new Ro(this),this.stencilFunc=new Do(this),this.stencilOp=new Ao(this),this.stencilTest=new Lo(this),this.depthRange=new Fo(this),this.depthTest=new ko(this),this.depthFunc=new Bo(this),this.blend=new Oo(this),this.blendFunc=new jo(this),this.blendColor=new No(this),this.blendEquation=new Zo(this),this.cullFace=new Uo(this),this.cullFaceSide=new Go(this),this.frontFace=new Vo(this),this.program=new Wo(this),this.activeTexture=new qo(this),this.viewport=new $o(this),this.bindFramebuffer=new Ho(this),this.bindRenderbuffer=new Xo(this),this.bindTexture=new Ko(this),this.bindVertexBuffer=new Yo(this),this.bindElementBuffer=new Qo(this),this.bindVertexArray=new Jo(this),this.pixelStoreUnpack=new ea(this),this.pixelStoreUnpackPremultiplyAlpha=new ta(this),this.pixelStoreUnpackFlipY=new ia(this),this.extTextureFilterAnisotropic=e.getExtension("EXT_texture_filter_anisotropic"),this.extTextureFilterAnisotropic&&(this.extTextureFilterAnisotropicMax=e.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT)),this.maxTextureSize=e.getParameter(e.MAX_TEXTURE_SIZE),si(e)){this.HALF_FLOAT=e.HALF_FLOAT;const o=e.getExtension("EXT_color_buffer_half_float");this.RGBA16F=null!==(t=e.RGBA16F)&&void 0!==t?t:null==o?void 0:o.RGBA16F_EXT,this.RGB16F=null!==(i=e.RGB16F)&&void 0!==i?i:null==o?void 0:o.RGB16F_EXT,e.getExtension("EXT_color_buffer_float");}else {e.getExtension("EXT_color_buffer_half_float"),e.getExtension("OES_texture_half_float_linear");const t=e.getExtension("OES_texture_half_float");this.HALF_FLOAT=null==t?void 0:t.HALF_FLOAT_OES;}}setDefault(){this.unbindVAO(),this.clearColor.setDefault(),this.clearDepth.setDefault(),this.clearStencil.setDefault(),this.colorMask.setDefault(),this.depthMask.setDefault(),this.stencilMask.setDefault(),this.stencilFunc.setDefault(),this.stencilOp.setDefault(),this.stencilTest.setDefault(),this.depthRange.setDefault(),this.depthTest.setDefault(),this.depthFunc.setDefault(),this.blend.setDefault(),this.blendFunc.setDefault(),this.blendColor.setDefault(),this.blendEquation.setDefault(),this.cullFace.setDefault(),this.cullFaceSide.setDefault(),this.frontFace.setDefault(),this.program.setDefault(),this.activeTexture.setDefault(),this.bindFramebuffer.setDefault(),this.pixelStoreUnpack.setDefault(),this.pixelStoreUnpackPremultiplyAlpha.setDefault(),this.pixelStoreUnpackFlipY.setDefault();}setDirty(){this.clearColor.dirty=!0,this.clearDepth.dirty=!0,this.clearStencil.dirty=!0,this.colorMask.dirty=!0,this.depthMask.dirty=!0,this.stencilMask.dirty=!0,this.stencilFunc.dirty=!0,this.stencilOp.dirty=!0,this.stencilTest.dirty=!0,this.depthRange.dirty=!0,this.depthTest.dirty=!0,this.depthFunc.dirty=!0,this.blend.dirty=!0,this.blendFunc.dirty=!0,this.blendColor.dirty=!0,this.blendEquation.dirty=!0,this.cullFace.dirty=!0,this.cullFaceSide.dirty=!0,this.frontFace.dirty=!0,this.program.dirty=!0,this.activeTexture.dirty=!0,this.viewport.dirty=!0,this.bindFramebuffer.dirty=!0,this.bindRenderbuffer.dirty=!0,this.bindTexture.dirty=!0,this.bindVertexBuffer.dirty=!0,this.bindElementBuffer.dirty=!0,this.bindVertexArray.dirty=!0,this.pixelStoreUnpack.dirty=!0,this.pixelStoreUnpackPremultiplyAlpha.dirty=!0,this.pixelStoreUnpackFlipY.dirty=!0;}createIndexBuffer(e,t){return new wo(this,e,t)}createVertexBuffer(e,t,i){return new Po(this,e,t,i)}createRenderbuffer(e,t,i){const o=this.gl,a=o.createRenderbuffer();return this.bindRenderbuffer.set(a),o.renderbufferStorage(o.RENDERBUFFER,e,t,i),this.bindRenderbuffer.set(null),a}createFramebuffer(e,t,i,o){return new la(this,e,t,i,o)}clear({color:e,depth:t,stencil:i}){const o=this.gl;let a=0;e&&(a|=o.COLOR_BUFFER_BIT,this.clearColor.set(e),this.colorMask.set([!0,!0,!0,!0])),void 0!==t&&(a|=o.DEPTH_BUFFER_BIT,this.depthRange.set([0,1]),this.clearDepth.set(t),this.depthMask.set(!0)),void 0!==i&&(a|=o.STENCIL_BUFFER_BIT,this.clearStencil.set(i),this.stencilMask.set(255)),o.clear(a);}setCullFace(e){!1===e.enable?this.cullFace.set(!1):(this.cullFace.set(!0),this.cullFaceSide.set(e.mode),this.frontFace.set(e.frontFace));}setDepthMode(e){e.func!==this.gl.ALWAYS||e.mask?(this.depthTest.set(!0),this.depthFunc.set(e.func),this.depthMask.set(e.mask),this.depthRange.set(e.range)):this.depthTest.set(!1);}setStencilMode(e){e.test.func!==this.gl.ALWAYS||e.mask?(this.stencilTest.set(!0),this.stencilMask.set(e.mask),this.stencilOp.set([e.fail,e.depthFail,e.pass]),this.stencilFunc.set({func:e.test.func,ref:e.ref,mask:e.test.mask})):this.stencilTest.set(!1);}setColorMode(e){t.bQ(e.blendFunction,ei.Replace)?this.blend.set(!1):(this.blend.set(!0),this.blendFunc.set(e.blendFunction),this.blendColor.set(e.blendColor)),this.colorMask.set(e.mask);}createVertexArray(){var e;return si(this.gl)?this.gl.createVertexArray():null===(e=this.gl.getExtension("OES_vertex_array_object"))||void 0===e?void 0:e.createVertexArrayOES()}deleteVertexArray(e){var t;si(this.gl)?this.gl.deleteVertexArray(e):null===(t=this.gl.getExtension("OES_vertex_array_object"))||void 0===t||t.deleteVertexArrayOES(e);}unbindVAO(){this.bindVertexArray.set(null);}}let ha;function ua(e,i,o,a,r){var s,n;const l=e.context,c=e.transform,h=l.gl,u=e.useProgram("collisionBox"),d=[];let _=0,p=0;for(const t of a){const a=i.getTile(t).getBucket(o);if(!a)continue;const n=r?a.textCollisionBox:a.iconCollisionBox,m=a.collisionCircleArray;m.length>0&&(d.push({circleArray:m,circleOffset:p,coord:t}),_+=m.length/4,p=_),n&&u.draw(l,h.LINES,oi.disabled,ri.disabled,e.colorModeForRenderPass(),ii.disabled,Ki(e.transform),null===(s=e.style.map.terrain)||void 0===s?void 0:s.getTerrainData(t),c.getProjectionData({overscaledTileID:t,applyGlobeMatrix:!0,applyTerrainMatrix:!0}),o.id,n.layoutVertexBuffer,n.indexBuffer,n.segments,null,e.transform.zoom,null,null,n.collisionVertexBuffer);}if(!r||!d.length)return;const m=e.useProgram("collisionCircle"),f=new t.ca;f.resize(4*_),f._trim();let g=0;for(const e of d)for(let t=0;t=0&&(f[g.associatedIconIndex]={shiftedAnchor:E,angle:S});}else st(g.numGlyphs,p);}if(c){m.clear();const i=e.icon.placedSymbolArray;for(let e=0;ee.style.map.terrain.getElevation(l,t,i):null,s="map"===o.layout.get("text-rotation-alignment");$e(c,e,r,j,i,b,h,s,l.toUnwrapped(),v.width,v.height,Z,a);}const W=r&&M||V,q=y||W?da:b?j:e.transform.clipSpaceToPixelsMatrix,$=m&&0!==o.paint.get(r?"text-halo-width":"icon-halo-width").constantOr(1);let H;H=m?c.iconsInText?go(f.kind,z,w,b,y,W,e,q,N,Z,D,k,E):fo(f.kind,z,w,b,y,W,e,q,N,Z,r,D,$,E):mo(f.kind,z,w,b,y,W,e,q,N,Z,r,D,E);const X={program:S,buffers:u,uniformValues:H,projectionData:U,atlasTexture:A,atlasTextureIcon:B,atlasInterpolation:L,atlasInterpolationIcon:F,isSDF:m,hasHalo:$};if(T&&c.canOverlap){P=!0;const e=u.segments.get();for(const i of e)I.push({segments:new t.aV([i]),sortKey:i.sortKey,state:X,terrainData:R});}else I.push({segments:u.segments,sortKey:0,state:X,terrainData:R});}P&&I.sort(((e,t)=>e.sortKey-t.sortKey));const S=null!==(m=o.paint.get(r?"text-halo-width":"icon-halo-width").constantOr(null))&&void 0!==m?m:1/0,z=o.layout.get("text-letter-spacing").constantOr(0)*t.aJ<0||S>1;for(const t of I){const i=t.state;f.activeTexture.set(g.TEXTURE0),i.atlasTexture.bind(i.atlasInterpolation,g.CLAMP_TO_EDGE),i.atlasTextureIcon&&(f.activeTexture.set(g.TEXTURE1),i.atlasTextureIcon&&i.atlasTextureIcon.bind(i.atlasInterpolationIcon,g.CLAMP_TO_EDGE));const a=i.isSDF&&i.hasHalo;if(a){const a=i.uniformValues;a.u_is_halo=1,z&&(a.u_is_plain=0,va(i.buffers,t.segments,o,e,i.program,C,u,d,a,i.projectionData,t.terrainData),a.u_is_halo=0,a.u_is_plain=1);}va(i.buffers,t.segments,o,e,i.program,C,u,d,i.uniformValues,i.projectionData,t.terrainData),a&&!z&&(i.uniformValues.u_is_halo=0);}}function va(e,t,i,o,a,r,s,n,l,c,h){const u=o.context;a.draw(u,u.gl.TRIANGLES,r,s,n,ii.backCCW,l,h,c,i.id,e.layoutVertexBuffer,e.indexBuffer,t,i.paint,o.transform.zoom,e.programConfigurations.get(i.id),e.dynamicLayoutVertexBuffer,e.opacityVertexBuffer);}function xa(e,i,o,a,r){const s=e.context,n=s.gl,l=ri.disabled,c=new ei([n.ONE,n.ONE],t.bo.transparent,[!0,!0,!0,!0]),h=i.getBucket(o);if(!h)return;const u=a.key;let d=o.heatmapFbos.get(u);d||(d=ya(s,i.tileSize,i.tileSize),o.heatmapFbos.set(u,d)),s.bindFramebuffer.set(d.framebuffer),s.viewport.set([0,0,i.tileSize,i.tileSize]),s.clear({color:t.bo.transparent});const _=h.programConfigurations.get(o.id),p=e.useProgram("heatmap",_,!r),m=e.transform.getProjectionData({overscaledTileID:i.tileID,applyGlobeMatrix:!0,applyTerrainMatrix:!0}),f=e.style.map.terrain.getTerrainData(a);p.draw(s,n.TRIANGLES,oi.disabled,l,c,ii.disabled,Ji(i,e.transform.zoom,o.paint.get("heatmap-intensity"),1),f,m,o.id,h.layoutVertexBuffer,h.indexBuffer,h.segments,o.paint,e.transform.zoom,_);}function ba(e,t,i,o,a){const r=e.context,s=r.gl,n=e.transform;r.setColorMode(e.colorModeForRenderPass());const l=wa(r,t),c=i.key,h=t.heatmapFbos.get(c);if(!h)return;r.activeTexture.set(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,h.colorAttachment.get()),r.activeTexture.set(s.TEXTURE1),l.bind(s.LINEAR,s.CLAMP_TO_EDGE);const u=n.getProjectionData({overscaledTileID:i,applyTerrainMatrix:a,applyGlobeMatrix:!o});e.useProgram("heatmapTexture").draw(r,s.TRIANGLES,oi.disabled,ri.disabled,e.colorModeForRenderPass(),ii.disabled,eo(e,t,0,1),null,u,t.id,e.rasterBoundsBuffer,e.quadTriangleIndexBuffer,e.rasterBoundsSegments,t.paint,n.zoom),h.destroy(),t.heatmapFbos.delete(c);}function ya(e,t,i){var o,a;const r=e.gl,s=r.createTexture();r.bindTexture(r.TEXTURE_2D,s),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.LINEAR);const n=null!==(o=e.HALF_FLOAT)&&void 0!==o?o:r.UNSIGNED_BYTE,l=null!==(a=e.RGBA16F)&&void 0!==a?a:r.RGBA;r.texImage2D(r.TEXTURE_2D,0,l,t,i,0,r.RGBA,n,null);const c=e.createFramebuffer(t,i,!1,!1);return c.colorAttachment.set(s),c}function wa(e,i){return i.colorRampTexture||(i.colorRampTexture=new t.T(e,i.colorRamp,e.gl.RGBA)),i.colorRampTexture}function Ta(e,i,o,a,r,s,n,l){let c=256;if(r.stepInterpolant){const a=i.getSource().maxzoom,r=n.canonical.z===a?Math.ceil(1<e.options.anisotropicFilterPitch&&_.texParameterf(_.TEXTURE_2D,d.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT,d.extTextureFilterAnisotropicMax);const S=null===(h=e.style.map.terrain)||void 0===h?void 0:h.getTerrainData(T),z=m.getProjectionData({overscaledTileID:T,aligned:v,applyGlobeMatrix:!c,applyTerrainMatrix:!0}),R=_o(I,M,E.fadeMix,i,n),D=f.getMeshFromTileID(d,T.canonical,r,s,"raster");p.draw(d,_.TRIANGLES,o,a?a[T.overscaledZ]:ri.disabled,g,l?ii.frontCCW:ii.backCCW,R,S,z,i.id,D.vertexBuffer,D.indexBuffer,D.segments);}}function ka(e,i,o,a){const r={parentTile:null,parentScaleBy:1,parentTopLeft:[0,0],fadeValues:{tileOpacity:1,parentTileOpacity:1,fadeMix:{opacity:1,mix:0}}};if(0===o||a)return r;if(e.fadingParentID){const a=i.getLoadedTile(e.fadingParentID);if(!a)return r;const s=Math.pow(2,a.tileID.overscaledZ-e.tileID.overscaledZ),n=[e.tileID.canonical.x*s%1,e.tileID.canonical.y*s%1],l=function(e,i,o){const a=c(),r=(a-i.timeAdded)/o,s=e.fadingDirection===ue.Incoming,n=t.al((a-e.timeAdded)/o,0,1),l=t.al(1-r,0,1),h=s?n:l;return {tileOpacity:h,parentTileOpacity:s?l:n,fadeMix:{opacity:1,mix:1-h}}}(e,a,o);return {parentTile:a,parentScaleBy:s,parentTopLeft:n,fadeValues:l}}if(e.selfFading){const i=function(e,i){const o=(c()-e.timeAdded)/i,a=t.al(o,0,1);return {tileOpacity:a,fadeMix:{opacity:a,mix:0}}}(e,o);return {parentTile:null,parentScaleBy:1,parentTopLeft:[0,0],fadeValues:i}}return r}const Ba=new t.bo(1,0,0,1),Oa=new t.bo(0,1,0,1),ja=new t.bo(0,0,1,1),Na=new t.bo(1,0,1,1),Za=new t.bo(0,1,1,1);function Ua(e,t,i,o){Va(e,0,t+i/2,e.transform.width,i,o);}function Ga(e,t,i,o){Va(e,t-i/2,0,i,e.transform.height,o);}function Va(e,t,i,o,a,r){const s=e.context,n=s.gl;n.enable(n.SCISSOR_TEST),n.scissor(t*e.pixelRatio,i*e.pixelRatio,o*e.pixelRatio,a*e.pixelRatio),s.clear({color:r}),n.disable(n.SCISSOR_TEST);}function Wa(e,i,o){var a;const r=e.context,s=r.gl,n=e.useProgram("debug"),l=oi.disabled,c=ri.disabled,h=e.colorModeForRenderPass(),u="$debug",d=null===(a=e.style.map.terrain)||void 0===a?void 0:a.getTerrainData(o);r.activeTexture.set(s.TEXTURE0);const _=i.getTileByID(o.key).latestRawTileData,p=Math.floor(((null==_?void 0:_.byteLength)||0)/1024),m=i.getTile(o).tileSize,f=512/Math.min(m,512)*(o.overscaledZ/e.transform.zoom)*.5;let g=o.canonical.toString();o.overscaledZ!==o.canonical.z&&(g+=` => ${o.overscaledZ}`),function(e,t){e.initDebugOverlayCanvas();const i=e.debugOverlayCanvas,o=e.context.gl,a=e.debugOverlayCanvas.getContext("2d");a.clearRect(0,0,i.width,i.height),a.shadowColor="white",a.shadowBlur=2,a.lineWidth=1.5,a.strokeStyle="white",a.textBaseline="top",a.font="bold 36px Open Sans, sans-serif",a.fillText(t,5,5),a.strokeText(t,5,5),e.debugOverlayTexture.update(i),e.debugOverlayTexture.bind(o.LINEAR,o.CLAMP_TO_EDGE);}(e,`${g} ${p}kB`);const v=e.transform.getProjectionData({overscaledTileID:o,applyGlobeMatrix:!0,applyTerrainMatrix:!0});n.draw(r,s.TRIANGLES,l,c,ei.alphaBlended,ii.disabled,Qi(t.bo.transparent,f),null,v,u,e.debugBuffer,e.quadTriangleIndexBuffer,e.debugSegments),n.draw(r,s.LINE_STRIP,l,c,h,ii.disabled,Qi(t.bo.red),d,v,u,e.debugBuffer,e.tileBorderIndexBuffer,e.debugSegments);}function qa(e,t,i,o){const{isRenderingGlobe:a}=o,r=e.context,s=r.gl,n=e.transform,l=e.colorModeForRenderPass(),c=e.getDepthModeFor3D(),h=e.useProgram("terrain");r.bindFramebuffer.set(null),r.viewport.set([0,0,e.width,e.height]);for(const o of i){const i=t.getTerrainMesh(o.tileID),u=e.renderToTexture.getTexture(o),d=t.getTerrainData(o.tileID);r.activeTexture.set(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,u.texture);const _=t.getMeshFrameDelta(n.zoom),p=n.calculateFogMatrix(o.tileID.toUnwrapped()),m=ji(_,p,e.style.sky,n.pitch,a),f=n.getProjectionData({overscaledTileID:o.tileID,applyTerrainMatrix:!1,applyGlobeMatrix:!0});h.draw(r,s.TRIANGLES,c,ri.disabled,l,ii.backCCW,m,d,f,"terrain",i.vertexBuffer,i.indexBuffer,i.segments);}}function $a(e,i){if(!i.mesh){const o=new t.aU;o.emplaceBack(-1,-1),o.emplaceBack(1,-1),o.emplaceBack(1,1),o.emplaceBack(-1,1);const a=new t.aW;a.emplaceBack(0,1,2),a.emplaceBack(0,2,3),i.mesh=new Bt(e.createVertexBuffer(o,Ot.members),e.createIndexBuffer(a),t.aV.simpleSegment(0,0,o.length,a.length));}return i.mesh}const Ha={symbol:function(e,i,o,a,r,s){if("translucent"!==e.renderPass)return;const{isRenderingToTexture:n}=s,l=ri.disabled,c=e.colorModeForRenderPass();(o._unevaluatedLayout.hasValue("text-variable-anchor")||o._unevaluatedLayout.hasValue("text-variable-anchor-offset"))&&function(e,i,o,a,r,s,n,l,c){var h;const u=i.transform,d=i.style.map.terrain,_="map"===r,p="map"===s;for(const r of e){const e=a.getTile(r),s=e.getBucket(o);if(!(null===(h=null==s?void 0:s.text)||void 0===h?void 0:h.segments.get().length))continue;const m=t.aw(s.textSizeData,u.zoom),f=t.aK(e,1,i.transform.zoom),g=Ze(_,i.transform,f),v="none"!==o.layout.get("icon-text-fit")&&s.hasIconData();if(m){const i=Math.pow(2,u.zoom-e.tileID.overscaledZ),o=d?(e,t)=>d.getElevation(r,e,t):null;ma(s,_,p,c,u,g,i,m,v,t.aL(u,e,n,l),r.toUnwrapped(),o);}}}(a,e,o,i,o.layout.get("text-rotation-alignment"),o.layout.get("text-pitch-alignment"),o.paint.get("text-translate"),o.paint.get("text-translate-anchor"),r),0!==o.paint.get("icon-opacity").constantOr(1)&&ga(e,i,o,a,!1,o.paint.get("icon-translate"),o.paint.get("icon-translate-anchor"),o.layout.get("icon-rotation-alignment"),o.layout.get("icon-pitch-alignment"),o.layout.get("icon-keep-upright"),l,c,n),0!==o.paint.get("text-opacity").constantOr(1)&&ga(e,i,o,a,!0,o.paint.get("text-translate"),o.paint.get("text-translate-anchor"),o.layout.get("text-rotation-alignment"),o.layout.get("text-pitch-alignment"),o.layout.get("text-keep-upright"),l,c,n),i.map.showCollisionBoxes&&(ua(e,i,o,a,!0),ua(e,i,o,a,!1));},circle:function(e,i,o,a,r){var s;if("translucent"!==e.renderPass)return;const{isRenderingToTexture:n}=r,l=o.paint.get("circle-opacity"),c=o.paint.get("circle-stroke-width"),h=o.paint.get("circle-stroke-opacity"),u=!o.layout.get("circle-sort-key").isConstant();if(0===l.constantOr(1)&&(0===c.constantOr(1)||0===h.constantOr(1)))return;const d=e.context,_=d.gl,p=e.transform,m=e.getDepthModeForSublayer(0,oi.ReadOnly),f=ri.disabled,g=e.colorModeForRenderPass(),v=[],x=p.getCircleRadiusCorrection();for(const r of a){const a=i.getTile(r),l=a.getBucket(o);if(!l)continue;const c=o.paint.get("circle-translate"),h=o.paint.get("circle-translate-anchor"),d=t.aL(p,a,c,h),_=l.programConfigurations.get(o.id),m=e.useProgram("circle",_),f=l.layoutVertexBuffer,g=l.indexBuffer,b=null===(s=e.style.map.terrain)||void 0===s?void 0:s.getTerrainData(r),y={programConfiguration:_,program:m,layoutVertexBuffer:f,indexBuffer:g,uniformValues:Xi(e,a,o,d,x),terrainData:b,projectionData:p.getProjectionData({overscaledTileID:r,applyGlobeMatrix:!n,applyTerrainMatrix:!0})};if(u){const e=l.segments.get();for(const i of e)v.push({segments:new t.aV([i]),sortKey:i.sortKey,state:y});}else v.push({segments:l.segments,sortKey:0,state:y});}u&&v.sort(((e,t)=>e.sortKey-t.sortKey));for(const t of v){const{programConfiguration:i,program:a,layoutVertexBuffer:r,indexBuffer:s,uniformValues:n,terrainData:l,projectionData:c}=t.state;a.draw(d,_.TRIANGLES,m,f,g,ii.backCCW,n,l,c,o.id,r,s,t.segments,o.paint,e.transform.zoom,i);}},heatmap:function(e,i,o,a,r){if(0===o.paint.get("heatmap-opacity"))return;const s=e.context,{isRenderingToTexture:n,isRenderingGlobe:l}=r;if(e.style.map.terrain){for(const t of a){const a=i.getTile(t);i.hasRenderableParent(t)||("offscreen"===e.renderPass?xa(e,a,o,t,l):"translucent"===e.renderPass&&ba(e,o,t,n,l));}s.viewport.set([0,0,e.width,e.height]);}else "offscreen"===e.renderPass?function(e,i,o,a){const r=e.context,s=r.gl,n=e.transform,l=ri.disabled,c=new ei([s.ONE,s.ONE],t.bo.transparent,[!0,!0,!0,!0]);((function(e,i,o){const a=e.gl;e.activeTexture.set(a.TEXTURE1),e.viewport.set([0,0,i.width/4,i.height/4]);let r=o.heatmapFbos.get(t.cd);r?(a.bindTexture(a.TEXTURE_2D,r.colorAttachment.get()),e.bindFramebuffer.set(r.framebuffer)):(r=ya(e,i.width/4,i.height/4),o.heatmapFbos.set(t.cd,r));}))(r,e,o),r.clear({color:t.bo.transparent});for(const t of a){if(i.hasRenderableParent(t))continue;const a=i.getTile(t),h=a.getBucket(o);if(!h)continue;const u=h.programConfigurations.get(o.id),d=e.useProgram("heatmap",u),_=n.getProjectionData({overscaledTileID:t,applyGlobeMatrix:!0,applyTerrainMatrix:!1}),p=n.getCircleRadiusCorrection();d.draw(r,s.TRIANGLES,oi.disabled,l,c,ii.backCCW,Ji(a,n.zoom,o.paint.get("heatmap-intensity"),p),null,_,o.id,h.layoutVertexBuffer,h.indexBuffer,h.segments,o.paint,n.zoom,u);}r.viewport.set([0,0,e.width,e.height]);}(e,i,o,a):"translucent"===e.renderPass&&function(e,i){const o=e.context,a=o.gl;o.setColorMode(e.colorModeForRenderPass());const r=i.heatmapFbos.get(t.cd);r&&(o.activeTexture.set(a.TEXTURE0),a.bindTexture(a.TEXTURE_2D,r.colorAttachment.get()),o.activeTexture.set(a.TEXTURE1),wa(o,i).bind(a.LINEAR,a.CLAMP_TO_EDGE),e.useProgram("heatmapTexture").draw(o,a.TRIANGLES,oi.disabled,ri.disabled,e.colorModeForRenderPass(),ii.disabled,eo(e,i,0,1),null,null,i.id,e.viewportBuffer,e.quadTriangleIndexBuffer,e.viewportSegments,i.paint,e.transform.zoom));}(e,o);},line:function(e,t,i,o,a){var r;if("translucent"!==e.renderPass)return;const{isRenderingToTexture:s}=a,n=i.paint.get("line-opacity"),l=i.paint.get("line-width");if(0===n.constantOr(1)||0===l.constantOr(1))return;const c=e.getDepthModeForSublayer(0,oi.ReadOnly),h=e.colorModeForRenderPass(),u=i.paint.get("line-dasharray"),d=u.constantOr(1),_=i.paint.get("line-pattern"),p=_.constantOr(1),m=i.paint.get("line-gradient"),f=i.getCrossfadeParameters();let g;g=p?"linePattern":d&&m?"lineGradientSDF":d?"lineSDF":m?"lineGradient":"line";const v=e.context,x=v.gl,b=e.transform;let y=!0;for(const a of o){const o=t.getTile(a);if(p&&!o.patternsLoaded())continue;const n=o.getBucket(i);if(!n)continue;const l=n.programConfigurations.get(i.id),w=e.context.program.get(),T=e.useProgram(g,l),P=y||T.program!==w,C=null===(r=e.style.map.terrain)||void 0===r?void 0:r.getTerrainData(a),M=_.constantOr(null),I=null==u?void 0:u.constantOr(null);if(M&&o.imageAtlas){const e=o.imageAtlas,t=e.patternPositions[M.to.toString()],i=e.patternPositions[M.from.toString()];t&&i&&l.setConstantPatternPositions(t,i);}else if(I){const t="round"===i.layout.get("line-cap").constantOr(null),o=e.lineAtlas.getDash(I.to,t),a=e.lineAtlas.getDash(I.from,t);l.setConstantDashPositions(o,a);}const E=b.getProjectionData({overscaledTileID:a,applyGlobeMatrix:!s,applyTerrainMatrix:!0}),S=b.getPixelScale();let z;p?(z=no(e,o,i,S,f),Pa(v,x,o,l,f)):d&&m?(z=co(e,o,i,S,f,n.lineClipsArray.length),Ia(e,t,v,x,i,n,a,l,f)):d?(z=lo(e,o,i,S,f),Ca(e,v,x,l,P,f)):m?(z=so(e,o,i,S,n.lineClipsArray.length),Ma(e,t,v,x,i,n,a)):z=ro(e,o,i,S);const R=e.stencilModeForClipping(a);T.draw(v,x.TRIANGLES,c,R,h,ii.disabled,z,C,E,i.id,n.layoutVertexBuffer,n.indexBuffer,n.segments,i.paint,e.transform.zoom,l,n.layoutVertexBuffer2),y=!1;}},fill:function(e,i,o,a,r){const s=o.paint.get("fill-color"),n=o.paint.get("fill-opacity");if(0===n.constantOr(1))return;const{isRenderingToTexture:l}=r,c=e.colorModeForRenderPass(),h=o.paint.get("fill-pattern"),u=e.opaquePassEnabledForLayer()&&!h.constantOr(1)&&1===s.constantOr(t.bo.transparent).a&&1===n.constantOr(0)?"opaque":"translucent";if(e.renderPass===u){const t=e.getDepthModeForSublayer(1,"opaque"===e.renderPass?oi.ReadWrite:oi.ReadOnly);Sa(e,i,o,a,t,c,!1,l);}if("translucent"===e.renderPass&&o.paint.get("fill-antialias")){const t=e.getDepthModeForSublayer(o.getPaintProperty("fill-outline-color")?2:0,oi.ReadOnly);Sa(e,i,o,a,t,c,!0,l);}},fillExtrusion:function(e,t,i,o,a){const r=i.paint.get("fill-extrusion-opacity");if(0===r)return;const{isRenderingToTexture:s}=a;if("translucent"===e.renderPass){const a=new oi(e.context.gl.LEQUAL,oi.ReadWrite,e.depthRangeFor3D);if(1!==r||i.paint.get("fill-extrusion-pattern").constantOr(1))za(e,t,i,o,a,ri.disabled,ei.disabled,s),za(e,t,i,o,a,e.stencilModeFor3D(),e.colorModeForRenderPass(),s);else {const r=e.colorModeForRenderPass();za(e,t,i,o,a,ri.disabled,r,s);}}},hillshade:function(e,i,o,a,r){if("offscreen"!==e.renderPass&&"translucent"!==e.renderPass)return;const{isRenderingToTexture:s}=r,n=e.context,l=e.style.projection.useSubdivision,c=e.getDepthModeForSublayer(0,oi.ReadOnly),h=e.colorModeForRenderPass();if("offscreen"===e.renderPass)!function(e,i,o,a,r,s,n){const l=e.context,c=l.gl,h="nearest"===a.paint.get("resampling")?c.NEAREST:c.LINEAR;for(const u of o){const o=i.getTile(u),d=o.dem;if(!(null==d?void 0:d.data))continue;if(!o.needsHillshadePrepare)continue;const _=d.dim,p=d.stride,m=d.getPixels();if(l.activeTexture.set(c.TEXTURE1),l.pixelStoreUnpackPremultiplyAlpha.set(!1),o.demTexture||(o.demTexture=e.getTileTexture(p)),o.demTexture){const e=o.demTexture;e.update(m,{premultiply:!1}),e.bind(c.NEAREST,c.CLAMP_TO_EDGE);}else o.demTexture=new t.T(l,m,c.RGBA,{premultiply:!1}),o.demTexture.bind(c.NEAREST,c.CLAMP_TO_EDGE);l.activeTexture.set(c.TEXTURE0);let f=o.fbo;if(!f){const e=new t.T(l,{width:_,height:_,data:null},c.RGBA);e.bind(h,c.CLAMP_TO_EDGE),f=o.fbo=l.createFramebuffer(_,_,!0,!1),f.colorAttachment.set(e.texture);}l.bindFramebuffer.set(f.framebuffer),l.viewport.set([0,0,_,_]),e.useProgram("hillshadePrepare").draw(l,c.TRIANGLES,r,s,n,ii.disabled,io(o.tileID,d),null,null,a.id,e.rasterBoundsBuffer,e.quadTriangleIndexBuffer,e.rasterBoundsSegments),o.needsHillshadePrepare=!1;}}(e,i,a,o,c,ri.disabled,h),n.viewport.set([0,0,e.width,e.height]);else if("translucent"===e.renderPass)if(l){const[t,r,n]=e.stencilConfigForOverlapTwoPass(a);Ra(e,i,o,n,t,c,h,!1,s),Ra(e,i,o,n,r,c,h,!0,s);}else {const[t,r]=e.getStencilConfigForOverlapAndUpdateStencilID(a);Ra(e,i,o,r,t,c,h,!1,s);}},colorRelief:function(e,t,i,o,a){if("translucent"!==e.renderPass)return;if(!o.length)return;const{isRenderingToTexture:r}=a,s=e.style.projection.useSubdivision,n=e.getDepthModeForSublayer(0,oi.ReadOnly),l=e.colorModeForRenderPass();if(s){const[a,s,c]=e.stencilConfigForOverlapTwoPass(o);Aa(e,t,i,c,a,n,l,!1,r),Aa(e,t,i,c,s,n,l,!0,r);}else {const[a,s]=e.getStencilConfigForOverlapAndUpdateStencilID(o);Aa(e,t,i,s,a,n,l,!1,r);}},raster:function(e,t,i,o,a){if("translucent"!==e.renderPass)return;if(0===i.paint.get("raster-opacity"))return;if(!o.length)return;const{isRenderingToTexture:r}=a,s=t.getSource(),n=e.style.projection.useSubdivision;if(s instanceof te)Fa(e,t,i,o,null,!1,!1,s.tileCoords,s.flippedWindingOrder,r);else if(n){const[a,s,n]=e.stencilConfigForOverlapTwoPass(o);Fa(e,t,i,n,a,!1,!0,La,!1,r),Fa(e,t,i,n,s,!0,!0,La,!1,r);}else {const[a,s]=e.getStencilConfigForOverlapAndUpdateStencilID(o);Fa(e,t,i,s,a,!1,!0,La,!1,r);}},background:function(e,t,i,o,a){var r;const s=i.paint.get("background-color"),n=i.paint.get("background-opacity");if(0===n)return;const{isRenderingToTexture:l}=a,c=e.context,h=c.gl,u=e.style.projection,d=e.transform,_=d.tileSize,p=i.paint.get("background-pattern");if(e.isPatternMissing(p))return;const m=!p&&1===s.a&&1===n&&e.opaquePassEnabledForLayer()?"opaque":"translucent";if(e.renderPass!==m)return;const f=ri.disabled,g=e.getDepthModeForSublayer(0,"opaque"===m?oi.ReadWrite:oi.ReadOnly),v=e.colorModeForRenderPass(),x=e.useProgram(p?"backgroundPattern":"background"),b=o||Me(d,{tileSize:_,terrain:e.style.map.terrain});p&&(c.activeTexture.set(h.TEXTURE0),e.imageManager.bind(e.context));const y=i.getCrossfadeParameters();for(const t of b){const o=d.getProjectionData({overscaledTileID:t,applyGlobeMatrix:!l,applyTerrainMatrix:!0}),a=p?xo(n,e,p,{tileID:t,tileSize:_},y):vo(n,s),m=null===(r=e.style.map.terrain)||void 0===r?void 0:r.getTerrainData(t),b=u.getMeshFromTileID(c,t.canonical,!1,!0,"raster");x.draw(c,h.TRIANGLES,g,f,v,ii.backCCW,a,m,o,i.id,b.vertexBuffer,b.indexBuffer,b.segments);}},sky:function(e,t){const i=e.context,o=i.gl,a=((e,t,i)=>{const o=Math.cos(t.rollInRadians),a=Math.sin(t.rollInRadians),r=ge(t),s=t.getProjectionData({overscaledTileID:null,applyGlobeMatrix:!0,applyTerrainMatrix:!0}).projectionTransition;return {u_sky_color:e.properties.get("sky-color"),u_horizon_color:e.properties.get("horizon-color"),u_horizon:[(t.width/2-r*a)*i,(t.height/2+r*o)*i],u_horizon_normal:[-a,o],u_sky_horizon_blend:e.properties.get("sky-horizon-blend")*t.height/2*i,u_sky_blend:s}})(t,e.style.map.transform,e.pixelRatio),r=new oi(o.LEQUAL,oi.ReadWrite,[0,1]),s=ri.disabled,n=e.colorModeForRenderPass(),l=e.useProgram("sky"),c=$a(i,t);l.draw(i,o.TRIANGLES,r,s,n,ii.disabled,a,null,void 0,"sky",c.vertexBuffer,c.indexBuffer,c.segments);},atmosphere:function(e,i,o){const a=e.context,r=a.gl,s=e.useProgram("atmosphere"),n=new oi(r.LEQUAL,oi.ReadOnly,[0,1]),l=e.transform,c=function(e,i){const o=e.properties.get("position"),a=[-o.x,-o.y,-o.z],r=t.ap(new Float64Array(16));return "map"===e.properties.get("anchor")&&(t.be(r,r,i.rollInRadians),t.bf(r,r,-i.pitchInRadians),t.be(r,r,i.bearingInRadians),t.bf(r,r,i.center.lat*Math.PI/180),t.bI(r,r,-i.center.lng*Math.PI/180)),t.cg(a,a,r),a}(o,e.transform),h=l.getProjectionData({overscaledTileID:null,applyGlobeMatrix:!0,applyTerrainMatrix:!0}),u=i.properties.get("atmosphere-blend")*h.projectionTransition;if(0===u)return;const d=gi(l.worldSize,l.center.lat),_=l.inverseProjectionMatrix,p=new Float64Array(4);p[3]=1,t.aE(p,p,l.modelViewProjectionMatrix),p[0]/=p[3],p[1]/=p[3],p[2]/=p[3],p[3]=1,t.aE(p,p,_),p[0]/=p[3],p[1]/=p[3],p[2]/=p[3],p[3]=1;const m=((e,t,i,o,a)=>({u_sun_pos:e,u_atmosphere_blend:t,u_globe_position:i,u_globe_radius:o,u_inv_proj_matrix:a}))(c,u,[p[0],p[1],p[2]],d,_),f=$a(a,i);s.draw(a,r.TRIANGLES,n,ri.disabled,ei.alphaBlended,ii.disabled,m,null,null,"atmosphere",f.vertexBuffer,f.indexBuffer,f.segments);},custom:function(e,t,i,o){const{isRenderingGlobe:a}=o,r=e.context,s=i.implementation,n=e.style.projection,l=e.transform,c=l.getProjectionDataForCustomLayer(a),h={farZ:l.farZ,nearZ:l.nearZ,fov:l.fov*Math.PI/180,modelViewProjectionMatrix:l.modelViewProjectionMatrix,projectionMatrix:l.projectionMatrix,shaderData:{variantName:n.shaderVariantName,vertexShaderPrelude:`const float PI = 3.141592653589793;\nuniform mat4 u_projection_matrix;\n${n.shaderPreludeCode.vertexSource}`,define:n.shaderDefine},defaultProjectionData:c},u=s.renderingMode?s.renderingMode:"2d";if("offscreen"===e.renderPass){const t=s.prerender;t&&(e.setCustomLayerDefaults(),r.setColorMode(e.colorModeForRenderPass()),t.call(s,r.gl,h),r.setDirty(),e.setBaseState());}else if("translucent"===e.renderPass){e.setCustomLayerDefaults(),r.setColorMode(e.colorModeForRenderPass()),r.setStencilMode(ri.disabled);const t="3d"===u?e.getDepthModeFor3D():e.getDepthModeForSublayer(0,oi.ReadOnly);r.setDepthMode(t),s.render(r.gl,h),r.setDirty(),e.setBaseState(),r.bindFramebuffer.set(null);}},debug:function(e,t,i){for(const o of i)Wa(e,t,o);},debugPadding:function(e){const t=e.transform.padding;Ua(e,e.transform.height-(t.top||0),3,Ba),Ua(e,t.bottom||0,3,Oa),Ga(e,t.left||0,3,ja),Ga(e,e.transform.width-(t.right||0),3,Na);const i=e.transform.centerPoint;!function(e,t,i,o){Va(e,t-1,i-10,2,20,o),Va(e,t-10,i-1,20,2,o);}(e,i.x,e.transform.height-i.y,Za);},terrainDepth:function(e,i){const o=e.context,a=o.gl,r=e.transform,s=ei.unblended,n=new oi(a.LEQUAL,oi.ReadWrite,[0,1]),l=i.tileManager.getRenderableTiles(),c=e.useProgram("terrainDepth");o.bindFramebuffer.set(i.getFramebuffer("depth").framebuffer),o.viewport.set([0,0,e.width/devicePixelRatio,e.height/devicePixelRatio]),o.clear({color:t.bo.transparent,depth:1});for(const e of l){const t=i.getTerrainMesh(e.tileID),l=i.getTerrainData(e.tileID),h=r.getProjectionData({overscaledTileID:e.tileID,applyTerrainMatrix:!1,applyGlobeMatrix:!0}),u={u_ele_delta:i.getMeshFrameDelta(r.zoom)};c.draw(o,a.TRIANGLES,n,ri.disabled,s,ii.backCCW,u,l,h,"terrain",t.vertexBuffer,t.indexBuffer,t.segments);}o.bindFramebuffer.set(null),o.viewport.set([0,0,e.width,e.height]);},terrainCoords:function(e,i){const o=e.context,a=o.gl,r=e.transform,s=ei.unblended,n=new oi(a.LEQUAL,oi.ReadWrite,[0,1]),l=i.getCoordsTexture(),c=i.tileManager.getRenderableTiles(),h=e.useProgram("terrainCoords");o.bindFramebuffer.set(i.getFramebuffer("coords").framebuffer),o.viewport.set([0,0,e.width/devicePixelRatio,e.height/devicePixelRatio]),o.clear({color:t.bo.transparent,depth:1}),i.coordsIndex=[];for(const e of c){const t=i.getTerrainMesh(e.tileID),c=i.getTerrainData(e.tileID);o.activeTexture.set(a.TEXTURE0),a.bindTexture(a.TEXTURE_2D,l.texture);const u={u_terrain_coords_id:(255-i.coordsIndex.length)/255,u_texture:0,u_ele_delta:i.getMeshFrameDelta(r.zoom)},d=r.getProjectionData({overscaledTileID:e.tileID,applyTerrainMatrix:!1,applyGlobeMatrix:!0});h.draw(o,a.TRIANGLES,n,ri.disabled,s,ii.backCCW,u,c,d,"terrain",t.vertexBuffer,t.indexBuffer,t.segments),i.coordsIndex.push(e.tileID.key);}o.bindFramebuffer.set(null),o.viewport.set([0,0,e.width,e.height]);}};class Xa{constructor(e,i){this.drawFunctions=Ha,this.context=new ca(e),this.transform=i,this._tileTextures={},this.terrainFacilitator={depthDirty:!0,coordsDirty:!1,matrix:t.ap(new Float64Array(16)),renderTime:0},this.setup(),this.numSublayers=Fe.maxOverzooming+Fe.maxUnderzooming+1,this.depthEpsilon=1/Math.pow(2,16),this.crossTileSymbolIndex=new At;}resize(e,t,i){if(this.width=Math.floor(e*i),this.height=Math.floor(t*i),this.pixelRatio=i,this.context.viewport.set([0,0,this.width,this.height]),this.style)for(const e of this.style._order)this.style._layers[e].resize();}setup(){const e=this.context,i=new t.aU;i.emplaceBack(0,0),i.emplaceBack(t.a6,0),i.emplaceBack(0,t.a6),i.emplaceBack(t.a6,t.a6),this.tileExtentBuffer=e.createVertexBuffer(i,Ot.members),this.tileExtentSegments=t.aV.simpleSegment(0,0,4,2);const o=new t.aU;o.emplaceBack(0,0),o.emplaceBack(t.a6,0),o.emplaceBack(0,t.a6),o.emplaceBack(t.a6,t.a6),this.debugBuffer=e.createVertexBuffer(o,Ot.members),this.debugSegments=t.aV.simpleSegment(0,0,4,5);const a=new t.ch;a.emplaceBack(0,0,0,0),a.emplaceBack(t.a6,0,t.a6,0),a.emplaceBack(0,t.a6,0,t.a6),a.emplaceBack(t.a6,t.a6,t.a6,t.a6),this.rasterBoundsBuffer=e.createVertexBuffer(a,Bi.members),this.rasterBoundsSegments=t.aV.simpleSegment(0,0,4,2);const r=new t.aU;r.emplaceBack(0,0),r.emplaceBack(t.a6,0),r.emplaceBack(0,t.a6),r.emplaceBack(t.a6,t.a6),this.rasterBoundsBufferPosOnly=e.createVertexBuffer(r,Ot.members),this.rasterBoundsSegmentsPosOnly=t.aV.simpleSegment(0,0,4,5);const s=new t.aU;s.emplaceBack(0,0),s.emplaceBack(1,0),s.emplaceBack(0,1),s.emplaceBack(1,1),this.viewportBuffer=e.createVertexBuffer(s,Ot.members),this.viewportSegments=t.aV.simpleSegment(0,0,4,2);const n=new t.ci;n.emplaceBack(0),n.emplaceBack(1),n.emplaceBack(3),n.emplaceBack(2),n.emplaceBack(0),this.tileBorderIndexBuffer=e.createIndexBuffer(n);const l=new t.aW;l.emplaceBack(1,0,2),l.emplaceBack(1,2,3),this.quadTriangleIndexBuffer=e.createIndexBuffer(l);const c=this.context.gl;this.stencilClearMode=new ri({func:c.ALWAYS,mask:0},0,255,c.ZERO,c.ZERO,c.ZERO),this.tileExtentMesh=new Bt(this.tileExtentBuffer,this.quadTriangleIndexBuffer,this.tileExtentSegments);}clearStencil(){const e=this.context,i=e.gl;this.nextStencilID=1,this.currentStencilSource=void 0;const o=t.O();t.c7(o,0,this.width,this.height,0,0,1),t.S(o,o,[i.drawingBufferWidth,i.drawingBufferHeight,0]);const a={mainMatrix:o,tileMercatorCoords:[0,0,1,1],clippingPlane:[0,0,0,0],projectionTransition:0,fallbackMatrix:o};this.useProgram("clippingMask",null,!0).draw(e,i.TRIANGLES,oi.disabled,this.stencilClearMode,ei.disabled,ii.disabled,null,null,a,"$clipping",this.viewportBuffer,this.quadTriangleIndexBuffer,this.viewportSegments);}_renderTileClippingMasks(e,t,i){if(this.currentStencilSource===e.source||!e.isTileClipped()||!(null==t?void 0:t.length))return;this.currentStencilSource=e.source,this.nextStencilID+t.length>256&&this.clearStencil();const o=this.context;o.setColorMode(ei.disabled),o.setDepthMode(oi.disabled);const a={};for(const e of t)a[e.key]=this.nextStencilID++;this._renderTileMasks(a,t,i,!0),this._renderTileMasks(a,t,i,!1),this._tileClippingMaskIDs=a;}_renderTileMasks(e,t,i,o){var a;const r=this.context,s=r.gl,n=this.style.projection,l=this.transform,c=this.useProgram("clippingMask");for(const h of t){const t=e[h.key],u=null===(a=this.style.map.terrain)||void 0===a?void 0:a.getTerrainData(h),d=n.getMeshFromTileID(this.context,h.canonical,o,!0,"stencil"),_=l.getProjectionData({overscaledTileID:h,applyGlobeMatrix:!i,applyTerrainMatrix:!0});c.draw(r,s.TRIANGLES,oi.disabled,new ri({func:s.ALWAYS,mask:0},t,255,s.KEEP,s.KEEP,s.REPLACE),ei.disabled,i?ii.disabled:ii.backCCW,null,u,_,"$clipping",d.vertexBuffer,d.indexBuffer,d.segments);}}_renderTilesDepthBuffer(){var e;const t=this.context,i=t.gl,o=this.style.projection,a=this.transform,r=this.useProgram("depth"),s=this.getDepthModeFor3D(),n=Me(a,{tileSize:a.tileSize});for(const l of n){const n=null===(e=this.style.map.terrain)||void 0===e?void 0:e.getTerrainData(l),c=o.getMeshFromTileID(this.context,l.canonical,!0,!0,"raster"),h=a.getProjectionData({overscaledTileID:l,applyGlobeMatrix:!0,applyTerrainMatrix:!0});r.draw(t,i.TRIANGLES,s,ri.disabled,ei.disabled,ii.backCCW,null,n,h,"$clipping",c.vertexBuffer,c.indexBuffer,c.segments);}}stencilModeFor3D(){this.currentStencilSource=void 0,this.nextStencilID+1>256&&this.clearStencil();const e=this.nextStencilID++,t=this.context.gl;return new ri({func:t.NOTEQUAL,mask:255},e,255,t.KEEP,t.KEEP,t.REPLACE)}stencilModeForClipping(e){const t=this.context.gl;return new ri({func:t.EQUAL,mask:255},this._tileClippingMaskIDs[e.key],0,t.KEEP,t.KEEP,t.REPLACE)}getStencilConfigForOverlapAndUpdateStencilID(e){const t=this.context.gl,i=e.sort(((e,t)=>t.overscaledZ-e.overscaledZ)),o=i[i.length-1].overscaledZ,a=i[0].overscaledZ-o+1;if(a>1){this.currentStencilSource=void 0,this.nextStencilID+a>256&&this.clearStencil();const e={};for(let i=0;it.overscaledZ-e.overscaledZ)),o=i[i.length-1].overscaledZ,a=i[0].overscaledZ-o+1;if(this.clearStencil(),a>1){const e={},r={};for(let i=0;i0};for(const e in n){const t=n[e];t.used&&t.prepare(this.context),l[e]=t.getVisibleCoordinates(!1),h[e]=l[e].slice().reverse(),u[e]=t.getVisibleCoordinates(!0).reverse();}this.opaquePassCutoff=1/0;for(let e=0;ethis.useProgram(e)}),this.context.viewport.set([0,0,this.width,this.height]),this.context.bindFramebuffer.set(null),this.context.clear({color:i.showOverdrawInspector?t.bo.black:t.bo.transparent,depth:1}),this.clearStencil(),this.style.sky&&this.drawFunctions.sky(this,this.style.sky),this._showOverdrawInspector=i.showOverdrawInspector,this.depthRangeFor3D=[0,1-(e._order.length+2)*this.numSublayers*this.depthEpsilon],!this.renderToTexture)for(this.renderPass="opaque",this.currentLayer=s.length-1;this.currentLayer>=0;this.currentLayer--){const e=this.style._layers[s[this.currentLayer]],t=n[e.source],i=l[e.source];this._renderTileClippingMasks(e,i,!1),this.renderLayer(this,t,e,i,d);}this.renderPass="translucent";let _=!1;for(this.currentLayer=0;this.currentLayeri.source&&!i.isHidden(t)?[e.tileManagers[i.source]]:[])),a=o.filter((e=>"vector"===e.getSource().type)),r=o.filter((e=>"vector"!==e.getSource().type)),s=e=>{(!i||i.getSource().maxzoom0?t.pop():null}isPatternMissing(e){if(!e)return !1;if(!e.from||!e.to)return !0;const t=this.imageManager.getPattern(e.from.toString()),i=this.imageManager.getPattern(e.to.toString());return !t||!i}useProgram(e,t,i=!1,o=[]){var a;this.cache||(this.cache={});const r=!!this.style.map.terrain,s=this.style.projection,n=i?Ft.projectionMercator:s.shaderPreludeCode,l=i?jt:s.shaderDefine,c=e+(t?t.cacheKey:"")+`/${i?Nt:s.shaderVariantName}`+(this._showOverdrawInspector?"/overdraw":"")+(r?"/terrain":"")+(o?`/${o.join("/")}`:"");return (a=this.cache)[c]||(a[c]=new Ui(this.context,Ft[e],t,yo[e],this._showOverdrawInspector,r,n,l,o)),this.cache[c]}setCustomLayerDefaults(){this.context.unbindVAO(),this.context.cullFace.setDefault(),this.context.activeTexture.setDefault(),this.context.pixelStoreUnpack.setDefault(),this.context.pixelStoreUnpackPremultiplyAlpha.setDefault(),this.context.pixelStoreUnpackFlipY.setDefault();}setBaseState(){const e=this.context.gl;this.context.cullFace.set(!1),this.context.viewport.set([0,0,this.width,this.height]),this.context.blendEquation.set(e.FUNC_ADD);}initDebugOverlayCanvas(){null==this.debugOverlayCanvas&&(this.debugOverlayCanvas=document.createElement("canvas"),this.debugOverlayCanvas.width=512,this.debugOverlayCanvas.height=512,this.debugOverlayTexture=new t.T(this.context,this.debugOverlayCanvas,this.context.gl.RGBA));}destroy(){var e,t;if(this._tileTextures){for(const e in this._tileTextures){const t=this._tileTextures[e];if(t)for(const e of t)e.destroy();}this._tileTextures={};}if(this.tileExtentBuffer&&this.tileExtentBuffer.destroy(),this.debugBuffer&&this.debugBuffer.destroy(),this.rasterBoundsBuffer&&this.rasterBoundsBuffer.destroy(),this.rasterBoundsBufferPosOnly&&this.rasterBoundsBufferPosOnly.destroy(),this.viewportBuffer&&this.viewportBuffer.destroy(),this.tileBorderIndexBuffer&&this.tileBorderIndexBuffer.destroy(),this.quadTriangleIndexBuffer&&this.quadTriangleIndexBuffer.destroy(),this.tileExtentMesh&&(null===(e=this.tileExtentMesh.vertexBuffer)||void 0===e||e.destroy()),this.tileExtentMesh&&(null===(t=this.tileExtentMesh.indexBuffer)||void 0===t||t.destroy()),this.debugOverlayTexture&&this.debugOverlayTexture.destroy(),this.cache){for(const e in this.cache){const t=this.cache[e];(null==t?void 0:t.program)&&this.context.gl.deleteProgram(t.program);}this.cache={};}this.context&&this.context.setDefault();}overLimit(){const{drawingBufferWidth:e,drawingBufferHeight:t}=this.context.gl;return this.width!==e||this.height!==t}}function Ka(e,t){let i,o=!1,a=null;const r=()=>{a=null,o&&(e(...i),a=setTimeout(r,t),o=!1);};return (...e)=>(o=!0,i=e,a||r(),a)}Xa.MAX_TEXTURE_POOL_SIZE_PER_BUCKET=50;class Ya{constructor(e){this._getCurrentHash=()=>{const e=window.location.hash.replace("#","");if(this._hashName){let t;const i=e.split("&").map((e=>e.split("=")));for(const e of i)e[0]===this._hashName&&(t=e);return (t&&t[1]||"").split("/")}return e.split("/")},this._onHashChange=()=>{const e=this._getCurrentHash();if(!this._isValidHash(e))return !1;const t=this._map.dragRotate.isEnabled()&&this._map.touchZoomRotate.isEnabled()?+(e[3]||0):this._map.getBearing();return this._map.jumpTo({center:[+e[2],+e[1]],zoom:+e[0],bearing:t,pitch:+(e[4]||0)}),!0},this._updateHashUnthrottled=()=>{const e=window.location.href.replace(/(#.*)?$/,this.getHashString());window.history.replaceState(window.history.state,null,e);},this._removeHash=()=>{const e=this._getCurrentHash();if(0===e.length)return;const t=e.join("/");let i=t;i.split("&").length>0&&(i=i.split("&")[0]),this._hashName&&(i=`${this._hashName}=${t}`);let o=window.location.hash.replace(i,"");o.startsWith("#&")?o=o.slice(0,1)+o.slice(2):"#"===o&&(o="");let a=window.location.href.replace(/(#.+)?$/,o);a=a.replace("&&","&"),window.history.replaceState(window.history.state,null,a);},this._updateHash=Ka(this._updateHashUnthrottled,300),this._hashName=e&&encodeURIComponent(e);}addTo(e){return this._map=e,addEventListener("hashchange",this._onHashChange,!1),this._map.on("moveend",this._updateHash),this}remove(){return removeEventListener("hashchange",this._onHashChange,!1),this._map.off("moveend",this._updateHash),clearTimeout(this._updateHash()),this._removeHash(),delete this._map,this}getHashString(e){const t=this._map.getCenter(),i=Math.round(100*this._map.getZoom())/100,o=Math.ceil((i*Math.LN2+Math.log(512/360/.5))/Math.LN10),a=Math.pow(10,o),r=Math.round(t.lng*a)/a,s=Math.round(t.lat*a)/a,n=this._map.getBearing(),l=this._map.getPitch();let c="";if(c+=e?`/${r}/${s}/${i}`:`${i}/${s}/${r}`,(n||l)&&(c+="/"+Math.round(10*n)/10),l&&(c+=`/${Math.round(l)}`),this._hashName){const e=this._hashName;let t=!1;const i=window.location.hash.slice(1).split("&").map((i=>{const o=i.split("=")[0];return o===e?(t=!0,`${o}=${c}`):i})).filter((e=>e));return t||i.push(`${e}=${c}`),`#${i.join("&")}`}return `#${c}`}_isValidHash(e){if(e.length<3||e.some(isNaN))return !1;try{new t.W(+e[2],+e[1]);}catch(e){return !1}const i=+e[0],o=+(e[3]||0),a=+(e[4]||0);return i>=this._map.getMinZoom()&&i<=this._map.getMaxZoom()&&o>=-180&&o<=180&&a>=this._map.getMinPitch()&&a<=this._map.getMaxPitch()}}const Qa={linearity:.3,easing:t.cv(0,0,.3,1)},Ja=t.e({deceleration:2500,maxSpeed:1400},Qa),er=t.e({deceleration:20,maxSpeed:1400},Qa),tr=t.e({deceleration:1e3,maxSpeed:360},Qa),ir=t.e({deceleration:1e3,maxSpeed:90},Qa),or=t.e({deceleration:1e3,maxSpeed:360},Qa);class ar{constructor(e){this._map=e,this.clear();}clear(){this._inertiaBuffer=[];}record(e){this._drainInertiaBuffer(),this._inertiaBuffer.push({time:c(),settings:e});}_drainInertiaBuffer(){const e=this._inertiaBuffer,t=c();for(;e.length>0&&t-e[0].time>160;)e.shift();}_onMoveEnd(e){if(this._drainInertiaBuffer(),this._inertiaBuffer.length<2)return;const i={zoom:0,bearing:0,pitch:0,roll:0,pan:new t.P(0,0),pinchAround:void 0,around:void 0};for(const{settings:e}of this._inertiaBuffer)i.zoom+=e.zoomDelta||0,i.bearing+=e.bearingDelta||0,i.pitch+=e.pitchDelta||0,i.roll+=e.rollDelta||0,e.panDelta&&i.pan._add(e.panDelta),e.around&&(i.around=e.around),e.pinchAround&&(i.pinchAround=e.pinchAround);const o=this._inertiaBuffer[this._inertiaBuffer.length-1].time-this._inertiaBuffer[0].time,a={};if(i.pan.mag()){const r=sr(i.pan.mag(),o,t.e({},Ja,e||{})),s=i.pan.mult(r.amount/i.pan.mag()),n=this._map.cameraHelper.handlePanInertia(s,this._map.transform);a.center=n.easingCenter,a.offset=n.easingOffset,rr(a,r);}if(i.zoom){const e=sr(i.zoom,o,er);a.zoom=t.cw(this._map.transform.zoom+e.amount,this._map.getZoomSnap(),e.amount),rr(a,e);}if(i.bearing){const e=sr(i.bearing,o,tr);a.bearing=this._map.transform.bearing+t.al(e.amount,-179,179),rr(a,e);}if(i.pitch){const e=sr(i.pitch,o,ir);a.pitch=this._map.transform.pitch+e.amount,rr(a,e);}if(i.roll){const e=sr(i.roll,o,or);a.roll=this._map.transform.roll+t.al(e.amount,-179,179),rr(a,e);}if(a.zoom||a.bearing){const e=void 0===i.pinchAround?i.around:i.pinchAround;a.around=e?this._map.unproject(e):this._map.getCenter();}return this.clear(),t.e(a,{noMoveStart:!0})}}function rr(e,t){(!e.duration||e.durationi.unproject(e))),n=r.reduce(((e,t,i,o)=>e.add(t.div(o.length))),new t.P(0,0));super(e,{points:r,point:n,lngLats:s,lngLat:i.unproject(n),originalEvent:o}),this._defaultPrevented=!1;}}class cr extends t.n{preventDefault(){this._defaultPrevented=!0;}get defaultPrevented(){return this._defaultPrevented}constructor(e,t,i){super(e,{originalEvent:i}),this._defaultPrevented=!1;}}class hr{constructor(e,t){this._map=e,this._clickTolerance=t.clickTolerance;}reset(){delete this._mousedownPos;}wheel(e){return this._firePreventable(new cr(e.type,this._map,e))}mousedown(e,t){return this._mousedownPos=t,this._firePreventable(new nr(e.type,this._map,e))}mouseup(e){this._map.fire(new nr(e.type,this._map,e));}click(e,t){this._mousedownPos&&this._mousedownPos.dist(t)>=this._clickTolerance||this._map.fire(new nr(e.type,this._map,e));}dblclick(e){return this._firePreventable(new nr(e.type,this._map,e))}mouseover(e){this._map.fire(new nr(e.type,this._map,e));}mouseout(e){this._map.fire(new nr(e.type,this._map,e));}touchstart(e){return this._firePreventable(new lr(e.type,this._map,e))}touchmove(e){this._map.fire(new lr(e.type,this._map,e));}touchend(e){this._map.fire(new lr(e.type,this._map,e));}touchcancel(e){this._map.fire(new lr(e.type,this._map,e));}_firePreventable(e){if(this._map.fire(e),e.defaultPrevented)return {}}isEnabled(){return !0}isActive(){return !1}enable(){}disable(){}}class ur{constructor(e){this._map=e;}reset(){this._delayContextMenu=!1,this._ignoreContextMenu=!0,delete this._contextMenuEvent;}mousemove(e){this._map.fire(new nr(e.type,this._map,e));}mousedown(){this._delayContextMenu=!0,this._ignoreContextMenu=!1;}mouseup(){this._delayContextMenu=!1,this._contextMenuEvent&&(this._map.fire(new nr("contextmenu",this._map,this._contextMenuEvent)),delete this._contextMenuEvent);}contextmenu(e){this._delayContextMenu?this._contextMenuEvent=e:this._ignoreContextMenu||this._map.fire(new nr(e.type,this._map,e)),this._map.listens("contextmenu")&&e.preventDefault();}isEnabled(){return !0}isActive(){return !1}enable(){}disable(){}}class dr{constructor(e){this._map=e;}get transform(){return this._map._requestedCameraState||this._map.transform}get center(){return {lng:this.transform.center.lng,lat:this.transform.center.lat}}get zoom(){return this.transform.zoom}get pitch(){return this.transform.pitch}get bearing(){return this.transform.bearing}unproject(e){return this.transform.screenPointToLocation(t.P.convert(e),this._map.terrain)}}class _r{constructor(e,t){this._map=e,this._tr=new dr(e),this._el=e.getCanvasContainer(),this._container=e.getContainer(),this._clickTolerance=t.clickTolerance||1,t.boxZoom&&"object"==typeof t.boxZoom&&(this._boxZoomEnd=t.boxZoom.boxZoomEnd);}isEnabled(){return !!this._enabled}isActive(){return !!this._active}enable(){this.isEnabled()||(this._enabled=!0);}disable(){this.isEnabled()&&(this._enabled=!1);}mousedown(e,t){this.isEnabled()&&e.shiftKey&&0===e.button&&(d.disableDrag(),this._startPos=this._lastPos=t,this._active=!0);}mousemoveWindow(e,t){if(!this._active)return;const i=t;if(this._lastPos.equals(i)||!this._box&&i.dist(this._startPos)e.fitScreenCoordinates(o,a,this._tr.bearing,{linear:!0})};this._fireEvent("boxzoomcancel",e);}keydown(e){this._active&&27===e.keyCode&&(this.reset(),this._fireEvent("boxzoomcancel",e));}reset(){this._active=!1,this._container.classList.remove("maplibregl-crosshair"),this._box&&(this._box.remove(),this._box=null),d.enableDrag(),delete this._startPos,delete this._lastPos;}_fireEvent(e,i){return this._map.fire(new t.n(e,{originalEvent:i}))}}function pr(e,t){if(e.length!==t.length)throw new Error(`The number of touches and points are not equal - touches ${e.length}, points ${t.length}`);const i={};for(let o=0;othis.numTouches)&&(this.aborted=!0),this.aborted||(void 0===this.startTime&&(this.startTime=e.timeStamp),o.length===this.numTouches&&(this.centroid=function(e){const i=new t.P(0,0);for(const t of e)i._add(t);return i.div(e.length)}(i),this.touches=pr(o,i)));}touchmove(e,t,i){if(this.aborted||!this.centroid)return;const o=pr(i,t);for(const e in this.touches){const t=o[e];(!t||t.dist(this.touches[e])>30)&&(this.aborted=!0);}}touchend(e,t,i){if((!this.centroid||e.timeStamp-this.startTime>500)&&(this.aborted=!0),0===i.length){const e=!this.aborted&&this.centroid;if(this.reset(),e)return e}}}class fr{constructor(e){this.singleTap=new mr(e),this.numTaps=e.numTaps,this.reset();}reset(){this.lastTime=1/0,delete this.lastTap,this.count=0,this.singleTap.reset();}touchstart(e,t,i){this.singleTap.touchstart(e,t,i);}touchmove(e,t,i){this.singleTap.touchmove(e,t,i);}touchend(e,t,i){const o=this.singleTap.touchend(e,t,i);if(o){const t=e.timeStamp-this.lastTime<500,i=!this.lastTap||this.lastTap.dist(o)<30;if(t&&i||this.reset(),this.count++,this.lastTime=e.timeStamp,this.lastTap=o,this.count===this.numTaps)return this.reset(),o}}}class gr{constructor(e){this._tr=new dr(e),this._zoomIn=new fr({numTouches:1,numTaps:2}),this._zoomOut=new fr({numTouches:2,numTaps:1}),this.reset();}reset(){this._active=!1,this._zoomIn.reset(),this._zoomOut.reset();}touchstart(e,t,i){this._zoomIn.touchstart(e,t,i),this._zoomOut.touchstart(e,t,i);}touchmove(e,t,i){this._zoomIn.touchmove(e,t,i),this._zoomOut.touchmove(e,t,i);}touchend(e,i,o){const a=this._zoomIn.touchend(e,i,o),r=this._zoomOut.touchend(e,i,o),s=this._tr;return a?(this._active=!0,e.preventDefault(),setTimeout((()=>this.reset()),0),{cameraAnimation:i=>i.easeTo({duration:300,zoom:t.cw(s.zoom+1,i.getZoomSnap()),around:s.unproject(a)},{originalEvent:e})}):r?(this._active=!0,e.preventDefault(),setTimeout((()=>this.reset()),0),{cameraAnimation:i=>i.easeTo({duration:300,zoom:t.cw(s.zoom-1,i.getZoomSnap()),around:s.unproject(r)},{originalEvent:e})}):void 0}touchcancel(){this.reset();}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class vr{constructor(e){this._enabled=!!e.enable,this._moveStateManager=e.moveStateManager,this._clickTolerance=e.clickTolerance||1,this._moveFunction=e.move,this._activateOnStart=!!e.activateOnStart,e.assignEvents(this),this.reset();}reset(e){this._active=!1,this._moved=!1,delete this._lastPoint,this._moveStateManager.endMove(e);}_move(...e){const t=this._moveFunction(...e);if(t.bearingDelta||t.pitchDelta||t.rollDelta||t.around||t.panDelta)return this._active=!0,t}dragStart(e,t){this.isEnabled()&&!this._lastPoint&&this._moveStateManager.isValidStartEvent(e)&&(this._moveStateManager.startMove(e),this._lastPoint=Array.isArray(t)?t[0]:t,this._activateOnStart&&this._lastPoint&&(this._active=!0));}dragMove(e,t){if(!this.isEnabled())return;const i=this._lastPoint;if(!i)return;if(e.preventDefault(),!this._moveStateManager.isValidMoveEvent(e))return void this.reset(e);const o=Array.isArray(t)?t[0]:t;return !this._moved&&o.dist(i)!0}),t=new Tr){this.mouseMoveStateManager=e,this.oneFingerTouchMoveStateManager=t;}_executeRelevantHandler(e,t,i){return e instanceof MouseEvent?t(e):"undefined"!=typeof TouchEvent&&e instanceof TouchEvent?i(e):void 0}startMove(e){this._executeRelevantHandler(e,(e=>{this.mouseMoveStateManager.startMove(e);}),(e=>{this.oneFingerTouchMoveStateManager.startMove(e);}));}endMove(e){this._executeRelevantHandler(e,(e=>{this.mouseMoveStateManager.endMove(e);}),(e=>{this.oneFingerTouchMoveStateManager.endMove(e);}));}isValidStartEvent(e){return this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.isValidStartEvent(e)),(e=>this.oneFingerTouchMoveStateManager.isValidStartEvent(e)))}isValidMoveEvent(e){return this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.isValidMoveEvent(e)),(e=>this.oneFingerTouchMoveStateManager.isValidMoveEvent(e)))}isValidEndEvent(e){return this._executeRelevantHandler(e,(e=>this.mouseMoveStateManager.isValidEndEvent(e)),(e=>this.oneFingerTouchMoveStateManager.isValidEndEvent(e)))}}const Cr=e=>{e.mousedown=e.dragStart,e.mousemoveWindow=e.dragMove,e.mouseup=e.dragEnd,e.contextmenu=e=>{e.preventDefault();};};class Mr{constructor(e,t){this._clickTolerance=e.clickTolerance||1,this._map=t,this.reset();}reset(){this._active=!1,this._touches={},this._sum=new t.P(0,0);}_shouldBePrevented(e){return e<(this._map.cooperativeGestures.isEnabled()?2:1)}touchstart(e,t,i){return this._calculateTransform(e,t,i)}touchmove(e,t,i){if(this._active){if(!this._shouldBePrevented(i.length))return e.preventDefault(),this._calculateTransform(e,t,i);this._map.cooperativeGestures.notifyGestureBlocked("touch_pan",e);}}touchend(e,t,i){this._calculateTransform(e,t,i),this._active&&this._shouldBePrevented(i.length)&&this.reset();}touchcancel(){this.reset();}_calculateTransform(e,i,o){o.length>0&&(this._active=!0);const a=pr(o,i),r=new t.P(0,0),s=new t.P(0,0);let n=0;for(const e in a){const t=a[e],i=this._touches[e];i&&(r._add(t),s._add(t.sub(i)),n++,a[e]=t);}if(this._touches=a,this._shouldBePrevented(n)||!s.mag())return;const l=s.div(n);return this._sum._add(l),this._sum.mag()Math.abs(e.x)}class Lr extends Ir{constructor(e){super(),this._currentTouchCount=0,this._map=e;}reset(){super.reset(),this._valid=void 0,delete this._firstMove,delete this._lastPoints;}touchstart(e,t,i){super.touchstart(e,t,i),this._currentTouchCount=i.length;}_start(e){this._lastPoints=e,Ar(e[0].sub(e[1]))&&(this._valid=!1);}_move(e,t,i){if(this._map.cooperativeGestures.isEnabled()&&this._currentTouchCount<3)return;const o=e[0].sub(this._lastPoints[0]),a=e[1].sub(this._lastPoints[1]);return this._valid=this.gestureBeginsVertically(o,a,i.timeStamp),this._valid?(this._lastPoints=e,this._active=!0,{pitchDelta:(o.y+a.y)/2*-.5}):void 0}gestureBeginsVertically(e,t,i){if(void 0!==this._valid)return this._valid;const o=e.mag()>=2,a=t.mag()>=2;if(!o&&!a)return;if(!o||!a)return void 0===this._firstMove&&(this._firstMove=i),i-this._firstMove<100&&void 0;const r=e.y>0==t.y>0;return Ar(e)&&Ar(t)&&r}}const Fr={panStep:100,bearingStep:15,pitchStep:10};class kr{constructor(e){this._tr=new dr(e);const t=Fr;this._panStep=t.panStep,this._bearingStep=t.bearingStep,this._pitchStep=t.pitchStep,this._rotationDisabled=!1;}reset(){this._active=!1;}keydown(e){if(e.altKey||e.ctrlKey||e.metaKey)return;let i=0,o=0,a=0,r=0,s=0;switch(e.keyCode){case 61:case 107:case 171:case 187:i=1;break;case 189:case 109:case 173:i=-1;break;case 37:e.shiftKey?o=-1:(e.preventDefault(),r=-1);break;case 39:e.shiftKey?o=1:(e.preventDefault(),r=1);break;case 38:e.shiftKey?a=1:(e.preventDefault(),s=-1);break;case 40:e.shiftKey?a=-1:(e.preventDefault(),s=1);break;default:return}return this._rotationDisabled&&(o=0,a=0),{cameraAnimation:n=>{const l=this._tr;n.easeTo({duration:300,easeId:"keyboardHandler",easing:Br,zoom:i?t.cw(l.zoom+i*(e.shiftKey?2:1),n.getZoomSnap()):l.zoom,bearing:l.bearing+o*this._bearingStep,pitch:l.pitch+a*this._pitchStep,offset:[-r*this._panStep,-s*this._panStep],center:l.center},{originalEvent:e});}}}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}disableRotation(){this._rotationDisabled=!0;}enableRotation(){this._rotationDisabled=!1;}}function Br(e){return e*(2-e)}const Or=4.000244140625,jr=1/450;class Nr{constructor(e,t){this._onTimeout=e=>{this._type="wheel",this._delta-=this._lastValue,this._active||this._start(e);},this._map=e,this._tr=new dr(e),this._triggerRenderFrame=t,this._delta=0,this._defaultZoomRate=.01,this._wheelZoomRate=jr;}setZoomRate(e){this._defaultZoomRate=e;}setWheelZoomRate(e){this._wheelZoomRate=e;}isEnabled(){return !!this._enabled}isActive(){return !!this._active||void 0!==this._finishTimeout}isZooming(){return !!this._zooming}enable(e){this.isEnabled()||(this._enabled=!0,this._aroundCenter=!!e&&"center"===e.around);}disable(){this.isEnabled()&&(this._enabled=!1);}_shouldBePrevented(e){return !!this._map.cooperativeGestures.isEnabled()&&!(e.ctrlKey||this._map.cooperativeGestures.isBypassed(e))}wheel(e){if(!this.isEnabled())return;if(this._shouldBePrevented(e))return void this._map.cooperativeGestures.notifyGestureBlocked("wheel_zoom",e);let t=e.deltaMode===WheelEvent.DOM_DELTA_LINE?40*e.deltaY:e.deltaY;const i=c(),o=i-(this._lastWheelEventTime||0);this._lastWheelEventTime=i,0!==t&&t%Or==0?this._type="wheel":0!==t&&Math.abs(t)<4?this._type="trackpad":o>400?(this._type=null,this._lastValue=t,this._timeout=setTimeout(this._onTimeout,40,e)):this._type||(this._type=Math.abs(o*t)<200?"trackpad":"wheel",this._timeout&&(clearTimeout(this._timeout),this._timeout=null,t+=this._lastValue)),e.shiftKey&&t&&(t/=4),this._type&&(this._lastWheelEvent=e,this._delta-=t,this._active||this._start(e)),e.preventDefault();}_start(e){if(!this._delta)return;this._needsRerender=!1,this._active=!0,this.isZooming()||(this._zooming=!0),this._finishTimeout&&(clearTimeout(this._finishTimeout),delete this._finishTimeout);const i=d.mousePos(this._map.getCanvas(),e),o=this._tr;this._aroundPoint=this._aroundCenter?o.transform.locationToScreenPoint(t.W.convert(o.center)):i,this._needsRerender||(this._needsRerender=!0,this._triggerRenderFrame());}renderFrame(){if(!this._needsRerender)return;if(this._needsRerender=!1,!this.isActive())return;const e=this._tr.transform;if("number"==typeof this._lastExpectedZoom){const t=e.zoom-this._lastExpectedZoom;"number"==typeof this._startZoom&&(this._startZoom+=t),"number"==typeof this._targetZoom&&(this._targetZoom+=t);}if(0!==this._delta){const i="wheel"===this._type&&Math.abs(this._delta)>Or?this._wheelZoomRate:this._defaultZoomRate;let o=2/(1+Math.exp(-Math.abs(this._delta*i)));this._delta<0&&0!==o&&(o=1/o);const a="number"!=typeof this._targetZoom?e.scale:t.ao(this._targetZoom),r=e.applyConstrain(e.getCameraLngLat(),t.ar(a*o)).zoom,s=this._map.getZoomSnap();if("wheel"===this._type&&s>0){const i=t.cw(e.zoom,s);this._targetZoom=t.cw(r,s,r-i);}else this._targetZoom=r;"wheel"===this._type&&(this._startZoom=e.zoom,this._easing=this._smoothOutEasing(200)),this._delta=0;}const i="number"!=typeof this._targetZoom?e.zoom:this._targetZoom,o=this._startZoom,a=this._easing;let r,s=!1;if("wheel"===this._type&&o&&a){const e=c()-this._lastWheelEventTime,n=Math.min((e+5)/200,1),l=a(n);r=t.H.number(o,i,l),n<1?this._needsRerender=!0:s=!0;}else r=i,s=!0;return this._active=!0,s&&(this._active=!1,this._finishTimeout=setTimeout((()=>{this._zooming=!1,this._triggerRenderFrame(),delete this._targetZoom,delete this._lastExpectedZoom,delete this._finishTimeout;}),200)),this._lastExpectedZoom=r,{noInertia:!0,needsRenderFrame:!s,zoomDelta:r-e.zoom,around:this._aroundPoint,originalEvent:this._lastWheelEvent}}_smoothOutEasing(e){let i=t.cy;if(this._prevEase){const e=this._prevEase,o=(c()-e.start)/e.duration,a=e.easing(o+.01)-e.easing(o),r=.27/Math.sqrt(a*a+1e-4)*.01,s=Math.sqrt(.0729-r*r);i=t.cv(r,s,.25,1);}return this._prevEase={start:c(),duration:e,easing:i},i}reset(){this._active=!1,this._zooming=!1,delete this._targetZoom,delete this._lastExpectedZoom,this._finishTimeout&&(clearTimeout(this._finishTimeout),delete this._finishTimeout);}}class Zr{constructor(e,t){this._clickZoom=e,this._tapZoom=t;}enable(){this._clickZoom.enable(),this._tapZoom.enable();}disable(){this._clickZoom.disable(),this._tapZoom.disable();}isEnabled(){return this._clickZoom.isEnabled()&&this._tapZoom.isEnabled()}isActive(){return this._clickZoom.isActive()||this._tapZoom.isActive()}}class Ur{constructor(e){this._tr=new dr(e),this.reset();}reset(){this._active=!1;}dblclick(e,i){return e.preventDefault(),{cameraAnimation:o=>{o.easeTo({duration:300,zoom:t.cw(this._tr.zoom+(e.shiftKey?-1:1),o.getZoomSnap()),around:this._tr.unproject(i)},{originalEvent:e});}}}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class Gr{constructor(){this._tap=new fr({numTouches:1,numTaps:1}),this._zoomRate=1,this.reset();}setZoomRate(e){this._zoomRate=null!=e?e:1;}reset(){this._active=!1,delete this._swipePoint,delete this._swipeTouch,delete this._tapTime,delete this._tapPoint,this._tap.reset();}touchstart(e,t,i){if(!this._swipePoint)if(this._tapTime){const o=t[0],a=e.timeStamp-this._tapTime<500,r=this._tapPoint.dist(o)<30;a&&r?i.length>0&&(this._swipePoint=o,this._swipeTouch=i[0].identifier):this.reset();}else this._tap.touchstart(e,t,i);}touchmove(e,t,i){if(this._tapTime){if(this._swipePoint){if(i[0].identifier!==this._swipeTouch)return;const o=t[0],a=o.y-this._swipePoint.y;return this._swipePoint=o,e.preventDefault(),this._active=!0,{zoomDelta:a/128*this._zoomRate}}}else this._tap.touchmove(e,t,i);}touchend(e,t,i){if(this._tapTime)this._swipePoint&&0===i.length&&this.reset();else {const o=this._tap.touchend(e,t,i);o&&(this._tapTime=e.timeStamp,this._tapPoint=o);}}touchcancel(){this.reset();}enable(){this._enabled=!0;}disable(){this._enabled=!1,this.reset();}isEnabled(){return this._enabled}isActive(){return this._active}}class Vr{constructor(e,t,i){this._el=e,this._mousePan=t,this._touchPan=i;}enable(e){this._inertiaOptions=e||{},this._mousePan.enable(),this._touchPan.enable(),this._el.classList.add("maplibregl-touch-drag-pan");}disable(){this._mousePan.disable(),this._touchPan.disable(),this._el.classList.remove("maplibregl-touch-drag-pan");}isEnabled(){return this._mousePan.isEnabled()&&this._touchPan.isEnabled()}isActive(){return this._mousePan.isActive()||this._touchPan.isActive()}}class Wr{constructor(e,t,i,o){this._pitchWithRotate=e.pitchWithRotate,this._rollEnabled=e.rollEnabled,this._mouseRotate=t,this._mousePitch=i,this._mouseRoll=o;}enable(){this._mouseRotate.enable(),this._pitchWithRotate&&this._mousePitch.enable(),this._rollEnabled&&this._mouseRoll.enable();}disable(){this._mouseRotate.disable(),this._mousePitch.disable(),this._mouseRoll.disable();}isEnabled(){return this._mouseRotate.isEnabled()&&(!this._pitchWithRotate||this._mousePitch.isEnabled())&&(!this._rollEnabled||this._mouseRoll.isEnabled())}isActive(){return this._mouseRotate.isActive()||this._mousePitch.isActive()||this._mouseRoll.isActive()}}class qr{constructor(e,t,i,o){this._el=e,this._touchZoom=t,this._touchRotate=i,this._tapDragZoom=o,this._rotationDisabled=!1,this._enabled=!0;}enable(e){this._touchZoom.enable(e),this._rotationDisabled||this._touchRotate.enable(e),this._tapDragZoom.enable(),this._el.classList.add("maplibregl-touch-zoom-rotate");}disable(){this._touchZoom.disable(),this._touchRotate.disable(),this._tapDragZoom.disable(),this._el.classList.remove("maplibregl-touch-zoom-rotate");}isEnabled(){return this._touchZoom.isEnabled()&&(this._rotationDisabled||this._touchRotate.isEnabled())&&this._tapDragZoom.isEnabled()}isActive(){return this._touchZoom.isActive()||this._touchRotate.isActive()||this._tapDragZoom.isActive()}setZoomRate(e){this._touchZoom.setZoomRate(e),this._tapDragZoom.setZoomRate(e);}setZoomThreshold(e){this._touchZoom.setZoomThreshold(e);}disableRotation(){this._rotationDisabled=!0,this._touchRotate.disable();}enableRotation(){this._rotationDisabled=!1,this._touchZoom.isEnabled()&&this._touchRotate.enable();}}class $r{constructor(e,t){this._bypassKey=navigator.userAgent.includes("Mac")?"metaKey":"ctrlKey",this._map=e,this._options=t,this._enabled=!1;}isActive(){return !1}reset(){}_setupUI(){if(this._container)return;const e=this._map.getCanvasContainer();e.classList.add("maplibregl-cooperative-gestures"),this._container=d.create("div","maplibregl-cooperative-gesture-screen",e);let t=this._map._getUIString("CooperativeGesturesHandler.WindowsHelpText");"metaKey"===this._bypassKey&&(t=this._map._getUIString("CooperativeGesturesHandler.MacHelpText"));const i=this._map._getUIString("CooperativeGesturesHandler.MobileHelpText"),o=document.createElement("div");o.className="maplibregl-desktop-message",o.textContent=t,this._container.appendChild(o);const a=document.createElement("div");a.className="maplibregl-mobile-message",a.textContent=i,this._container.appendChild(a),this._container.setAttribute("aria-hidden","true");}_destroyUI(){this._container&&(this._container.remove(),this._map.getCanvasContainer().classList.remove("maplibregl-cooperative-gestures")),delete this._container;}enable(){this._setupUI(),this._enabled=!0;}disable(){this._enabled=!1,this._destroyUI();}isEnabled(){return this._enabled}isBypassed(e){return e[this._bypassKey]}notifyGestureBlocked(e,i){this._enabled&&(this._map.fire(new t.n("cooperativegestureprevented",{gestureType:e,originalEvent:i})),this._container.classList.add("maplibregl-show"),setTimeout((()=>{this._container.classList.remove("maplibregl-show");}),100));}}const Hr=e=>e.zoom||e.drag||e.roll||e.pitch||e.rotate;class Xr extends t.n{}function Kr(e){var t;return (null===(t=e.panDelta)||void 0===t?void 0:t.mag())||e.zoomDelta||e.bearingDelta||e.pitchDelta||e.rollDelta}class Yr{get _ownerDocument(){var e;return (null===(e=this._el)||void 0===e?void 0:e.ownerDocument)||document}get _ownerWindow(){var e,t;return (null===(t=null===(e=this._el)||void 0===e?void 0:e.ownerDocument)||void 0===t?void 0:t.defaultView)||window}constructor(e,i){this.handleWindowEvent=e=>{this.handleEvent(e,`${e.type}Window`);},this.handleEvent=(e,i)=>{if("blur"===e.type)return void this.stop(!0);this._updatingCamera=!0;const o="renderFrame"===e.type?void 0:e,a={needsRenderFrame:!1},r={},s={};for(const{handlerName:n,handler:l,allowed:c}of this._handlers){if(!l.isEnabled())continue;let h;if(this._blockedByActive(s,c,n))l.reset();else if(l[i||e.type]){if(t.cz(e,i||e.type)){const t=d.mousePos(this._map.getCanvas(),e);h=l[i||e.type](e,t);}else if(t.cA(e,i||e.type)){const t=this._getMapTouches(e.touches),o=d.touchPos(this._map.getCanvas(),t);h=l[i||e.type](e,o,t);}else t.cB(i||e.type)||(h=l[i||e.type](e));this.mergeHandlerResult(a,r,h,n,o),(null==h?void 0:h.needsRenderFrame)&&this._triggerRenderFrame();}(h||l.isActive())&&(s[n]=l);}const n={};for(const e in this._previousActiveHandlers)s[e]||(n[e]=o);this._previousActiveHandlers=s,(Object.keys(n).length||Kr(a))&&(this._changes.push([a,r,n]),this._triggerRenderFrame()),(Object.keys(s).length||Kr(a))&&this._map._stop(!0),this._updatingCamera=!1;const{cameraAnimation:l}=a;l&&(this._inertia.clear(),this._fireEvents({},{},!0),this._changes=[],l(this._map));},this._map=e,this._el=this._map.getCanvasContainer(),this._handlers=[],this._handlersById={},this._changes=[],this._inertia=new ar(e),this._bearingSnap=i.bearingSnap,this._previousActiveHandlers={},this._eventsInProgress={},this._addDefaultHandlers(i);const o=this._el;this._listeners=[[o,"touchstart",{passive:!0}],[o,"touchmove",{passive:!1}],[o,"touchend",void 0],[o,"touchcancel",void 0],[o,"mousedown",void 0],[o,"mousemove",void 0],[o,"mouseup",void 0],[this._ownerDocument,"mousemove",{capture:!0}],[this._ownerDocument,"mouseup",void 0],[o,"mouseover",void 0],[o,"mouseout",void 0],[o,"dblclick",void 0],[o,"click",void 0],[o,"keydown",{capture:!1}],[o,"keyup",void 0],[o,"wheel",{passive:!1}],[o,"contextmenu",void 0],[this._ownerWindow,"blur",void 0]];for(const[e,t,i]of this._listeners)e.addEventListener(t,e===this._ownerDocument?this.handleWindowEvent:this.handleEvent,i);}destroy(){for(const[e,t,i]of this._listeners)e.removeEventListener(t,e===this._ownerDocument?this.handleWindowEvent:this.handleEvent,i);}_addDefaultHandlers(e){const i=this._map,o=i.getCanvasContainer();this._add("mapEvent",new hr(i,e));const a=i.boxZoom=new _r(i,e);this._add("boxZoom",a),e.interactive&&e.boxZoom&&a.enable();const r=i.cooperativeGestures=new $r(i,e.cooperativeGestures);this._add("cooperativeGestures",r),e.cooperativeGestures&&r.enable();const s=new gr(i),n=new Ur(i);i.doubleClickZoom=new Zr(n,s),this._add("tapZoom",s),this._add("clickZoom",n),e.interactive&&e.doubleClickZoom&&i.doubleClickZoom.enable();const l=new Gr;this._add("tapDragZoom",l);const c=i.touchPitch=new Lr(i);this._add("touchPitch",c),e.interactive&&e.touchPitch&&i.touchPitch.enable(e.touchPitch);const h=()=>i.project(i.getCenter()),u=function({enable:e,clickTolerance:i,aroundCenter:o=!0,minPixelCenterThreshold:a=100,rotateDegreesPerPixelMoved:r=.8},s){const n=new wr({checkCorrectEvent:e=>0===e.button&&e.ctrlKey||2===e.button&&!e.ctrlKey});return new vr({clickTolerance:i,move:(e,i)=>{const n=s();if(o&&Math.abs(n.y-e.y)>a)return {bearingDelta:t.cx(new t.P(e.x,i.y),i,n)};let l=(i.x-e.x)*r;return o&&i.y0===e.button&&e.ctrlKey||2===e.button});return new vr({clickTolerance:t,move:(e,t)=>({pitchDelta:(t.y-e.y)*i}),moveStateManager:o,enable:e,assignEvents:Cr})}(e),_=function({enable:e,clickTolerance:t,rollDegreesPerPixelMoved:i=.3},o){const a=new wr({checkCorrectEvent:e=>2===e.button&&e.ctrlKey});return new vr({clickTolerance:t,move:(e,t)=>{const a=o();let r=(t.x-e.x)*i;return t.y0===e.button&&!e.ctrlKey});return new vr({clickTolerance:t,move:(e,t)=>({around:t,panDelta:t.sub(e)}),activateOnStart:!0,moveStateManager:i,enable:e,assignEvents:Cr})}(e),m=new Mr(e,i);i.dragPan=new Vr(o,p,m),this._add("mousePan",p),this._add("touchPan",m,["touchZoom","touchRotate"]),e.interactive&&e.dragPan&&i.dragPan.enable(e.dragPan);const f=new Dr,g=new zr;i.touchZoomRotate=new qr(o,g,f,l),this._add("touchRotate",f,["touchPan","touchZoom"]),this._add("touchZoom",g,["touchPan","touchRotate"]),e.interactive&&e.touchZoomRotate&&i.touchZoomRotate.enable(e.touchZoomRotate),this._add("blockableMapEvent",new ur(i));const v=i.scrollZoom=new Nr(i,(()=>this._triggerRenderFrame()));this._add("scrollZoom",v,["mousePan"]),e.interactive&&e.scrollZoom&&i.scrollZoom.enable(e.scrollZoom);const x=i.keyboard=new kr(i);this._add("keyboard",x),e.interactive&&e.keyboard&&i.keyboard.enable();}_add(e,t,i){this._handlers.push({handlerName:e,handler:t,allowed:i}),this._handlersById[e]=t;}stop(e){if(!this._updatingCamera){for(const{handler:e}of this._handlers)e.reset();this._inertia.clear(),this._fireEvents({},{},e),this._changes=[];}}isActive(){for(const{handler:e}of this._handlers)if(e.isActive())return !0;return !1}isZooming(){return !!this._eventsInProgress.zoom||this._map.scrollZoom.isZooming()}isRotating(){return !!this._eventsInProgress.rotate}isMoving(){return Boolean(Hr(this._eventsInProgress))||this.isZooming()}_blockedByActive(e,t,i){for(const o in e)if(o!==i&&!(null==t?void 0:t.includes(o)))return !0;return !1}_getMapTouches(e){const t=[];for(const i of e)this._el.contains(i.target)&&t.push(i);return t}mergeHandlerResult(e,i,o,a,r){if(!o)return;t.e(e,o);const s={handlerName:a,originalEvent:o.originalEvent||r};void 0!==o.zoomDelta&&(i.zoom=s),void 0!==o.panDelta&&(i.drag=s),void 0!==o.rollDelta&&(i.roll=s),void 0!==o.pitchDelta&&(i.pitch=s),void 0!==o.bearingDelta&&(i.rotate=s);}_applyChanges(){const e={},i={},o={};for(const[a,r,s]of this._changes)a.panDelta&&(e.panDelta=(e.panDelta||new t.P(0,0))._add(a.panDelta)),a.zoomDelta&&(e.zoomDelta=(e.zoomDelta||0)+a.zoomDelta),a.bearingDelta&&(e.bearingDelta=(e.bearingDelta||0)+a.bearingDelta),a.pitchDelta&&(e.pitchDelta=(e.pitchDelta||0)+a.pitchDelta),a.rollDelta&&(e.rollDelta=(e.rollDelta||0)+a.rollDelta),void 0!==a.around&&(e.around=a.around),void 0!==a.pinchAround&&(e.pinchAround=a.pinchAround),a.noInertia&&(e.noInertia=a.noInertia),t.e(i,r),t.e(o,s);this._updateMapTransform(e,i,o),this._changes=[];}_updateMapTransform(e,t,i){const o=this._map,a=o._getTransformForUpdate(),r=o.terrain;if(!(Kr(e)||r&&this._terrainMovement))return void this._fireEvents(t,i,!0);o._stop(!0);let{panDelta:s,zoomDelta:n,bearingDelta:l,pitchDelta:c,rollDelta:h,around:u,pinchAround:d}=e;void 0!==d&&(u=d),u||(u=o.transform.centerPoint),r&&!a.isPointOnMapSurface(u)&&(u=a.centerPoint);const _={panDelta:s,zoomDelta:n,rollDelta:h,pitchDelta:c,bearingDelta:l,around:u};this._map.cameraHelper.useGlobeControls&&!a.isPointOnMapSurface(u)&&(u=a.centerPoint);const p=u.distSqr(a.centerPoint)<.01?a.center:a.screenPointToLocation(s?u.sub(s):u);this._handleMapControls({terrain:r,tr:a,deltasForHelper:_,preZoomAroundLoc:p,combinedEventsInProgress:t,panDelta:s}),o._applyUpdatedTransform(a),this._map._update(),e.noInertia||this._inertia.record(e),this._fireEvents(t,i,!0);}_handleMapControls({terrain:e,tr:t,deltasForHelper:i,preZoomAroundLoc:o,combinedEventsInProgress:a,panDelta:r}){const s=this._map.cameraHelper;if(s.handleMapControlsRollPitchBearingZoom(i,t),e)return s.useGlobeControls?(this._terrainMovement||!a.drag&&!a.zoom||(this._terrainMovement=!0,this._map._elevationFreeze=!0),void s.handleMapControlsPan(i,t,o)):this._terrainMovement||!a.drag&&!a.zoom?void(a.drag&&this._terrainMovement&&r?t.setCenter(t.screenPointToLocation(t.centerPoint.sub(r))):s.handleMapControlsPan(i,t,o)):(this._terrainMovement=!0,this._map._elevationFreeze=!0,void s.handleMapControlsPan(i,t,o));s.handleMapControlsPan(i,t,o);}_fireEvents(e,i,o){const a=Hr(this._eventsInProgress),r=Hr(e),s={};for(const t in e){const{originalEvent:i}=e[t];this._eventsInProgress[t]||(s[`${t}start`]=i),this._eventsInProgress[t]=e[t];}!a&&r&&this._fireEvent("movestart",r.originalEvent);for(const e in s)this._fireEvent(e,s[e]);r&&this._fireEvent("move",r.originalEvent);for(const t in e){const{originalEvent:i}=e[t];this._fireEvent(t,i);}const l={};let c;for(const e in this._eventsInProgress){const{handlerName:t,originalEvent:o}=this._eventsInProgress[e];this._handlersById[t].isActive()||(delete this._eventsInProgress[e],c=i[t]||o,l[`${e}end`]=c);}for(const e in l)this._fireEvent(e,l[e]);const h=Hr(this._eventsInProgress),u=(a||r)&&!h;if(u&&this._terrainMovement){this._map._elevationFreeze=!1,this._terrainMovement=!1;const e=this._map._getTransformForUpdate();this._map.getCenterClampedToGround()&&e.recalculateZoomAndCenter(this._map.terrain),this._map._applyUpdatedTransform(e);}if(o&&u){this._updatingCamera=!0;const e=this._inertia._onMoveEnd(this._map.dragPan._inertiaOptions),i=e=>0!==e&&-this._bearingSnap{delete this._frameId,this.handleEvent(new Xr("renderFrame",{timeStamp:e})),this._applyChanges();}))}_triggerRenderFrame(){void 0===this._frameId&&(this._frameId=this._requestFrame());}}class Qr extends t.E{constructor(e,t,i){super(),this._renderFrameCallback=()=>{const e=Math.min((c()-this._easeStart)/this._easeOptions.duration,1);this._onEaseFrame(this._easeOptions.easing(e)),e<1&&this._easeFrameId?this._easeFrameId=this._requestRenderFrame(this._renderFrameCallback):this.stop();},this._moving=!1,this._zooming=!1,this.transform=e,this._bearingSnap=i.bearingSnap,this._zoomSnap=i.zoomSnap,this.cameraHelper=t,this.on("moveend",(()=>{delete this._requestedCameraState;}));}migrateProjection(e,t){e.apply(this.transform,!0),this.transform=e,this.cameraHelper=t;}getCenter(){return new t.W(this.transform.center.lng,this.transform.center.lat)}setCenter(e,t){return this.jumpTo({center:e},t)}getCenterElevation(){return this.transform.elevation}setCenterElevation(e,t){return this.jumpTo({elevation:e},t),this}getCenterClampedToGround(){return this._centerClampedToGround}setCenterClampedToGround(e){this._centerClampedToGround=e;}panBy(e,i,o){return e=t.P.convert(e).mult(-1),this.panTo(this.transform.center,t.e({offset:e},i),o)}panTo(e,i,o){return this.easeTo(t.e({center:e},i),o)}getZoom(){return this.transform.zoom}setZoom(e,t){return this.jumpTo({zoom:e},t),this}zoomTo(e,i,o){return this.easeTo(t.e({zoom:e},i),o)}zoomIn(e,i){return this.zoomTo(t.cw(this.getZoom()+1,this._zoomSnap),e,i),this}zoomOut(e,i){return this.zoomTo(t.cw(this.getZoom()-1,this._zoomSnap),e,i),this}getVerticalFieldOfView(){return this.transform.fov}setVerticalFieldOfView(e,i){return e!=this.transform.fov&&(this.transform.setFov(e),this.fire(new t.n("movestart",i)).fire(new t.n("move",i)).fire(new t.n("moveend",i))),this}getBearing(){return this.transform.bearing}setZoomSnap(e){return this._zoomSnap=e,this}getZoomSnap(){return this._zoomSnap}setBearing(e,t){return this.jumpTo({bearing:e},t),this}getPadding(){return this.transform.padding}setPadding(e,t){return this.jumpTo({padding:e},t),this}rotateTo(e,i,o){return this.easeTo(t.e({bearing:e},i),o)}resetNorth(e,i){return this.rotateTo(0,t.e({duration:1e3},e),i),this}resetNorthPitch(e,i){return this.easeTo(t.e({bearing:0,pitch:0,roll:0,duration:1e3},e),i),this}snapToNorth(e,t){return Math.abs(this.getBearing()){f.easeFunc(t),this.terrain&&!e.freezeElevation&&this._updateElevation(t),this._applyUpdatedTransform(o),this._fireMoveEvents(i);}),(t=>{this.terrain&&e.freezeElevation&&this._finalizeElevation(),this._afterEase(i,t);}),e),this}_prepareEase(e,i,o={}){this._moving=!0,i||o.moving||this.fire(new t.n("movestart",e)),this._zooming&&!o.zooming&&this.fire(new t.n("zoomstart",e)),this._rotating&&!o.rotating&&this.fire(new t.n("rotatestart",e)),this._pitching&&!o.pitching&&this.fire(new t.n("pitchstart",e)),this._rolling&&!o.rolling&&this.fire(new t.n("rollstart",e));}_prepareElevation(e){this._elevationCenter=e,this._elevationStart=this.transform.elevation,this._elevationTarget=this.terrain.getElevationForLngLatZoom(e,this.transform.tileZoom),this._elevationFreeze=!0;}_updateElevation(e){void 0!==this._elevationStart&&void 0!==this._elevationCenter||this._prepareElevation(this.transform.center),this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter,this.transform.tileZoom));const i=this.terrain.getElevationForLngLatZoom(this._elevationCenter,this.transform.tileZoom);if(e<1&&i!==this._elevationTarget){const t=this._elevationTarget-this._elevationStart;this._elevationStart+=e*(t-(i-(t*e+this._elevationStart))/(1-e)),this._elevationTarget=i;}this.transform.setElevation(t.H.number(this._elevationStart,this._elevationTarget,e));}_finalizeElevation(){this._elevationFreeze=!1,this.getCenterClampedToGround()&&this.transform.recalculateZoomAndCenter(this.terrain);}_getTransformForUpdate(){return this.transformCameraUpdate||this.terrain?(this._requestedCameraState||(this._requestedCameraState=this.transform.clone()),this._requestedCameraState):this.transform}_elevateCameraIfInsideTerrain(e){if(!this.terrain&&e.elevation>=0&&e.pitch<=90)return {};const t=e.getCameraLngLat(),i=e.getCameraAltitude(),o=this.terrain?this.terrain.getElevationForLngLatZoom(t,e.zoom):0;if(ithis._elevateCameraIfInsideTerrain(e))),this.transformCameraUpdate&&t.push((e=>this.transformCameraUpdate(e))),!t.length)return;const i=e.clone();for(const e of t){const t=i.clone(),{center:o,zoom:a,roll:r,pitch:s,bearing:n,elevation:l}=e(t);o&&t.setCenter(o),void 0!==l&&t.setElevation(l),void 0!==a&&t.setZoom(a),void 0!==r&&t.setRoll(r),void 0!==s&&t.setPitch(s),void 0!==n&&t.setBearing(n),i.apply(t,!1);}this.transform.apply(i,!1);}_fireMoveEvents(e){this.fire(new t.n("move",e)),this._zooming&&this.fire(new t.n("zoom",e)),this._rotating&&this.fire(new t.n("rotate",e)),this._pitching&&this.fire(new t.n("pitch",e)),this._rolling&&this.fire(new t.n("roll",e));}_afterEase(e,i){if(this._easeId&&i&&this._easeId===i)return;delete this._easeId;const o=this._zooming,a=this._rotating,r=this._pitching,s=this._rolling;this._moving=!1,this._zooming=!1,this._rotating=!1,this._pitching=!1,this._rolling=!1,this._padding=!1,o&&this.fire(new t.n("zoomend",e)),a&&this.fire(new t.n("rotateend",e)),r&&this.fire(new t.n("pitchend",e)),s&&this.fire(new t.n("rollend",e)),this.fire(new t.n("moveend",e));}flyTo(e,i){if(!e.essential&&n.prefersReducedMotion){const o=t.V(e,["center","zoom","bearing","pitch","roll","elevation","padding"]);return this.jumpTo(o,i)}this.stop(),"zoom"in(e=t.e({offset:[0,0],speed:1.2,curve:1.42,easing:t.cy},e))&&this._zoomSnap&&(e.zoom=t.cw(e.zoom,this._zoomSnap));const o=this._getTransformForUpdate(),a=o.bearing,r=o.pitch,s=o.roll,l=o.padding,c="bearing"in e?this._normalizeBearing(e.bearing,a):a,h="pitch"in e?+e.pitch:r,u="roll"in e?this._normalizeBearing(e.roll,s):s,d="padding"in e?e.padding:o.padding,_=t.P.convert(e.offset);let p=o.centerPoint.add(_);const m=o.screenPointToLocation(p),f=this.cameraHelper.handleFlyTo(o,{bearing:c,pitch:h,roll:u,padding:d,locationAtOffset:m,offsetAsPoint:_,center:e.center,minZoom:e.minZoom,zoom:e.zoom});let g=e.curve;const v=Math.max(o.width,o.height),x=v/f.scaleOfZoom,b=f.pixelPathLength;"number"==typeof f.scaleOfMinZoom&&(g=Math.sqrt(v/f.scaleOfMinZoom/b*2));const y=g*g;function w(e){const t=(x*x-v*v+(e?-1:1)*y*y*b*b)/(2*(e?x:v)*y*b);return Math.log(Math.sqrt(t*t+1)-t)}function T(e){return (Math.exp(e)-Math.exp(-e))/2}function P(e){return (Math.exp(e)+Math.exp(-e))/2}const C=w(!1);let M=function(e){return P(C)/P(C+g*e)},I=function(e){return v*((P(C)*(T(t=C+g*e)/P(t))-T(C))/y)/b;var t;},E=(w(!0)-C)/g;if(Math.abs(b)<2e-6||!isFinite(E)){if(Math.abs(v-x)<1e-6)return this.easeTo(e,i);const t=x0,M=e=>Math.exp(t*g*e);}return e.duration="duration"in e?+e.duration:1e3*E/("screenSpeed"in e?+e.screenSpeed/g:+e.speed),e.maxDuration&&e.duration>e.maxDuration&&(e.duration=0),this._zooming=!0,this._rotating=a!==c,this._pitching=h!==r,this._rolling=u!==s,this._padding=!o.isPaddingEqual(d),this._prepareEase(i,!1),this.terrain&&this._prepareElevation(f.targetCenter),this._ease((n=>{const m=n*E,g=1/M(m),v=I(m);this._rotating&&o.setBearing(t.H.number(a,c,n)),this._pitching&&o.setPitch(t.H.number(r,h,n)),this._rolling&&o.setRoll(t.H.number(s,u,n)),this._padding&&(o.interpolatePadding(l,d,n),p=o.centerPoint.add(_)),f.easeFunc(n,g,v,p),this.terrain&&!e.freezeElevation&&this._updateElevation(n),this._applyUpdatedTransform(o),this._fireMoveEvents(i);}),(()=>{this.terrain&&e.freezeElevation&&this._finalizeElevation(),this._afterEase(i);}),e),this}isEasing(){return !!this._easeFrameId}stop(){return this._stop()}_stop(e,t){var i;if(this._easeFrameId&&(this._cancelRenderFrame(this._easeFrameId),delete this._easeFrameId,delete this._onEaseFrame),this._onEaseEnd){const e=this._onEaseEnd;delete this._onEaseEnd,e.call(this,t);}return e||null===(i=this.handlers)||void 0===i||i.stop(!1),this}_ease(e,t,i){!1===i.animate||0===i.duration?(e(1),t()):(this._easeStart=c(),this._easeOptions=i,this._onEaseFrame=e,this._onEaseEnd=t,this._easeFrameId=this._requestRenderFrame(this._renderFrameCallback));}_normalizeBearing(e,i){e=t.X(e,-180,180);const o=Math.abs(e-i);return Math.abs(e-360-i)MapLibre'};class es{constructor(e=Jr){this._toggleAttribution=()=>{this._container.classList.contains("maplibregl-compact")&&(this._container.classList.contains("maplibregl-compact-show")?(this._container.setAttribute("open",""),this._container.classList.remove("maplibregl-compact-show")):(this._container.classList.add("maplibregl-compact-show"),this._container.removeAttribute("open")));},this._updateData=e=>{!e||"metadata"!==e.sourceDataType&&"visibility"!==e.sourceDataType&&"style"!==e.dataType&&"terrain"!==e.type||this._updateAttributions();},this._updateCompact=()=>{this._map.getCanvasContainer().offsetWidth<=640||this._compact?!1===this._compact?this._container.setAttribute("open",""):this._container.classList.contains("maplibregl-compact")||this._container.classList.contains("maplibregl-attrib-empty")||(this._container.setAttribute("open",""),this._container.classList.add("maplibregl-compact","maplibregl-compact-show")):(this._container.setAttribute("open",""),this._container.classList.contains("maplibregl-compact")&&this._container.classList.remove("maplibregl-compact","maplibregl-compact-show"));},this._updateCompactMinimize=()=>{this._container.classList.contains("maplibregl-compact")&&this._container.classList.contains("maplibregl-compact-show")&&this._container.classList.remove("maplibregl-compact-show");},this.options=e;}getDefaultPosition(){return "bottom-right"}onAdd(e){return this._map=e,this._compact=this.options.compact,this._container=d.create("details","maplibregl-ctrl maplibregl-ctrl-attrib"),this._compactButton=d.create("summary","maplibregl-ctrl-attrib-button",this._container),this._compactButton.addEventListener("click",this._toggleAttribution),this._setElementTitle(this._compactButton,"ToggleAttribution"),this._innerContainer=d.create("div","maplibregl-ctrl-attrib-inner",this._container),this._updateAttributions(),this._updateCompact(),this._map.on("styledata",this._updateData),this._map.on("sourcedata",this._updateData),this._map.on("terrain",this._updateData),this._map.on("resize",this._updateCompact),this._map.on("drag",this._updateCompactMinimize),this._container}onRemove(){this._container.remove(),this._map.off("styledata",this._updateData),this._map.off("sourcedata",this._updateData),this._map.off("terrain",this._updateData),this._map.off("resize",this._updateCompact),this._map.off("drag",this._updateCompactMinimize),this._map=void 0,this._compact=void 0,this._attribHTML=void 0;}_setElementTitle(e,t){const i=this._map._getUIString(`AttributionControl.${t}`);e.title=i,e.setAttribute("aria-label",i);}_updateAttributions(){if(!this._map.style)return;let e=[];if(this.options.customAttribution&&(Array.isArray(this.options.customAttribution)?e=e.concat(this.options.customAttribution.map((e=>"string"!=typeof e?"":e))):"string"==typeof this.options.customAttribution&&e.push(this.options.customAttribution)),this._map.style.stylesheet){const e=this._map.style.stylesheet;this.styleOwner=e.owner,this.styleId=e.id;}const t=this._map.style.tileManagers;for(const i in t){const o=t[i];if(o.used||o.usedForTerrain){const t=o.getSource();t.attribution&&!e.includes(t.attribution)&&e.push(t.attribution);}}e=e.filter((e=>String(e).trim())),e.sort(((e,t)=>e.length-t.length)),e=e.filter(((t,i)=>{for(let o=i+1;o{const e=this._container.children;if(e.length){const t=e[0];this._map.getCanvasContainer().offsetWidth<=640||this._compact?!1!==this._compact&&t.classList.add("maplibregl-compact"):t.classList.remove("maplibregl-compact");}},this.options=e;}getDefaultPosition(){return "bottom-left"}onAdd(e){var t;this._map=e,this._compact=null===(t=this.options)||void 0===t?void 0:t.compact,this._container=d.create("div","maplibregl-ctrl");const i=d.create("a","maplibregl-ctrl-logo");return i.target="_blank",i.rel="noopener nofollow",i.href="https://maplibre.org/",i.setAttribute("aria-label",this._map._getUIString("LogoControl.Title")),i.setAttribute("rel","noopener nofollow"),this._container.appendChild(i),this._container.style.display="block",this._map.on("resize",this._updateCompact),this._updateCompact(),this._container}onRemove(){this._container.remove(),this._map.off("resize",this._updateCompact),this._map=void 0,this._compact=void 0;}}class is{constructor(){this._queue=[],this._id=0,this._cleared=!1,this._currentlyRunning=!1;}add(e){const t=++this._id;return this._queue.push({callback:e,id:t,cancelled:!1}),t}remove(e){const t=this._currentlyRunning,i=t?this._queue.concat(t):this._queue;for(const t of i)if(t.id===e)return void(t.cancelled=!0)}run(e=0){if(this._currentlyRunning)throw new Error("Attempting to run(), but is already running.");const t=this._currentlyRunning=this._queue;this._queue=[];for(const i of t)if(!i.cancelled&&(i.callback(e),this._cleared))break;this._cleared=!1,this._currentlyRunning=!1;}clear(){this._currentlyRunning&&(this._cleared=!0),this._queue=[];}}var os=t.aS([{name:"a_pos3d",type:"Int16",components:3}]);class as extends t.E{constructor(e){super(),this._lastTilesetChange=c(),this.tileManager=e,this._tiles={},this._renderableTilesKeys=[],this._sourceTileCache={},this.minzoom=0,this.maxzoom=22,this.deltaZoom=1,this.tileSize=e._source.tileSize*2**this.deltaZoom,e.usedForTerrain=!0,e.tileSize=this.tileSize;}destruct(){this.tileManager.usedForTerrain=!1,this.tileManager.tileSize=null;}getSource(){return this.tileManager._source}update(e,i){this.tileManager.update(e,i),this._renderableTilesKeys=[];const o={};for(const a of Me(e,{tileSize:this.tileSize,minzoom:this.minzoom,maxzoom:this.maxzoom,reparseOverscaled:!1,terrain:i,calculateTileZoom:this.tileManager._source.calculateTileZoom}))o[a.key]=!0,this._renderableTilesKeys.push(a.key),this._tiles[a.key]||(a.terrainRttPosMatrix32f=new Float64Array(16),t.c7(a.terrainRttPosMatrix32f,0,t.a6,t.a6,0,0,1),this._tiles[a.key]=new de(a,this.tileSize),this._lastTilesetChange=c());for(const e in this._tiles)o[e]||delete this._tiles[e];}freeRtt(e){for(const t in this._tiles){const i=this._tiles[t];(!e||i.tileID.equals(e)||i.tileID.isChildOf(e)||e.isChildOf(i.tileID))&&(i.rtt=[]);}}getRenderableTiles(){return this._renderableTilesKeys.map((e=>this.getTileByID(e)))}getTileByID(e){return this._tiles[e]}getTerrainCoords(e,t){return t?this._getTerrainCoordsForTileRanges(e,t):this._getTerrainCoordsForRegularTile(e)}_getTerrainCoordsForRegularTile(e){const i={};for(const o of this._renderableTilesKeys){const a=this._tiles[o].tileID,r=e.clone(),s=t.bj();if(a.canonical.equals(e.canonical))t.c7(s,0,t.a6,t.a6,0,0,1);else if(a.canonical.isChildOf(e.canonical)){const i=a.canonical.z-e.canonical.z,o=a.canonical.x-(a.canonical.x>>i<>i<>i;t.c7(s,0,n,n,0,0,1),t.Q(s,s,[-o*n,-r*n,0]);}else {if(!e.canonical.isChildOf(a.canonical))continue;{const i=e.canonical.z-a.canonical.z,o=e.canonical.x-(e.canonical.x>>i<>i<>i;t.c7(s,0,t.a6,t.a6,0,0,1),t.Q(s,s,[o*n,r*n,0]),t.S(s,s,[1/2**i,1/2**i,0]);}}r.terrainRttPosMatrix32f=new Float32Array(s),i[o]=r;}return i}_getTerrainCoordsForTileRanges(e,i){const o={};for(const a of this._renderableTilesKeys){const r=this._tiles[a].tileID;if(!this._isWithinTileRanges(r,i))continue;const s=e.clone(),n=t.bj();if(r.canonical.z===e.canonical.z){const i=e.canonical.x-r.canonical.x+e.wrap*(1<e.canonical.z){const i=r.canonical.z-e.canonical.z,o=r.canonical.x-(r.canonical.x>>i<>i<>i),l=e.canonical.y-(r.canonical.y>>i),c=t.a6>>i;t.c7(n,0,c,c,0,0,1),t.Q(n,n,[-o*c+s*t.a6,-a*c+l*t.a6,0]);}else {const i=e.canonical.z-r.canonical.z,o=e.canonical.x-(e.canonical.x>>i<>i<>i)-r.canonical.x,l=(e.canonical.y>>i)-r.canonical.y,c=t.a6<a.maxzoom&&(r=a.maxzoom),r=a.minzoom&&!(null==s?void 0:s.dem);)s=this.findTileInCaches(e.scaledTo(r--).key);return s}findTileInCaches(e){let t=this.tileManager.getTileByID(e);return t||(t=this.tileManager._outOfViewCache.getByKey(e),t)}anyTilesAfterTime(e=Date.now()){return this._lastTilesetChange>=e}_isWithinTileRanges(e,t){const i=t[e.canonical.z];return !!i&&(e.wrap>i.minWrap||e.wrap=i.minTileXWrapped&&e.canonical.x<=i.maxTileXWrapped&&e.canonical.y>=i.minTileY&&e.canonical.y<=i.maxTileY)}}class rs{constructor(e,t,i){this._meshCache={},this.painter=e,this.tileManager=new as(t),this.options=i,this.exaggeration="number"==typeof i.exaggeration?i.exaggeration:1,this.qualityFactor=2,this.meshSize=128,this._demMatrixCache={},this.coordsIndex=[],this._coordsTextureSize=1024;}destroy(){this._fbo&&(this._fbo.destroy(),this._fbo=null),this._fboCoordsTexture&&(this._fboCoordsTexture.destroy(),this._fboCoordsTexture=null),this._fboDepthTexture&&(this._fboDepthTexture.destroy(),this._fboDepthTexture=null),this._emptyDemTexture&&(this._emptyDemTexture.destroy(),this._emptyDemTexture=null),this._emptyDepthTexture&&(this._emptyDepthTexture.destroy(),this._emptyDepthTexture=null),this._coordsTexture&&(this._coordsTexture.destroy(),this._coordsTexture=null);for(const e in this._meshCache)this._meshCache[e].destroy();this._meshCache={},this.tileManager.destruct();}getDEMElevation(e,i,o,a=t.a6){var r;const s=e.normalizeCoordinates(i,o,a);if(!s)return 0;const n=this.getTerrainData(s.tileID),l=null===(r=n.tile)||void 0===r?void 0:r.dem;if(!l)return 0;const c=t.cC([],[s.x/a*t.a6,s.y/a*t.a6],n.u_terrain_matrix),h=[c[0]*l.dim,c[1]*l.dim],u=Math.floor(h[0]),d=Math.floor(h[1]),_=h[0]-u,p=h[1]-d;return l.get(u,d)*(1-_)*(1-p)+l.get(u+1,d)*_*(1-p)+l.get(u,d+1)*(1-_)*p+l.get(u+1,d+1)*_*p}getElevationForLngLatZoom(e,i){if(!t.cD(i,e.wrap()))return 0;const{tileID:o,mercatorX:a,mercatorY:r}=this._getOverscaledTileIDFromLngLatZoom(e,i);return this.getElevation(o,a%t.a6,r%t.a6,t.a6)}getElevationForLngLat(e,t){const i=Me(t,{maxzoom:this.tileManager.maxzoom,minzoom:this.tileManager.minzoom,tileSize:512,terrain:this});let o=0;for(const e of i)e.canonical.z>o&&(o=Math.min(e.canonical.z,this.tileManager.maxzoom));return this.getElevationForLngLatZoom(e,o)}getElevation(e,i,o,a=t.a6){return this.getDEMElevation(e,i,o,a)*this.exaggeration}getTerrainData(e){var i,o;if(!this._emptyDemTexture){const e=this.painter.context,i=new t.R({width:1,height:1},new Uint8Array(4));this._emptyDepthTexture=new t.T(e,i,e.gl.RGBA,{premultiply:!1}),this._emptyDemUnpack=[0,0,0,0],this._emptyDemTexture=new t.T(e,new t.R({width:1,height:1}),e.gl.RGBA,{premultiply:!1}),this._emptyDemTexture.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),this._emptyDemMatrix=t.ap([]);}const a=this.tileManager.getSourceTile(e,!0);if((null==a?void 0:a.dem)&&(!a.demTexture||a.needsTerrainPrepare)){const e=this.painter.context;a.demTexture=this.painter.getTileTexture(a.dem.stride),a.demTexture?a.demTexture.update(a.dem.getPixels(),{premultiply:!1}):a.demTexture=new t.T(e,a.dem.getPixels(),e.gl.RGBA,{premultiply:!1}),a.demTexture.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),a.needsTerrainPrepare=!1;}const r=a&&a.toString()+a.tileID.key+e.key;if(r&&!this._demMatrixCache[r]){const i=this.tileManager.getSource().maxzoom;let o=e.canonical.z-a.tileID.canonical.z;e.overscaledZ>e.canonical.z&&(e.canonical.z>=i?o=e.canonical.z-i:t.w("cannot calculate elevation if elevation maxzoom > source.maxzoom"));const s=e.canonical.x-(e.canonical.x>>o<>o<>8<<4|e>>8,i[t+3]=0;const o=new t.R({width:this._coordsTextureSize,height:this._coordsTextureSize},new Uint8Array(i.buffer)),a=new t.T(e,o,e.gl.RGBA,{premultiply:!1});return a.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),this._coordsTexture=a,a}pointCoordinate(e){this.painter.maybeDrawDepth(!0),this.painter.maybeDrawCoords();const i=new Uint8Array(4),o=this.painter.context,a=o.gl,r=Math.round(e.x*this.painter.pixelRatio/devicePixelRatio),s=Math.round(e.y*this.painter.pixelRatio/devicePixelRatio),n=Math.round(this.painter.height/devicePixelRatio);o.bindFramebuffer.set(this.getFramebuffer("coords").framebuffer),a.readPixels(r,n-s-1,1,1,a.RGBA,a.UNSIGNED_BYTE,i),o.bindFramebuffer.set(null);const l=i[0]+(i[2]>>4<<8),c=i[1]+((15&i[2])<<8),h=this.coordsIndex[255-i[3]],u=h&&this.tileManager.getTileByID(h);if(!u)return null;const d=this._coordsTextureSize,_=(1<0,a=o&&0===e.canonical.y,r=o&&e.canonical.y===(1<e.id!==t)),this._recentlyUsed.push(e.id);}stampObject(e){e.stamp=++this._stamp;}getOrCreateFreeObject(){for(const e of this._recentlyUsed)if(!this._objects[e].inUse)return this._objects[e];if(this._objects.length>=this._size)throw new Error("No free RenderPool available, call freeAllObjects() required!");const e=this._createObject(this._objects.length);return this._objects.push(e),e}freeObject(e){e.inUse=!1;}freeAllObjects(){for(const e of this._objects)this.freeObject(e);}isFull(){return !(this._objects.length!e.inUse))}}const ns={background:!0,fill:!0,line:!0,raster:!0,hillshade:!0,"color-relief":!0};class ls{constructor(e,t){this.painter=e,this.terrain=t,this.pool=new ss(e.context,30,t.tileManager.tileSize*t.qualityFactor);}destruct(){this.pool.destruct();}getTexture(e){return this.pool.getObjectForId(e.rtt[this._stacks.length-1].id).texture}prepareForRender(e,t){var i,o,a;this._stacks=[],this._prevType=null,this._rttTiles=[],this._renderableTiles=this.terrain.tileManager.getRenderableTiles(),this._renderableLayerIds=e._order.filter((i=>!e._layers[i].isHidden(t))),this._coordsAscending={};for(const t in e.tileManagers){this._coordsAscending[t]={};const i=e.tileManagers[t].getVisibleCoordinates(),o=e.tileManagers[t].getSource(),r=o instanceof te?o.terrainTileRanges:null;for(const e of i){const i=this.terrain.tileManager.getTerrainCoords(e,r);for(const e in i)(a=this._coordsAscending[t])[e]||(a[e]=[]),this._coordsAscending[t][e].push(i[e]);}}this._rttFingerprints={};for(const t of e._order){const a=e._layers[t],r=a.source;if(ns[a.type]&&!this._rttFingerprints[r]){this._rttFingerprints[r]={};const t=null!==(o=null===(i=e.tileManagers[r])||void 0===i?void 0:i.getState().revision)&&void 0!==o?o:0;for(const e in this._coordsAscending[r])this._rttFingerprints[r][e]=`${this._coordsAscending[r][e].map((e=>e.key)).sort().join()}#${t}`;}}for(const e of this._renderableTiles)for(const t in this._rttFingerprints){const i=this._rttFingerprints[t][e.tileID.key];i&&i!==e.rttFingerprint[t]&&(e.rtt=[]);}}renderLayer(e,i){if(e.isHidden(this.painter.transform.zoom))return !1;const o=Object.assign(Object.assign({},i),{isRenderingToTexture:!0}),a=e.type,r=this.painter,s=this._renderableLayerIds[this._renderableLayerIds.length-1]===e.id;if(ns[a]&&(this._prevType&&ns[this._prevType]||this._stacks.push([]),this._prevType=a,this._stacks[this._stacks.length-1].push(e.id),!s))return !0;if(ns[this._prevType]||ns[a]&&s){this._prevType=a;const e=this._stacks.length-1,i=this._stacks[e]||[];for(const a of this._renderableTiles){if(this.pool.isFull()&&(qa(this.painter,this.terrain,this._rttTiles,o),this._rttTiles=[],this.pool.freeAllObjects()),this._rttTiles.push(a),a.rtt[e]){const t=this.pool.getObjectForId(a.rtt[e].id);if(t.stamp===a.rtt[e].stamp){this.pool.useObject(t);continue}}const s=this.pool.getOrCreateFreeObject();this.pool.useObject(s),this.pool.stampObject(s),a.rtt[e]={id:s.id,stamp:s.stamp},r.context.bindFramebuffer.set(s.fbo.framebuffer),r.context.clear({color:t.bo.transparent,stencil:0}),r.currentStencilSource=void 0;for(const e of i){const t=r.style._layers[e],i=t.source?this._coordsAscending[t.source][a.tileID.key]:[a.tileID];r.context.viewport.set([0,0,s.fbo.width,s.fbo.height]),r._renderTileClippingMasks(t,i,!0),r.renderLayer(r,r.style.tileManagers[t.source],t,i,o),t.source&&(a.rttFingerprint[t.source]=this._rttFingerprints[t.source][a.tileID.key]);}}return qa(this.painter,this.terrain,this._rttTiles,o),this._rttTiles=[],this.pool.freeAllObjects(),ns[a]}return !1}}const cs={"AttributionControl.ToggleAttribution":"Toggle attribution","AttributionControl.MapFeedback":"Map feedback","FullscreenControl.Enter":"Enter fullscreen","FullscreenControl.Exit":"Exit fullscreen","GeolocateControl.FindMyLocation":"Find my location","GeolocateControl.LocationNotAvailable":"Location not available","LogoControl.Title":"MapLibre logo","Map.Title":"Map","Marker.Title":"Map marker","NavigationControl.ResetBearing":"Drag to rotate map, click to reset north","NavigationControl.ZoomIn":"Zoom in","NavigationControl.ZoomOut":"Zoom out","Popup.Close":"Close popup","ScaleControl.Feet":"ft","ScaleControl.Meters":"m","ScaleControl.Kilometers":"km","ScaleControl.Miles":"mi","ScaleControl.NauticalMiles":"nm","GlobeControl.Enable":"Enable globe","GlobeControl.Disable":"Disable globe","TerrainControl.Enable":"Enable terrain","TerrainControl.Disable":"Disable terrain","CooperativeGesturesHandler.WindowsHelpText":"Use Ctrl + scroll to zoom the map","CooperativeGesturesHandler.MacHelpText":"Use ⌘ + scroll to zoom the map","CooperativeGesturesHandler.MobileHelpText":"Use two fingers to move the map"},hs=i,us={hash:!1,interactive:!0,bearingSnap:7,zoomSnap:0,attributionControl:Jr,maplibreLogo:!1,refreshExpiredTiles:!0,canvasContextAttributes:{antialias:!1,preserveDrawingBuffer:!1,powerPreference:"high-performance",failIfMajorPerformanceCaveat:!1,desynchronized:!1,contextType:void 0},scrollZoom:!0,minZoom:-2,maxZoom:22,minPitch:0,maxPitch:60,boxZoom:!0,dragRotate:!0,dragPan:!0,keyboard:!0,doubleClickZoom:!0,touchZoomRotate:!0,touchPitch:!0,cooperativeGestures:!1,trackResize:!0,center:[0,0],elevation:0,zoom:0,bearing:0,pitch:0,roll:0,renderWorldCopies:!0,maxTileCacheSize:null,maxTileCacheZoomLevels:t.c.MAX_TILE_CACHE_ZOOM_LEVELS,transformRequest:null,transformCameraUpdate:null,transformConstrain:null,fadeDuration:300,crossSourceCollisions:!0,clickTolerance:3,localIdeographFontFamily:"sans-serif",pitchWithRotate:!0,rollEnabled:!1,reduceMotion:void 0,validateStyle:!0,maxCanvasSize:[4096,4096],cancelPendingTileRequestsWhileZooming:!0,centerClampedToGround:!0,experimentalZoomLevelsToOverscale:void 0,anisotropicFilterPitch:20};let ds=class extends Qr{get _ownerWindow(){var e,t;return (null===(t=null===(e=this._container)||void 0===e?void 0:e.ownerDocument)||void 0===t?void 0:t.defaultView)||window}constructor(e){var i,o,a;const r=Object.assign(Object.assign(Object.assign({},us),e),{canvasContextAttributes:Object.assign(Object.assign({},us.canvasContextAttributes),e.canvasContextAttributes)});if(null!=r.minZoom&&null!=r.maxZoom&&r.minZoom>r.maxZoom)throw new Error("maxZoom must be greater than or equal to minZoom");if(null!=r.minPitch&&null!=r.maxPitch&&r.minPitch>r.maxPitch)throw new Error("maxPitch must be greater than or equal to minPitch");if(null!=r.minPitch&&r.minPitch<0)throw new Error("minPitch must be greater than or equal to 0");if(null!=r.maxPitch&&r.maxPitch>180)throw new Error("maxPitch must be less than or equal to 180");const s=new Xt,l=new Jt;void 0!==r.minZoom&&s.setMinZoom(r.minZoom),void 0!==r.maxZoom&&s.setMaxZoom(r.maxZoom),void 0!==r.minPitch&&s.setMinPitch(r.minPitch),void 0!==r.maxPitch&&s.setMaxPitch(r.maxPitch),void 0!==r.renderWorldCopies&&s.setRenderWorldCopies(r.renderWorldCopies),null!==r.transformConstrain&&s.setConstrainOverride(r.transformConstrain),super(s,l,{bearingSnap:r.bearingSnap,zoomSnap:r.zoomSnap}),this._idleTriggered=!1,this._crossFadingFactor=1,this._renderTaskQueue=new is,this._controls=[],this._mapId=t.ad(),this._lostContextStyle={style:null,images:null},this._contextLost=e=>{if(e.preventDefault(),this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this.painter.destroy(),this._lostContextStyle=this._getStyleAndImages(),this.style){for(const e of Object.values(this.style._layers))if("custom"===e.type&&console.warn(`Custom layer with id '${e.id}' cannot be restored after WebGL context loss. You will need to re-add it manually after context restoration.`),e._listeners)for(const[t]of Object.entries(e._listeners))console.warn(`Custom layer with id '${e.id}' had event listeners for event '${t}' which cannot be restored after WebGL context loss. You will need to re-add them manually after context restoration.`);this.style.destroy(),this.style=null,this.fire(new t.n("webglcontextlost",{originalEvent:e}));}else this.fire(new t.n("webglcontextlost",{originalEvent:e}));},this._contextRestored=e=>{this._lostContextStyle.style&&this.setStyle(this._lostContextStyle.style,{diff:!1}),this._lostContextStyle.images&&this.style&&(this.style.imageManager.images=this._lostContextStyle.images),this._lostContextStyle={style:null,images:null},this._setupPainter(),this.resize(),this._update(),this._resizeInternal(),this.fire(new t.n("webglcontextrestored",{originalEvent:e}));},this._onMapScroll=e=>{if(e.target===this._container)return this._container.scrollTop=0,this._container.scrollLeft=0,!1},this._onWindowOnline=()=>{this._update();},this._interactive=r.interactive,this._maxTileCacheSize=r.maxTileCacheSize,this._maxTileCacheZoomLevels=r.maxTileCacheZoomLevels,this._canvasContextAttributes=Object.assign({},r.canvasContextAttributes),this._trackResize=!0===r.trackResize,this._bearingSnap=r.bearingSnap,this._zoomSnap=r.zoomSnap,this._centerClampedToGround=r.centerClampedToGround,this._refreshExpiredTiles=!0===r.refreshExpiredTiles,this._fadeDuration=r.fadeDuration,this._crossSourceCollisions=!0===r.crossSourceCollisions,this._collectResourceTiming=!0===r.collectResourceTiming,this._locale=Object.assign(Object.assign({},cs),r.locale),this._clickTolerance=r.clickTolerance,this._overridePixelRatio=r.pixelRatio,this._maxCanvasSize=r.maxCanvasSize,this._zoomLevelsToOverscale=r.experimentalZoomLevelsToOverscale,this.transformCameraUpdate=r.transformCameraUpdate,this.transformConstrain=r.transformConstrain,this.cancelPendingTileRequestsWhileZooming=!0===r.cancelPendingTileRequestsWhileZooming,this.setAnisotropicFilterPitch(r.anisotropicFilterPitch),void 0!==r.reduceMotion&&(n.prefersReducedMotion=r.reduceMotion),this._imageQueueHandle=u.addThrottleControl((()=>this.isMoving())),this._requestManager=new _(r.transformRequest),this._container=this._resolveContainer(r.container),r.maxBounds&&this.setMaxBounds(r.maxBounds),this._setupContainer(),this._setupPainter(),this.on("move",(()=>this._update(!1))),this.on("moveend",(()=>this._update(!1))),this.on("zoom",(()=>this._update(!0))),this.on("terrain",(()=>{this.painter.terrainFacilitator.depthDirty=!0,this._update(!0);})),this.once("idle",(()=>this._idleTriggered=!0)),"undefined"!=typeof window&&(this._ownerWindow.addEventListener("online",this._onWindowOnline,!1),this._setupResizeObserver()),this.handlers=new Yr(this,r),this._hash=r.hash?new Ya("string"==typeof r.hash&&r.hash||void 0).addTo(this):void 0,(null===(i=this._hash)||void 0===i?void 0:i._onHashChange())||(this.jumpTo({center:r.center,elevation:r.elevation,zoom:r.zoom,bearing:r.bearing,pitch:r.pitch,roll:r.roll}),r.bounds&&(this.resize(),this.fitBounds(r.bounds,t.e({},r.fitBoundsOptions,{duration:0}))));const c="string"==typeof r.style||!("globe"===(null===(a=null===(o=r.style)||void 0===o?void 0:o.projection)||void 0===a?void 0:a.type));this.resize(null,c),this._localIdeographFontFamily=r.localIdeographFontFamily,this._validateStyle=r.validateStyle,r.style&&this.setStyle(r.style,{localIdeographFontFamily:r.localIdeographFontFamily}),r.attributionControl&&this.addControl(new es("boolean"==typeof r.attributionControl?void 0:r.attributionControl)),r.maplibreLogo&&this.addControl(new ts,r.logoPosition),this.on("style.load",(()=>{if(c||this._resizeTransform(),this.transform.unmodified){const e=t.V(this.style.stylesheet,["center","zoom","bearing","pitch","roll"]);this.jumpTo(e);}})),this.on("data",(e=>{this._update("style"===e.dataType),this.fire(new t.n(`${e.dataType}data`,e));})),this.on("dataloading",(e=>{this.fire(new t.n(`${e.dataType}dataloading`,e));})),this.on("dataabort",(e=>{this.fire(new t.n("sourcedataabort",e));}));}_getMapId(){return this._mapId}setGlobalStateProperty(e,t){return this.style.setGlobalStateProperty(e,t),this._update(!0)}getGlobalState(){return this.style.getGlobalState()}addControl(e,i){if(void 0===i&&(i=e.getDefaultPosition?e.getDefaultPosition():"top-right"),!(null==e?void 0:e.onAdd))return this.fire(new t.l(new Error("Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.")));const o=e.onAdd(this);this._controls.push(e);const a=this._controlPositions[i];return i.includes("bottom")?a.insertBefore(o,a.firstChild):a.appendChild(o),this}removeControl(e){if(!(null==e?void 0:e.onRemove))return this.fire(new t.l(new Error("Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.")));const i=this._controls.indexOf(e);return i>-1&&this._controls.splice(i,1),e.onRemove(this),this}hasControl(e){return this._controls.includes(e)}coveringTiles(e){return Me(this.transform,e)}calculateCameraOptionsFromTo(e,t,i,o){return null==o&&this.terrain&&(o=this.terrain.getElevationForLngLat(i,this.transform)),super.calculateCameraOptionsFromTo(e,t,i,o)}resize(e,i=!0){if(null!==this._lostContextStyle.style)return this;this._resizeInternal(i);const o=!this._moving;return o&&(this.stop(),this.fire(new t.n("movestart",e)).fire(new t.n("move",e))),this.fire(new t.n("resize",e)),o&&this.fire(new t.n("moveend",e)),this}_resizeInternal(e=!0){const[t,i]=this._containerDimensions(),o=this._getClampedPixelRatio(t,i);if(this._resizeCanvas(t,i,o),this.painter.resize(t,i,o),this.painter.overLimit()){const e=this.painter.context.gl;this._maxCanvasSize=[e.drawingBufferWidth,e.drawingBufferHeight];const o=this._getClampedPixelRatio(t,i);this._resizeCanvas(t,i,o),this.painter.resize(t,i,o);}this._resizeTransform(e);}_resizeTransform(e=!0){var t;const[i,o]=this._containerDimensions();this.transform.resize(i,o,e),null===(t=this._requestedCameraState)||void 0===t||t.resize(i,o,e);}_getClampedPixelRatio(e,t){const{0:i,1:o}=this._maxCanvasSize,a=this.getPixelRatio(),r=e*a,s=t*a;return Math.min(r>i?i/r:1,s>o?o/s:1)*a}getPixelRatio(){var e;return null!==(e=this._overridePixelRatio)&&void 0!==e?e:devicePixelRatio}setPixelRatio(e){this._overridePixelRatio=e,this.resize();}getBounds(){return this.transform.getBounds()}getMaxBounds(){return this.transform.getMaxBounds()}setMaxBounds(e){return this.transform.setMaxBounds(G.convert(e)),this._update()}setMinZoom(e){if((e=null==e?-2:e)>=-2&&e<=this.transform.maxZoom){const i=this.transform.zoom,o=this._getTransformForUpdate();return o.setMinZoom(e),this._applyUpdatedTransform(o),this._update(),i!==this.transform.zoom&&this.fire(new t.n("zoomstart")).fire(new t.n("zoom")).fire(new t.n("zoomend")).fire(new t.n("movestart")).fire(new t.n("move")).fire(new t.n("moveend")),this}throw new Error("minZoom must be between -2 and the current maxZoom, inclusive")}getMinZoom(){return this.transform.minZoom}setMaxZoom(e){if((e=null==e?22:e)>=this.transform.minZoom){const i=this.transform.zoom,o=this._getTransformForUpdate();return o.setMaxZoom(e),this._applyUpdatedTransform(o),this._update(),i!==this.transform.zoom&&this.fire(new t.n("zoomstart")).fire(new t.n("zoom")).fire(new t.n("zoomend")).fire(new t.n("movestart")).fire(new t.n("move")).fire(new t.n("moveend")),this}throw new Error("maxZoom must be greater than the current minZoom")}getMaxZoom(){return this.transform.maxZoom}setMinPitch(e){if((e=null==e?0:e)<0)throw new Error("minPitch must be greater than or equal to 0");if(e>=0&&e<=this.transform.maxPitch){const i=this.transform.pitch,o=this._getTransformForUpdate();return o.setMinPitch(e),this._applyUpdatedTransform(o),this._update(),i!==this.transform.pitch&&this.fire(new t.n("pitchstart")).fire(new t.n("pitch")).fire(new t.n("pitchend")).fire(new t.n("movestart")).fire(new t.n("move")).fire(new t.n("moveend")),this}throw new Error("minPitch must be between 0 and the current maxPitch, inclusive")}getMinPitch(){return this.transform.minPitch}setMaxPitch(e){if((e=null==e?60:e)>180)throw new Error("maxPitch must be less than or equal to 180");if(e>=this.transform.minPitch){const i=this.transform.pitch,o=this._getTransformForUpdate();return o.setMaxPitch(e),this._applyUpdatedTransform(o),this._update(),i!==this.transform.pitch&&this.fire(new t.n("pitchstart")).fire(new t.n("pitch")).fire(new t.n("pitchend")).fire(new t.n("movestart")).fire(new t.n("move")).fire(new t.n("moveend")),this}throw new Error("maxPitch must be greater than the current minPitch")}getMaxPitch(){return this.transform.maxPitch}getAnisotropicFilterPitch(){return this._anisotropicFilterPitch}setAnisotropicFilterPitch(e){if((e=null==e?20:e)>180)throw new Error("anisotropicFilterPitch must be less than or equal to 180");if(e<0)throw new Error("anisotropicFilterPitch must be greater than or equal to 0");return this._anisotropicFilterPitch=e,this._update()}getRenderWorldCopies(){return this.transform.renderWorldCopies}setRenderWorldCopies(e){return this.transform.setRenderWorldCopies(e),this._update()}setTransformConstrain(e){return this.transform.setConstrainOverride(e),this._update()}project(e){return this.transform.locationToScreenPoint(t.W.convert(e),this.style&&this.terrain)}unproject(e){return this.transform.screenPointToLocation(t.P.convert(e),this.terrain)}isMoving(){var e;return this._moving||(null===(e=this.handlers)||void 0===e?void 0:e.isMoving())}isZooming(){var e;return this._zooming||(null===(e=this.handlers)||void 0===e?void 0:e.isZooming())}isRotating(){var e;return this._rotating||(null===(e=this.handlers)||void 0===e?void 0:e.isRotating())}_createDelegatedListener(e,t,i){if("mouseenter"===e||"mouseover"===e){let o=!1;const a=a=>{const r=t.filter((e=>this.getLayer(e))),s=0!==r.length?this.queryRenderedFeatures(a.point,{layers:r}):[];s.length?o||(o=!0,i.call(this,new nr(e,this,a.originalEvent,{features:s}))):o=!1;};return {layers:t,listener:i,delegates:{mousemove:a,mouseout:()=>{o=!1;}}}}if("mouseleave"===e||"mouseout"===e){let o=!1;const a=a=>{const r=t.filter((e=>this.getLayer(e)));(0!==r.length?this.queryRenderedFeatures(a.point,{layers:r}):[]).length?o=!0:o&&(o=!1,i.call(this,new nr(e,this,a.originalEvent)));},r=t=>{o&&(o=!1,i.call(this,new nr(e,this,t.originalEvent)));};return {layers:t,listener:i,delegates:{mousemove:a,mouseout:r}}}{const o=e=>{const o=t.filter((e=>this.getLayer(e))),a=0!==o.length?this.queryRenderedFeatures(e.point,{layers:o}):[];a.length&&(e.features=a,i.call(this,e),delete e.features);};return {layers:t,listener:i,delegates:{[e]:o}}}}_saveDelegatedListener(e,t){var i;this._delegatedListeners||(this._delegatedListeners={}),(i=this._delegatedListeners)[e]||(i[e]=[]),this._delegatedListeners[e].push(t);}_removeDelegatedListener(e,t,i){var o;if(!(null===(o=this._delegatedListeners)||void 0===o?void 0:o[e]))return;const a=this._delegatedListeners[e];for(let e=0;et.includes(e)))){for(const e in o.delegates)this.off(e,o.delegates[e]);return void a.splice(e,1)}}}on(e,t,i){if(void 0===i)return super.on(e,t);const o="string"==typeof t?[t]:t,a=this._createDelegatedListener(e,o,i);this._saveDelegatedListener(e,a);for(const e in a.delegates)this.on(e,a.delegates[e]);return {unsubscribe:()=>{this._removeDelegatedListener(e,o,i);}}}once(e,t,i){if(void 0===i)return super.once(e,t);const o="string"==typeof t?[t]:t,a=this._createDelegatedListener(e,o,i);for(const t in a.delegates){const r=a.delegates[t];a.delegates[t]=(...t)=>{this._removeDelegatedListener(e,o,i),r(...t);};}this._saveDelegatedListener(e,a);for(const e in a.delegates)this.once(e,a.delegates[e]);return this}off(e,t,i){return void 0===i?super.off(e,t):(this._removeDelegatedListener(e,"string"==typeof t?[t]:t,i),this)}queryRenderedFeatures(e,i){if(!this.style)return [];let o;const a=e instanceof t.P||Array.isArray(e),r=a?e:[[0,0],[this.transform.width,this.transform.height]];if(i||(i=(a?{}:e)||{}),r instanceof t.P||"number"==typeof r[0])o=[t.P.convert(r)];else {const e=t.P.convert(r[0]),i=t.P.convert(r[1]);o=[e,new t.P(i.x,e.y),i,new t.P(e.x,i.y),e];}return this.style.queryRenderedFeatures(o,i,this.transform)}querySourceFeatures(e,t){return this.style.querySourceFeatures(e,t)}setStyle(e,i){return !1!==(i=t.e({},{localIdeographFontFamily:this._localIdeographFontFamily,validate:this._validateStyle},i)).diff&&i.localIdeographFontFamily===this._localIdeographFontFamily&&this.style&&e?(this._diffStyle(e,i),this):(this._localIdeographFontFamily=i.localIdeographFontFamily,this._updateStyle(e,i))}setTransformRequest(e){return this._requestManager.setTransformRequest(e),this}_getUIString(e){const t=this._locale[e];if(null==t)throw new Error(`Missing UI string '${e}'`);return t}_updateStyle(e,t){var i,o,a;if(null===(i=this._diffStyleRequest)||void 0===i||i.abort(),this._diffStyleRequest=null,t.transformStyle&&this.style&&!this.style._loaded)return void this.style.once("style.load",(()=>this._updateStyle(e,t)));const r=this.style&&t.transformStyle?this.style.serialize():void 0;return this.style&&(this.style.setEventedParent(null),this.style._remove(!e)),e?(this.style=new ki(this,t||{}),this.style.setEventedParent(this,{style:this.style}),"string"==typeof e?this.style.loadURL(e,t,r):this.style.loadJSON(e,t,r),this):(this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),null===(a=null===(o=this.style)||void 0===o?void 0:o.projection)||void 0===a||a.destroy(),delete this.style,this)}_lazyInitEmptyStyle(){this.style||(this.style=new ki(this,{}),this.style.setEventedParent(this,{style:this.style}),this.style.loadEmpty());}_diffStyle(e,i){return t._(this,void 0,void 0,(function*(){var o;if(null===(o=this._diffStyleRequest)||void 0===o||o.abort(),"string"==typeof e){const o=e;this._diffStyleRequest=new AbortController;const a=this._diffStyleRequest;try{const e=yield this._requestManager.transformRequest(o,"Style");if(a.signal.aborted)return void(this._diffStyleRequest=null);const r=yield t.k(e,a);this._diffStyleRequest=null,this._updateDiff(r.data,i);}catch(e){this._diffStyleRequest=null,t.$(e)||this.fire(new t.l(t.d(e)));}}else "object"==typeof e&&(this._diffStyleRequest=null,this._updateDiff(e,i));}))}_updateDiff(e,i){try{this.style.setState(e,i)&&this._update(!0);}catch(o){t.w(`Unable to perform style diff: ${t.d(o).message}. Rebuilding the style from scratch.`),this._updateStyle(e,i);}}getStyle(){if(this.style)return this.style.serialize()}_getStyleAndImages(){return this.style?{style:this.style.serialize(),images:this.style.imageManager.cloneImages()}:{style:null,images:{}}}isStyleLoaded(){if(this.style)return this.style.loaded();t.w("There is no style added to the map.");}addSource(e,t){return this._lazyInitEmptyStyle(),this.style.addSource(e,t),this._update(!0)}isSourceLoaded(e){var i;const o=null===(i=this.style)||void 0===i?void 0:i.tileManagers[e];if(void 0!==o)return o.loaded();this.fire(new t.l(new Error(`There is no tile manager with ID '${e}'`)));}setTerrain(e){if(this.style._checkLoaded(),this._terrainDataCallback&&this.style.off("data",this._terrainDataCallback),e){const i=this.style.tileManagers[e.source];if(!i)throw new Error(`cannot load terrain, because there exists no source with ID: ${e.source}`);null===this.terrain&&i.reload();for(const i in this.style._layers){const o=this.style._layers[i];"hillshade"===o.type&&o.source===e.source&&t.w("You are using the same source for a hillshade layer and for 3D terrain. Please consider using two separate sources to improve rendering quality."),"color-relief"===o.type&&o.source===e.source&&t.w("You are using the same source for a color-relief layer and for 3D terrain. Please consider using two separate sources to improve rendering quality.");}this.terrain=new rs(this.painter,i,e),this.painter.renderToTexture=new ls(this.painter,this.terrain),this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),this.transform.setElevation(this.terrain.getElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),this._terrainDataCallback=t=>{var i;"style"===t.dataType?this.terrain.tileManager.freeRtt():"source"===t.dataType&&t.tile&&(t.sourceId!==e.source||this._elevationFreeze||(this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),this._centerClampedToGround&&this.transform.setElevation(this.terrain.getElevationForLngLatZoom(this.transform.center,this.transform.tileZoom))),"image"===(null===(i=t.source)||void 0===i?void 0:i.type)?this.terrain.tileManager.freeRtt():this.terrain.tileManager.freeRtt(t.tile.tileID));},this.style.on("data",this._terrainDataCallback);}else this.terrain&&this.terrain.destroy(),this.terrain=null,this.painter.renderToTexture&&this.painter.renderToTexture.destruct(),this.painter.renderToTexture=null,this.transform.setMinElevationForCurrentTile(0),this._centerClampedToGround&&this.transform.setElevation(0);return this.fire(new t.n("terrain",{terrain:e})),this}getTerrain(){var e,t;return null!==(t=null===(e=this.terrain)||void 0===e?void 0:e.options)&&void 0!==t?t:null}areTilesLoaded(){var e;const t=null===(e=this.style)||void 0===e?void 0:e.tileManagers;for(const e of Object.values(t))if(!e.areTilesLoaded())return !1;return !0}removeSource(e){return this.style.removeSource(e),this._update(!0)}getSource(e){return this.style.getSource(e)}setSourceTileLodParams(e,t,i){if(i){const o=this.getSource(i);if(!o)throw new Error(`There is no source with ID "${i}", cannot set LOD parameters`);o.calculateTileZoom=Te(Math.max(1,e),Math.max(1,t));}else for(const i in this.style.tileManagers)this.style.tileManagers[i].getSource().calculateTileZoom=Te(Math.max(1,e),Math.max(1,t));return this._update(!0),this}refreshTiles(e,i){const o=this.style.tileManagers[e];if(!o)throw new Error(`There is no tile manager with ID "${e}", cannot refresh tile`);void 0===i?o.reload(!0):o.refreshTiles(i.map((e=>new t.aa(e.z,e.x,e.y))));}addImage(e,i,o={}){const{pixelRatio:a=1,sdf:r=!1,stretchX:s,stretchY:l,content:c,textFitWidth:h,textFitHeight:u}=o;if(this._lazyInitEmptyStyle(),!(i instanceof HTMLImageElement||t.b(i))){if(void 0===i.width||void 0===i.height)return this.fire(new t.l(new Error("Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, or object with `width`, `height`, and `data` properties with the same format as `ImageData`")));{const{width:o,height:n,data:d}=i,_=i;return this.style.addImage(e,{data:new t.R({width:o,height:n},new Uint8Array(d)),pixelRatio:a,stretchX:s,stretchY:l,content:c,textFitWidth:h,textFitHeight:u,sdf:r,version:0,userImage:_}),_.onAdd&&_.onAdd(this,e),this}}{const{width:o,height:d,data:_}=n.getImageData(i);this.style.addImage(e,{data:new t.R({width:o,height:d},_),pixelRatio:a,stretchX:s,stretchY:l,content:c,textFitWidth:h,textFitHeight:u,sdf:r,version:0});}}updateImage(e,i){const o=this.style.getImage(e);if(!o)return this.fire(new t.l(new Error("The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.")));const a=i instanceof HTMLImageElement||t.b(i)?n.getImageData(i):i,{width:r,height:s,data:l}=a;if(void 0===r||void 0===s)return this.fire(new t.l(new Error("Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, or object with `width`, `height`, and `data` properties with the same format as `ImageData`")));if(r!==o.data.width||s!==o.data.height)return this.fire(new t.l(new Error("The width and height of the updated image must be that same as the previous version of the image")));const c=!(i instanceof HTMLImageElement||t.b(i));return o.data.replace(l,c),this.style.updateImage(e,o),this}getImage(e){return this.style.getImage(e)}hasImage(e){return e?!!this.style.getImage(e):(this.fire(new t.l(new Error("Missing required image id"))),!1)}removeImage(e){this.style.removeImage(e);}loadImage(e){return t._(this,void 0,void 0,(function*(){return u.getImage(yield this._requestManager.transformRequest(e,"Image"),new AbortController)}))}listImages(){return this.style.listImages()}addLayer(e,t){return this._lazyInitEmptyStyle(),this.style.addLayer(e,t),this._update(!0)}moveLayer(e,t){return this.style.moveLayer(e,t),this._update(!0)}removeLayer(e){return this.style.removeLayer(e),this._update(!0)}getLayer(e){return this.style.getLayer(e)}getLayersOrder(){return this.style.getLayersOrder()}setLayerZoomRange(e,t,i){return this.style.setLayerZoomRange(e,t,i),this._update(!0)}setFilter(e,t,i={}){return this.style.setFilter(e,t,i),this._update(!0)}getFilter(e){return this.style.getFilter(e)}setPaintProperty(e,t,i,o={}){return this.style.setPaintProperty(e,t,i,o),this._update(!0)}getPaintProperty(e,t){return this.style.getPaintProperty(e,t)}setLayoutProperty(e,t,i,o={}){return this.style.setLayoutProperty(e,t,i,o),this._update(!0)}getLayoutProperty(e,t){return this.style.getLayoutProperty(e,t)}setGlyphs(e,t={}){return this._lazyInitEmptyStyle(),this.style.setGlyphs(e,t),this._update(!0)}getGlyphs(){return this.style.getGlyphsUrl()}addSprite(e,t,i={}){return this._lazyInitEmptyStyle(),this.style.addSprite(e,t,i,(e=>{e||this._update(!0);})),this}removeSprite(e){return this._lazyInitEmptyStyle(),this.style.removeSprite(e),this._update(!0)}getSprite(){return this.style.getSprite()}setSprite(e,t={}){return this._lazyInitEmptyStyle(),this.style.setSprite(e,t,(e=>{e||this._update(!0);})),this}setLight(e,t={}){return this._lazyInitEmptyStyle(),this.style.setLight(e,t),this._update(!0)}getLight(){return this.style.getLight()}setSky(e,t={}){return this._lazyInitEmptyStyle(),this.style.setSky(e,t),this._update(!0)}getSky(){return this.style.getSky()}setFeatureState(e,t){return this.style.setFeatureState(e,t),this._update()}removeFeatureState(e,t){return this.style.removeFeatureState(e,t),this._update()}getFeatureState(e){return this.style.getFeatureState(e)}getContainer(){return this._container}getCanvasContainer(){return this._canvasContainer}getCanvas(){return this._canvas}_containerDimensions(){let e=0,t=0;return this._container&&(e=this._container.clientWidth||400,t=this._container.clientHeight||300),[e,t]}_setupResizeObserver(){var e;let t=!1;const i=Ka((e=>{this._trackResize&&!this._removed&&(this.resize(e),this.redraw());}),50),o=null!==(e=this._ownerWindow.ResizeObserver)&&void 0!==e?e:ResizeObserver;this._resizeObserver=new o((e=>{t?i(e):t=!0;})),this._resizeObserver.observe(this._container);}_resolveContainer(e){if("string"==typeof e){const t=document.getElementById(e);if(!t)throw new Error(`Container '${e}' not found.`);return t}if(e instanceof HTMLElement)return e;if(e&&"object"==typeof e&&1===e.nodeType)return e;throw new Error("Invalid type: 'container' must be a String or HTMLElement.")}_setupContainer(){const e=this._container;e.classList.add("maplibregl-map");const t=this._canvasContainer=d.create("div","maplibregl-canvas-container",e);this._interactive&&t.classList.add("maplibregl-interactive"),this._canvas=d.create("canvas","maplibregl-canvas",t),this._canvas.addEventListener("webglcontextlost",this._contextLost,!1),this._canvas.addEventListener("webglcontextrestored",this._contextRestored,!1),this._canvas.setAttribute("tabindex",this._interactive?"0":"-1"),this._canvas.setAttribute("aria-label",this._getUIString("Map.Title")),this._canvas.setAttribute("role","region");const i=this._containerDimensions(),o=this._getClampedPixelRatio(i[0],i[1]);this._resizeCanvas(i[0],i[1],o);const a=this._controlContainer=d.create("div","maplibregl-control-container",e),r=this._controlPositions={};for(const e of ["top-left","top-right","bottom-left","bottom-right"])r[e]=d.create("div",`maplibregl-ctrl-${e} `,a);this._container.addEventListener("scroll",this._onMapScroll,!1);}_resizeCanvas(e,t,i){this._canvas.width=Math.floor(i*e),this._canvas.height=Math.floor(i*t),this._canvas.style.width=`${e}px`,this._canvas.style.height=`${t}px`;}_setupPainter(){const e=Object.assign(Object.assign({},this._canvasContextAttributes),{alpha:!0,depth:!0,stencil:!0,premultipliedAlpha:!0});let t=null;this._canvas.addEventListener("webglcontextcreationerror",(i=>{t={requestedAttributes:e},i&&(t.statusMessage=i.statusMessage,t.type=i.type);}),{once:!0});let i=null;if(i=this._canvasContextAttributes.contextType?this._canvas.getContext(this._canvasContextAttributes.contextType,e):this._canvas.getContext("webgl2",e)||this._canvas.getContext("webgl",e),!i){const e="Failed to initialize WebGL";throw t?(t.message=e,new Error(JSON.stringify(t))):new Error(e)}this.painter=new Xa(i,this.transform);}migrateProjection(e,i){super.migrateProjection(e,i),this.painter.transform=e,this.fire(new t.n("projectiontransition",{newProjection:this.style.projection.name}));}loaded(){return !this._styleDirty&&!this._sourcesDirty&&!!this.style&&this.style.loaded()}_update(e){var t;return (null===(t=this.style)||void 0===t?void 0:t._loaded)?(this._styleDirty||(this._styleDirty=e),this._sourcesDirty=!0,this.triggerRepaint(),this):this}_requestRenderFrame(e){return this._update(),this._renderTaskQueue.add(e)}_cancelRenderFrame(e){this._renderTaskQueue.remove(e);}_render(e){var i,o,a,r,s,n;const l=this._idleTriggered?this._fadeDuration:0,h=(null===(i=this.style.projection)||void 0===i?void 0:i.transitionState)>0;if(this.painter.context.setDirty(),this.painter.setBaseState(),this._renderTaskQueue.run(e),this._removed)return;let u=!1;if(this.style&&this._styleDirty){this._styleDirty=!1;const e=this.transform.zoom,i=c();this.style.zoomHistory.update(e,i);const o=new t.J(e,{now:i,fadeDuration:l,zoomHistory:this.style.zoomHistory,transition:this.style.getTransition()}),a=o.crossFadingFactor();1===a&&a===this._crossFadingFactor||(u=!0,this._crossFadingFactor=a),this.style.update(o);}const d=(null===(o=this.style.projection)||void 0===o?void 0:o.transitionState)>0!==h;null===(a=this.style.projection)||void 0===a||a.setErrorQueryLatitudeDegrees(this.transform.center.lat),this.transform.setTransitionState(null===(r=this.style.projection)||void 0===r?void 0:r.transitionState,null===(s=this.style.projection)||void 0===s?void 0:s.latitudeErrorCorrectionRadians),this.style&&(this._sourcesDirty||d)&&(this._sourcesDirty=!1,this.style._updateSources(this.transform)),this.terrain?(this.terrain.tileManager.update(this.transform,this.terrain),this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this.transform.center,this.transform.tileZoom)),!this._elevationFreeze&&this._centerClampedToGround&&this.transform.setElevation(this.terrain.getElevationForLngLatZoom(this.transform.center,this.transform.tileZoom))):(this.transform.setMinElevationForCurrentTile(0),this._centerClampedToGround&&this.transform.setElevation(0)),this._placementDirty=null===(n=this.style)||void 0===n?void 0:n._updatePlacement(this.transform,this.showCollisionBoxes,l,this._crossSourceCollisions,d),this.painter.render(this.style,{showTileBoundaries:this.showTileBoundaries,showOverdrawInspector:this._showOverdrawInspector,rotating:this.isRotating(),zooming:this.isZooming(),moving:this.isMoving(),fadeDuration:l,showPadding:this.showPadding,anisotropicFilterPitch:this.getAnisotropicFilterPitch()}),this.fire(new t.n("render")),this.loaded()&&!this._loaded&&(this._loaded=!0,this.fire(new t.n("load"))),this.style&&(this.style.hasTransitions()||u)&&(this._styleDirty=!0),this.style&&!this._placementDirty&&this.style._releaseSymbolFadeTiles();const _=this._sourcesDirty||this._styleDirty||this._placementDirty;return _||this._repaint?this.triggerRepaint():!this.isMoving()&&this.loaded()&&this.fire(new t.n("idle")),!this._loaded||this._fullyLoaded||_||(this._fullyLoaded=!0),this}redraw(){return this.style&&(this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._render(0)),this}remove(){var e,i;this._hash&&this._hash.remove();for(const e of this._controls)e.onRemove(this);this._controls=[],this._frameRequest&&(this._frameRequest.abort(),this._frameRequest=null),this._renderTaskQueue.clear(),null===(e=this._diffStyleRequest)||void 0===e||e.abort(),this.painter.destroy(),this.handlers.destroy(),delete this.handlers,this.setStyle(null),"undefined"!=typeof window&&this._ownerWindow.removeEventListener("online",this._onWindowOnline,!1),u.removeThrottleControl(this._imageQueueHandle),null===(i=this._resizeObserver)||void 0===i||i.disconnect();const o=this.painter.context.gl.getExtension("WEBGL_lose_context");(null==o?void 0:o.loseContext)&&o.loseContext(),this._canvas.removeEventListener("webglcontextrestored",this._contextRestored,!1),this._canvas.removeEventListener("webglcontextlost",this._contextLost,!1),this._canvasContainer.remove(),this._controlContainer.remove(),this._container.removeEventListener("scroll",this._onMapScroll,!1),this._container.classList.remove("maplibregl-map"),this._removed=!0,this.fire(new t.n("remove"));}triggerRepaint(){this.style&&!this._frameRequest&&(this._frameRequest=new AbortController,n.frame(this._frameRequest,(e=>{this._frameRequest=null;try{this._render(e);}catch(e){if(!t.$(e)&&!function(e){return e.message===na}(e))throw e}}),(()=>{}),this._ownerWindow));}get showTileBoundaries(){return !!this._showTileBoundaries}set showTileBoundaries(e){this._showTileBoundaries!==e&&(this._showTileBoundaries=e,this._update());}get showPadding(){return !!this._showPadding}set showPadding(e){this._showPadding!==e&&(this._showPadding=e,this._update());}get showCollisionBoxes(){return !!this._showCollisionBoxes}set showCollisionBoxes(e){this._showCollisionBoxes!==e&&(this._showCollisionBoxes=e,e?this.style._generateCollisionBoxes():this._update());}get showOverdrawInspector(){return !!this._showOverdrawInspector}set showOverdrawInspector(e){this._showOverdrawInspector!==e&&(this._showOverdrawInspector=e,this._update());}get repaint(){return !!this._repaint}set repaint(e){this._repaint!==e&&(this._repaint=e,this.triggerRepaint());}get vertices(){return !!this._vertices}set vertices(e){this._vertices=e,this._update();}get version(){return hs}getCameraTargetElevation(){return this.transform.elevation}getProjection(){return this.style.getProjection()}setProjection(e){return this._lazyInitEmptyStyle(),this.style.setProjection(e),this._update(!0)}};const _s={showCompass:!0,showZoom:!0,visualizePitch:!1,visualizeRoll:!0};class ps{constructor(e,i,o=!1){this.mousedown=e=>{this.startMove(e,d.mousePos(this.element,e)),window.addEventListener("mousemove",this.mousemove),window.addEventListener("mouseup",this.mouseup);},this.mousemove=e=>{this.move(e,d.mousePos(this.element,e));},this.mouseup=e=>{this._rotatePitchHandler.dragEnd(e),this.offTemp();},this.touchstart=e=>{1!==e.targetTouches.length?this.reset():(this._startPos=this._lastPos=d.touchPos(this.element,e.targetTouches)[0],this.startMove(e,this._startPos),window.addEventListener("touchmove",this.touchmove,{passive:!1}),window.addEventListener("touchend",this.touchend));},this.touchmove=e=>{1!==e.targetTouches.length?this.reset():(this._lastPos=d.touchPos(this.element,e.targetTouches)[0],this.move(e,this._lastPos));},this.touchend=e=>{0===e.targetTouches.length&&this._startPos&&this._lastPos&&this._startPos.dist(this._lastPos){this._rotatePitchHandler.reset(),delete this._startPos,delete this._lastPos,this.offTemp();},this._clickTolerance=10,this.element=i;const a=new Pr;this._rotatePitchHandler=new vr({clickTolerance:3,move:(e,a)=>{const r=i.getBoundingClientRect(),s=new t.P((r.bottom-r.top)/2,(r.right-r.left)/2);return {bearingDelta:t.cx(new t.P(e.x,a.y),a,s),pitchDelta:o?-.5*(a.y-e.y):void 0}},moveStateManager:a,enable:!0,assignEvents:()=>{}}),this.map=e,i.addEventListener("mousedown",this.mousedown),i.addEventListener("touchstart",this.touchstart,{passive:!1}),i.addEventListener("touchcancel",this.reset);}startMove(e,t){this._rotatePitchHandler.dragStart(e,t),d.disableDrag();}move(e,t){const i=this.map,{bearingDelta:o,pitchDelta:a}=this._rotatePitchHandler.dragMove(e,t)||{};o&&i.setBearing(i.getBearing()+o),a&&i.setPitch(i.getPitch()+a);}off(){const e=this.element;e.removeEventListener("mousedown",this.mousedown),e.removeEventListener("touchstart",this.touchstart),window.removeEventListener("touchmove",this.touchmove),window.removeEventListener("touchend",this.touchend),e.removeEventListener("touchcancel",this.reset),this.offTemp();}offTemp(){d.enableDrag(),window.removeEventListener("mousemove",this.mousemove),window.removeEventListener("mouseup",this.mouseup),window.removeEventListener("touchmove",this.touchmove),window.removeEventListener("touchend",this.touchend);}}let ms;function fs(e,i,o,a=!1){if(a||!o.getCoveringTilesDetailsProvider().allowWorldCopies())return null==e?void 0:e.wrap();const r=new t.W(e.lng,e.lat);if(e=new t.W(e.lng,e.lat),i){const a=new t.W(e.lng-360,e.lat),r=new t.W(e.lng+360,e.lat),s=o.locationToScreenPoint(e).distSqr(i);o.locationToScreenPoint(a).distSqr(i)180;){const t=o.locationToScreenPoint(e);if(t.x>=0&&t.y>=0&&t.x<=o.width&&t.y<=o.height)break;e.lng>o.center.lng?e.lng-=360:e.lng+=360;}return e.lng!==r.lng&&o.isPointOnMapSurface(o.locationToScreenPoint(e))?e:r}const gs={center:"translate(-50%,-50%)",top:"translate(-50%,0)","top-left":"translate(0,0)","top-right":"translate(-100%,0)",bottom:"translate(-50%,-100%)","bottom-left":"translate(0,-100%)","bottom-right":"translate(-100%,-100%)",left:"translate(0,-50%)",right:"translate(-100%,-50%)"};function vs(e,t,i){const o=e.classList;for(const e in gs)o.remove(`maplibregl-${i}-anchor-${e}`);o.add(`maplibregl-${i}-anchor-${t}`);}class xs extends t.E{constructor(e){if(super(),this._onClick=e=>{this.fire(new t.n("click",{originalEvent:e}));},this._onKeyPress=e=>{"Space"!==e.code&&"Enter"!==e.code||this.togglePopup();},this._onMapClick=e=>{const t=e.originalEvent.target,i=this._element;this._popup&&(t===i||i.contains(t))&&this.togglePopup();},this._update=e=>{if(!this._map)return;const t=this._map.loaded()&&!this._map.isMoving();("terrain"===(null==e?void 0:e.type)||"render"===(null==e?void 0:e.type)&&!t)&&this._map.once("render",this._update),this._lngLat=fs(this._lngLat,this._flatPos,this._map.transform),this._flatPos=this._pos=this._map.project(this._lngLat)._add(this._offset),this._map.terrain&&(this._flatPos=this._map.transform.locationToScreenPoint(this._lngLat)._add(this._offset));let i="";"viewport"===this._rotationAlignment||"auto"===this._rotationAlignment?i=`rotateZ(${this._rotation}deg)`:"map"===this._rotationAlignment&&(i=`rotateZ(${this._rotation-this._map.getBearing()}deg)`);let o="";"viewport"===this._pitchAlignment||"auto"===this._pitchAlignment?o="rotateX(0deg)":"map"===this._pitchAlignment&&(o=`rotateX(${this._map.getPitch()}deg)`),this._subpixelPositioning||e&&"moveend"!==e.type||(this._pos=this._pos.round()),this._element.style.transform=`${gs[this._anchor]} translate(${this._pos.x}px, ${this._pos.y}px) ${o} ${i}`,n.frameAsync(new AbortController,this._map._ownerWindow).then((()=>{this._updateOpacity("moveend"===(null==e?void 0:e.type));})).catch((()=>{}));},this._onMove=e=>{if(!this._isDragging){const t=this._clickTolerance||this._map._clickTolerance;this._isDragging=e.point.dist(this._pointerdownPos)>=t;}this._isDragging&&(this._pos=e.point.sub(this._positionDelta),this._lngLat=this._map.unproject(this._pos),this.setLngLat(this._lngLat),this._element.style.pointerEvents="none","pending"===this._state&&(this._state="active",this.fire(new t.n("dragstart"))),this.fire(new t.n("drag")));},this._onUp=()=>{this._element.style.pointerEvents="auto",this._positionDelta=null,this._pointerdownPos=null,this._isDragging=!1,this._map.off("mousemove",this._onMove),this._map.off("touchmove",this._onMove),"active"===this._state&&this.fire(new t.n("dragend")),this._state="inactive";},this._addDragHandler=e=>{this._element.contains(e.originalEvent.target)&&(e.preventDefault(),this._positionDelta=e.point.sub(this._pos).add(this._offset),this._pointerdownPos=e.point,this._state="pending",this._map.on("mousemove",this._onMove),this._map.on("touchmove",this._onMove),this._map.once("mouseup",this._onUp),this._map.once("touchend",this._onUp));},this._anchor=(null==e?void 0:e.anchor)||"center",this._color=(null==e?void 0:e.color)||"#3FB1CE",this._scale=(null==e?void 0:e.scale)||1,this._draggable=(null==e?void 0:e.draggable)||!1,this._clickTolerance=(null==e?void 0:e.clickTolerance)||0,this._subpixelPositioning=(null==e?void 0:e.subpixelPositioning)||!1,this._isDragging=!1,this._state="inactive",this._rotation=(null==e?void 0:e.rotation)||0,this._rotationAlignment=(null==e?void 0:e.rotationAlignment)||"auto",this._pitchAlignment=(null==e?void 0:e.pitchAlignment)&&"auto"!==e.pitchAlignment?e.pitchAlignment:this._rotationAlignment,this.setOpacity(null==e?void 0:e.opacity,null==e?void 0:e.opacityWhenCovered),null==e?void 0:e.element)this._element=e.element,this._offset=t.P.convert((null==e?void 0:e.offset)||[0,0]);else {this._defaultMarker=!0,this._element=d.create("div");const i=d.createNS("http://www.w3.org/2000/svg","svg"),o=41,a=27;i.setAttributeNS(null,"display","block"),i.setAttributeNS(null,"height",`${o}px`),i.setAttributeNS(null,"width",`${a}px`),i.setAttributeNS(null,"viewBox",`0 0 ${a} ${o}`);const r=d.createNS("http://www.w3.org/2000/svg","g");r.setAttributeNS(null,"stroke","none"),r.setAttributeNS(null,"stroke-width","1"),r.setAttributeNS(null,"fill","none"),r.setAttributeNS(null,"fill-rule","evenodd");const s=d.createNS("http://www.w3.org/2000/svg","g");s.setAttributeNS(null,"fill-rule","nonzero");const n=d.createNS("http://www.w3.org/2000/svg","g");n.setAttributeNS(null,"transform","translate(3.0, 29.0)"),n.setAttributeNS(null,"fill","#000000");const l=[{rx:"10.5",ry:"5.25002273"},{rx:"10.5",ry:"5.25002273"},{rx:"9.5",ry:"4.77275007"},{rx:"8.5",ry:"4.29549936"},{rx:"7.5",ry:"3.81822308"},{rx:"6.5",ry:"3.34094679"},{rx:"5.5",ry:"2.86367051"},{rx:"4.5",ry:"2.38636864"}];for(const e of l){const t=d.createNS("http://www.w3.org/2000/svg","ellipse");t.setAttributeNS(null,"opacity","0.04"),t.setAttributeNS(null,"cx","10.5"),t.setAttributeNS(null,"cy","5.80029008"),t.setAttributeNS(null,"rx",e.rx),t.setAttributeNS(null,"ry",e.ry),n.appendChild(t);}const c=d.createNS("http://www.w3.org/2000/svg","g");c.setAttributeNS(null,"fill",this._color);const h=d.createNS("http://www.w3.org/2000/svg","path");h.setAttributeNS(null,"d","M27,13.5 C27,19.074644 20.250001,27.000002 14.75,34.500002 C14.016665,35.500004 12.983335,35.500004 12.25,34.500002 C6.7499993,27.000002 0,19.222562 0,13.5 C0,6.0441559 6.0441559,0 13.5,0 C20.955844,0 27,6.0441559 27,13.5 Z"),c.appendChild(h);const u=d.createNS("http://www.w3.org/2000/svg","g");u.setAttributeNS(null,"opacity","0.25"),u.setAttributeNS(null,"fill","#000000");const _=d.createNS("http://www.w3.org/2000/svg","path");_.setAttributeNS(null,"d","M13.5,0 C6.0441559,0 0,6.0441559 0,13.5 C0,19.222562 6.7499993,27 12.25,34.5 C13,35.522727 14.016664,35.500004 14.75,34.5 C20.250001,27 27,19.074644 27,13.5 C27,6.0441559 20.955844,0 13.5,0 Z M13.5,1 C20.415404,1 26,6.584596 26,13.5 C26,15.898657 24.495584,19.181431 22.220703,22.738281 C19.945823,26.295132 16.705119,30.142167 13.943359,33.908203 C13.743445,34.180814 13.612715,34.322738 13.5,34.441406 C13.387285,34.322738 13.256555,34.180814 13.056641,33.908203 C10.284481,30.127985 7.4148684,26.314159 5.015625,22.773438 C2.6163816,19.232715 1,15.953538 1,13.5 C1,6.584596 6.584596,1 13.5,1 Z"),u.appendChild(_);const p=d.createNS("http://www.w3.org/2000/svg","g");p.setAttributeNS(null,"transform","translate(6.0, 7.0)"),p.setAttributeNS(null,"fill","#FFFFFF");const m=d.createNS("http://www.w3.org/2000/svg","g");m.setAttributeNS(null,"transform","translate(8.0, 8.0)");const f=d.createNS("http://www.w3.org/2000/svg","circle");f.setAttributeNS(null,"fill","#000000"),f.setAttributeNS(null,"opacity","0.25"),f.setAttributeNS(null,"cx","5.5"),f.setAttributeNS(null,"cy","5.5"),f.setAttributeNS(null,"r","5.4999962");const g=d.createNS("http://www.w3.org/2000/svg","circle");g.setAttributeNS(null,"fill","#FFFFFF"),g.setAttributeNS(null,"cx","5.5"),g.setAttributeNS(null,"cy","5.5"),g.setAttributeNS(null,"r","5.4999962"),m.appendChild(f),m.appendChild(g),s.appendChild(n),s.appendChild(c),s.appendChild(u),s.appendChild(p),s.appendChild(m),i.appendChild(s),i.setAttributeNS(null,"height",o*this._scale+"px"),i.setAttributeNS(null,"width",a*this._scale+"px"),this._element.appendChild(i),this._offset=t.P.convert((null==e?void 0:e.offset)||[0,-14]);}if(this._element.classList.add("maplibregl-marker"),this._element.addEventListener("dragstart",(e=>{e.preventDefault();})),this._element.addEventListener("mousedown",(e=>{e.preventDefault();})),vs(this._element,this._anchor,"marker"),null==e?void 0:e.className)for(const t of e.className.split(" "))this._element.classList.add(t);this._popup=null;}addTo(e){return this.remove(),this._map=e,this._element.hasAttribute("aria-label")||this._element.setAttribute("aria-label",e._getUIString("Marker.Title")),this._element.hasAttribute("role")||this._element.setAttribute("role","button"),e.getCanvasContainer().appendChild(this._element),e.on("move",this._update),e.on("moveend",this._update),e.on("terrain",this._update),e.on("projectiontransition",this._update),this._element.addEventListener("click",this._onClick),this.setDraggable(this._draggable),this._update(),this._map.on("click",this._onMapClick),this}remove(){return this._opacityTimeout&&(clearTimeout(this._opacityTimeout),delete this._opacityTimeout),this._map&&(this._map.off("click",this._onMapClick),this._map.off("move",this._update),this._map.off("moveend",this._update),this._map.off("terrain",this._update),this._map.off("projectiontransition",this._update),this._map.off("mousedown",this._addDragHandler),this._map.off("touchstart",this._addDragHandler),this._map.off("mouseup",this._onUp),this._map.off("touchend",this._onUp),this._map.off("mousemove",this._onMove),this._map.off("touchmove",this._onMove),delete this._map),this._element.removeEventListener("click",this._onClick),this._element.remove(),this._popup&&this._popup.remove(),this}getLngLat(){return this._lngLat}setLngLat(e){return this._lngLat=t.W.convert(e),this._pos=null,this._popup&&this._popup.setLngLat(this._lngLat),this._update(),this}getElement(){return this._element}setPopup(e){if(this._popup&&(this._popup.remove(),this._popup=null,this._element.removeEventListener("keypress",this._onKeyPress),this._originalTabIndex||this._element.removeAttribute("tabindex")),e){if(!("offset"in e.options)){const t=38.1,i=13.5,o=Math.abs(i)/Math.SQRT2;e.options.offset=this._defaultMarker?{top:[0,0],"top-left":[0,0],"top-right":[0,0],bottom:[0,-t],"bottom-left":[o,-1*(t-i+o)],"bottom-right":[-o,-1*(t-i+o)],left:[i,-1*(t-i)],right:[-i,-1*(t-i)]}:this._offset;}this._popup=e,this._originalTabIndex=this._element.getAttribute("tabindex"),this._originalTabIndex||this._element.setAttribute("tabindex","0"),this._element.addEventListener("keypress",this._onKeyPress);}return this}setSubpixelPositioning(e){return this._subpixelPositioning=e,this}getPopup(){return this._popup}togglePopup(){const e=this._popup;return this._element.style.opacity===this._opacityWhenCovered?this:e?(e.isOpen()?e.remove():(e.setLngLat(this._lngLat),e.addTo(this._map)),this):this}_updateOpacity(e=!1){var i,o;const a=null===(i=this._map)||void 0===i?void 0:i.terrain,r=this._map.transform.isLocationOccluded(this._lngLat);if(!a||r){const e=r?this._opacityWhenCovered:this._opacity;return void(this._element.style.opacity!==e&&(this._element.style.opacity=e,this._element.classList.toggle("maplibregl-marker-covered",r)))}if(e)this._opacityTimeout=null;else {if(this._opacityTimeout)return;this._opacityTimeout=setTimeout((()=>{this._opacityTimeout=null;}),100);}const s=this._map,n=s.terrain.depthAtPoint(this._pos),l=s.terrain.getElevationForLngLat(this._lngLat,s.transform);if(s.transform.lngLatToCameraDepth(this._lngLat,l)-n<.006)return this._element.style.opacity=this._opacity,void this._element.classList.remove("maplibregl-marker-covered");const c=-this._offset.y/s.transform.pixelsPerMeter,h=Math.sin(s.getPitch()*Math.PI/180)*c,u=s.terrain.depthAtPoint(new t.P(this._pos.x,this._pos.y-this._offset.y)),d=s.transform.lngLatToCameraDepth(this._lngLat,l+h)-u>.006;(null===(o=this._popup)||void 0===o?void 0:o.isOpen())&&d&&this._popup.remove(),this._element.style.opacity=d?this._opacityWhenCovered:this._opacity,this._element.classList.toggle("maplibregl-marker-covered",d);}getOffset(){return this._offset}setOffset(e){return this._offset=t.P.convert(e),this._update(),this}addClassName(e){this._element.classList.add(e);}removeClassName(e){this._element.classList.remove(e);}toggleClassName(e){return this._element.classList.toggle(e)}setDraggable(e){return this._draggable=!!e,this._map&&(e?(this._map.on("mousedown",this._addDragHandler),this._map.on("touchstart",this._addDragHandler)):(this._map.off("mousedown",this._addDragHandler),this._map.off("touchstart",this._addDragHandler))),this}isDraggable(){return this._draggable}setRotation(e){return this._rotation=e||0,this._update(),this}getRotation(){return this._rotation}setRotationAlignment(e){return this._rotationAlignment=e||"auto",this._update(),this}getRotationAlignment(){return this._rotationAlignment}setPitchAlignment(e){return this._pitchAlignment=e&&"auto"!==e?e:this._rotationAlignment,this._update(),this}getPitchAlignment(){return this._pitchAlignment}setOpacity(e,t){return (void 0===this._opacity||void 0===e&&void 0===t)&&(this._opacity="1",this._opacityWhenCovered="0.2"),void 0!==e&&(this._opacity=String(e)),void 0!==t&&(this._opacityWhenCovered=String(t)),this._map&&this._updateOpacity(!0),this}}const bs={positionOptions:{enableHighAccuracy:!1,maximumAge:0,timeout:6e3},fitBoundsOptions:{maxZoom:15},trackUserLocation:!1,showAccuracyCircle:!0,showUserLocation:!0};let ys=0,ws=!1;const Ts={maxWidth:100,unit:"metric"};function Ps(e,t,i){const o=(null==i?void 0:i.maxWidth)||100,a=e._container.clientHeight/2,r=e._container.clientWidth/2,s=e.unproject([r-o/2,a]),n=e.unproject([r+o/2,a]),l=Math.round(e.project(n).x-e.project(s).x),c=Math.min(o,l,e._container.clientWidth),h=s.distanceTo(n);if("imperial"===(null==i?void 0:i.unit)){const i=3.2808*h;i>5280?Cs(t,c,i/5280,e._getUIString("ScaleControl.Miles")):Cs(t,c,i,e._getUIString("ScaleControl.Feet"));}else "nautical"===(null==i?void 0:i.unit)?Cs(t,c,h/1852,e._getUIString("ScaleControl.NauticalMiles")):h>=1e3?Cs(t,c,h/1e3,e._getUIString("ScaleControl.Kilometers")):Cs(t,c,h,e._getUIString("ScaleControl.Meters"));}function Cs(e,t,i,o){const a=function(e){const t=Math.pow(10,`${Math.floor(e)}`.length-1);let i=e/t;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:i>=1?1:function(e){const t=Math.pow(10,Math.ceil(-Math.log(e)/Math.LN10));return Math.round(e*t)/t}(i),t*i}(i);e.style.width=t*(a/i)+"px",e.innerHTML=`${a} ${o}`;}const Ms={closeButton:!0,closeOnClick:!0,focusAfterOpen:!0,className:"",maxWidth:"240px",subpixelPositioning:!1,locationOccludedOpacity:void 0,padding:void 0},Is=["a[href]","[tabindex]:not([tabindex='-1'])","[contenteditable]:not([contenteditable='false'])","button:not([disabled])","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].join(", ");function Es(e){if(e){if("number"==typeof e){const i=Math.round(Math.abs(e)/Math.SQRT2);return {center:new t.P(0,0),top:new t.P(0,e),"top-left":new t.P(i,i),"top-right":new t.P(-i,i),bottom:new t.P(0,-e),"bottom-left":new t.P(i,-i),"bottom-right":new t.P(-i,-i),left:new t.P(e,0),right:new t.P(-e,0)}}if(e instanceof t.P||Array.isArray(e)){const i=t.P.convert(e);return {center:i,top:i,"top-left":i,"top-right":i,bottom:i,"bottom-left":i,"bottom-right":i,left:i,right:i}}return {center:t.P.convert(e.center||[0,0]),top:t.P.convert(e.top||[0,0]),"top-left":t.P.convert(e["top-left"]||[0,0]),"top-right":t.P.convert(e["top-right"]||[0,0]),bottom:t.P.convert(e.bottom||[0,0]),"bottom-left":t.P.convert(e["bottom-left"]||[0,0]),"bottom-right":t.P.convert(e["bottom-right"]||[0,0]),left:t.P.convert(e.left||[0,0]),right:t.P.convert(e.right||[0,0])}}return Es(new t.P(0,0))}const Ss=i;e.AJAXError=t.cG,e.EXTENT=t.a6,e.Event=t.n,e.Evented=t.E,e.LngLat=t.W,e.MercatorCoordinate=t.a7,e.Point=t.P,e.addProtocol=t.cH,e.config=t.c,e.removeProtocol=t.cI,e.AttributionControl=es,e.BoxZoomHandler=_r,e.CanvasSource=oe,e.CooperativeGesturesHandler=$r,e.DoubleClickZoomHandler=Zr,e.DragPanHandler=Vr,e.DragRotateHandler=Wr,e.EdgeInsets=Ut,e.FullscreenControl=class extends t.E{constructor(e={}){var i;super(),this._onFullscreenChange=()=>{var e;let t=window.document.fullscreenElement||window.document.webkitFullscreenElement;for(;null===(e=null==t?void 0:t.shadowRoot)||void 0===e?void 0:e.fullscreenElement;)t=t.shadowRoot.fullscreenElement;t===this._container!==this._fullscreen&&this._handleFullscreenChange();},this._onClickFullscreen=()=>{this._isFullscreen()?this._exitFullscreen():this._requestFullscreen();},this._fullscreen=!1,this._pseudo=null!==(i=e.pseudo)&&void 0!==i&&i,(null==e?void 0:e.container)&&(e.container instanceof HTMLElement?this._container=e.container:t.w("Full screen control 'container' must be a DOM element.")),"onfullscreenchange"in document?this._fullscreenchange="fullscreenchange":"onmozfullscreenchange"in document?this._fullscreenchange="mozfullscreenchange":"onwebkitfullscreenchange"in document?this._fullscreenchange="webkitfullscreenchange":"onmsfullscreenchange"in document&&(this._fullscreenchange="MSFullscreenChange");}onAdd(e){return this._map=e,this._container||(this._container=this._map.getContainer()),this._controlContainer=d.create("div","maplibregl-ctrl maplibregl-ctrl-group"),this._setupUI(),this._controlContainer}onRemove(){this._controlContainer.remove(),this._map=null,window.document.removeEventListener(this._fullscreenchange,this._onFullscreenChange);}_setupUI(){const e=this._fullscreenButton=d.create("button","maplibregl-ctrl-fullscreen",this._controlContainer);d.create("span","maplibregl-ctrl-icon",e).setAttribute("aria-hidden","true"),e.type="button",this._updateTitle(),this._fullscreenButton.addEventListener("click",this._onClickFullscreen),window.document.addEventListener(this._fullscreenchange,this._onFullscreenChange);}_updateTitle(){const e=this._getTitle();this._fullscreenButton.setAttribute("aria-label",e),this._fullscreenButton.title=e;}_getTitle(){return this._map._getUIString(this._isFullscreen()?"FullscreenControl.Exit":"FullscreenControl.Enter")}_isFullscreen(){return this._fullscreen}_handleFullscreenChange(){this._fullscreen=!this._fullscreen,this._fullscreenButton.classList.toggle("maplibregl-ctrl-shrink"),this._fullscreenButton.classList.toggle("maplibregl-ctrl-fullscreen"),this._updateTitle(),this._fullscreen?(this.fire(new t.n("fullscreenstart")),this._prevCooperativeGesturesEnabled=this._map.cooperativeGestures.isEnabled(),this._map.cooperativeGestures.disable()):(this.fire(new t.n("fullscreenend")),this._prevCooperativeGesturesEnabled&&this._map.cooperativeGestures.enable());}_exitFullscreen(){this._pseudo?this._togglePseudoFullScreen():window.document.exitFullscreen?window.document.exitFullscreen():window.document.webkitCancelFullScreen?window.document.webkitCancelFullScreen():this._togglePseudoFullScreen();}_requestFullscreen(){this._pseudo?this._togglePseudoFullScreen():this._container.requestFullscreen?this._container.requestFullscreen():this._container.webkitRequestFullscreen?this._container.webkitRequestFullscreen():this._togglePseudoFullScreen();}_togglePseudoFullScreen(){this._container.classList.toggle("maplibregl-pseudo-fullscreen"),this._handleFullscreenChange(),this._map.resize();}},e.GeoJSONSource=ee,e.GeolocateControl=class extends t.E{constructor(e){super(),this._onSuccess=e=>{if(this._map){if(this._isOutOfMapMaxBounds(e))return this._setErrorState(),this.fire(new t.n("outofmaxbounds",e)),this._updateMarker(),void this._finish();if(this.options.trackUserLocation)switch(this._lastKnownPosition=e,this._watchState){case "WAITING_ACTIVE":case "ACTIVE_LOCK":case "ACTIVE_ERROR":this._watchState="ACTIVE_LOCK",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");break;case "BACKGROUND":case "BACKGROUND_ERROR":this._watchState="BACKGROUND",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background");break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}this.options.showUserLocation&&"OFF"!==this._watchState&&this._updateMarker(e),this.options.trackUserLocation&&"ACTIVE_LOCK"!==this._watchState||this._updateCamera(e),this.options.showUserLocation&&this._dotElement.classList.remove("maplibregl-user-location-dot-stale"),this.fire(new t.n("geolocate",e)),this._finish();}},this._updateCamera=e=>{const i=new t.W(e.coords.longitude,e.coords.latitude),o=e.coords.accuracy,a=this._map.getBearing(),r=t.e({bearing:a},this.options.fitBoundsOptions),s=G.fromLngLat(i,o);this._map.fitBounds(s,r,{geolocateSource:!0});},this._updateMarker=e=>{if(e){const i=new t.W(e.coords.longitude,e.coords.latitude);this._accuracyCircleMarker.setLngLat(i).addTo(this._map),this._userLocationDotMarker.setLngLat(i).addTo(this._map),this._accuracy=e.coords.accuracy,this._updateCircleRadiusIfNeeded();}else this._userLocationDotMarker.remove(),this._accuracyCircleMarker.remove();},this._onUpdate=()=>{this._updateCircleRadiusIfNeeded();},this._onError=e=>{if(this._map){if(1===e.code){this._watchState="OFF",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error"),this._geolocateButton.disabled=!0;const e=this._map._getUIString("GeolocateControl.LocationNotAvailable");this._geolocateButton.title=e,this._geolocateButton.setAttribute("aria-label",e),void 0!==this._geolocationWatchID&&this._clearWatch();}else {if(3===e.code&&ws)return;this._setErrorState();}"OFF"!==this._watchState&&this.options.showUserLocation&&this._dotElement.classList.add("maplibregl-user-location-dot-stale"),this.fire(new t.n("error",e)),this._finish();}},this._finish=()=>{this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=void 0;},this._onMoveStart=e=>{if(!this._map)return;const i=(null==e?void 0:e[0])instanceof ResizeObserverEntry;e.geolocateSource||"ACTIVE_LOCK"!==this._watchState||i||this._map.isZooming()||(this._watchState="BACKGROUND",this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active"),this.fire(new t.n("trackuserlocationend")),this.fire(new t.n("userlocationlostfocus")));},this._setupUI=()=>{this._map&&(this._container.addEventListener("contextmenu",(e=>{e.preventDefault();})),this._geolocateButton=d.create("button","maplibregl-ctrl-geolocate",this._container),d.create("span","maplibregl-ctrl-icon",this._geolocateButton).setAttribute("aria-hidden","true"),this._geolocateButton.type="button",this._geolocateButton.disabled=!0);},this._finishSetupUI=e=>{if(this._map){if(!1===e){t.w("Geolocation support is not available so the GeolocateControl will be disabled.");const e=this._map._getUIString("GeolocateControl.LocationNotAvailable");this._geolocateButton.disabled=!0,this._geolocateButton.title=e,this._geolocateButton.setAttribute("aria-label",e);}else {const e=this._map._getUIString("GeolocateControl.FindMyLocation");this._geolocateButton.disabled=!1,this._geolocateButton.title=e,this._geolocateButton.setAttribute("aria-label",e);}this.options.trackUserLocation&&(this._geolocateButton.setAttribute("aria-pressed","false"),this._watchState="OFF"),this.options.showUserLocation&&(this._dotElement=d.create("div","maplibregl-user-location-dot"),this._userLocationDotMarker=new xs({element:this._dotElement}),this._circleElement=d.create("div","maplibregl-user-location-accuracy-circle"),this._accuracyCircleMarker=new xs({element:this._circleElement,pitchAlignment:"map"}),this.options.trackUserLocation&&(this._watchState="OFF"),this._map.on("zoom",this._onUpdate),this._map.on("move",this._onUpdate),this._map.on("rotate",this._onUpdate),this._map.on("pitch",this._onUpdate)),this._geolocateButton.addEventListener("click",(()=>this.trigger())),this._setup=!0,this.options.trackUserLocation&&this._map.on("movestart",this._onMoveStart);}},this.options=t.e({},bs,e);}onAdd(e){return this._map=e,this._container=d.create("div","maplibregl-ctrl maplibregl-ctrl-group"),this._setupUI(),function(){return t._(this,arguments,void 0,(function*(e=!1){if(void 0!==ms&&!e)return ms;if(void 0===window.navigator.permissions)return ms=!!window.navigator.geolocation,ms;try{const e=yield window.navigator.permissions.query({name:"geolocation"});ms="denied"!==e.state;}catch(e){ms=!!window.navigator.geolocation;}return ms}))}().then((e=>this._finishSetupUI(e))),this._container}onRemove(){void 0!==this._geolocationWatchID&&(window.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0),this.options.showUserLocation&&this._userLocationDotMarker&&this._userLocationDotMarker.remove(),this.options.showAccuracyCircle&&this._accuracyCircleMarker&&this._accuracyCircleMarker.remove(),this._container.remove(),this._map.off("movestart",this._onMoveStart),this._map.off("zoom",this._onUpdate),this._map.off("move",this._onUpdate),this._map.off("rotate",this._onUpdate),this._map.off("pitch",this._onUpdate),this._map=void 0,ys=0,ws=!1;}_isOutOfMapMaxBounds(e){const t=this._map.getMaxBounds(),i=e.coords;return t&&(i.longitudet.getEast()||i.latitudet.getNorth())}_setErrorState(){switch(this._watchState){case "WAITING_ACTIVE":this._watchState="ACTIVE_ERROR",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error");break;case "ACTIVE_LOCK":this._watchState="ACTIVE_ERROR",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");break;case "BACKGROUND":this._watchState="BACKGROUND_ERROR",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");break;case "ACTIVE_ERROR":case "BACKGROUND_ERROR":case "OFF":case void 0:break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}}_updateCircleRadiusIfNeeded(){const e=this._userLocationDotMarker.getLngLat();if(!(this.options.showUserLocation&&this.options.showAccuracyCircle&&this._accuracy&&e))return;const t=this._map.project(e),i=this._map.unproject([t.x+100,t.y]),o=e.distanceTo(i)/100,a=2*this._accuracy/o;this._circleElement.style.width=`${a.toFixed(2)}px`,this._circleElement.style.height=`${a.toFixed(2)}px`;}trigger(){if(!this._setup)return t.w("Geolocate control triggered before added to a map"),!1;if(this.options.trackUserLocation){switch(this._watchState){case "OFF":this._watchState="WAITING_ACTIVE",this.fire(new t.n("trackuserlocationstart"));break;case "WAITING_ACTIVE":case "ACTIVE_LOCK":case "ACTIVE_ERROR":case "BACKGROUND_ERROR":ys--,ws=!1,this._watchState="OFF",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error"),this.fire(new t.n("trackuserlocationend"));break;case "BACKGROUND":this._watchState="ACTIVE_LOCK",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background"),this._lastKnownPosition&&this._updateCamera(this._lastKnownPosition),this.fire(new t.n("trackuserlocationstart")),this.fire(new t.n("userlocationfocus"));break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}switch(this._watchState){case "WAITING_ACTIVE":this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");break;case "ACTIVE_LOCK":this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");break;case "OFF":break;default:throw new Error(`Unexpected watchState ${this._watchState}`)}if("OFF"===this._watchState&&void 0!==this._geolocationWatchID)this._clearWatch();else if(void 0===this._geolocationWatchID){let e;this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.setAttribute("aria-pressed","true"),ys++,ys>1?(e={maximumAge:6e5,timeout:0},ws=!0):(e=this.options.positionOptions,ws=!1),this._geolocationWatchID=window.navigator.geolocation.watchPosition(this._onSuccess,this._onError,e);}}else window.navigator.geolocation.getCurrentPosition(this._onSuccess,this._onError,this.options.positionOptions),this._timeoutId=setTimeout(this._finish,1e4);return !0}_clearWatch(){window.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0,this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting"),this._geolocateButton.setAttribute("aria-pressed","false"),this.options.showUserLocation&&this._updateMarker(null);}},e.GlobeControl=class{constructor(){this._toggleProjection=()=>{var e;const t=null===(e=this._map.getProjection())||void 0===e?void 0:e.type;this._map.setProjection("mercator"!==t&&t?{type:"mercator"}:{type:"globe"}),this._updateGlobeIcon();},this._updateGlobeIcon=()=>{var e;this._globeButton.classList.remove("maplibregl-ctrl-globe"),this._globeButton.classList.remove("maplibregl-ctrl-globe-enabled"),"globe"===(null===(e=this._map.getProjection())||void 0===e?void 0:e.type)?(this._globeButton.classList.add("maplibregl-ctrl-globe-enabled"),this._globeButton.title=this._map._getUIString("GlobeControl.Disable")):(this._globeButton.classList.add("maplibregl-ctrl-globe"),this._globeButton.title=this._map._getUIString("GlobeControl.Enable"));};}onAdd(e){return this._map=e,this._container=d.create("div","maplibregl-ctrl maplibregl-ctrl-group"),this._globeButton=d.create("button","maplibregl-ctrl-globe",this._container),d.create("span","maplibregl-ctrl-icon",this._globeButton).setAttribute("aria-hidden","true"),this._globeButton.type="button",this._globeButton.addEventListener("click",this._toggleProjection),this._updateGlobeIcon(),this._map.on("styledata",this._updateGlobeIcon),this._map.on("projectiontransition",this._updateGlobeIcon),this._container}onRemove(){this._container.remove(),this._map.off("styledata",this._updateGlobeIcon),this._map.off("projectiontransition",this._updateGlobeIcon),this._globeButton.removeEventListener("click",this._toggleProjection),this._map=void 0;}},e.Hash=Ya,e.ImageSource=te,e.KeyboardHandler=kr,e.LngLatBounds=G,e.LogoControl=ts,e.Map=ds,e.MapLibreMap=ds,e.MapMouseEvent=nr,e.MapTouchEvent=lr,e.MapWheelEvent=cr,e.Marker=xs,e.NavigationControl=class{constructor(e){this._updateZoomButtons=()=>{const e=this._map.getZoom(),t=e===this._map.getMaxZoom(),i=e===this._map.getMinZoom();this._zoomInButton.disabled=t,this._zoomOutButton.disabled=i,this._zoomInButton.setAttribute("aria-disabled",t.toString()),this._zoomOutButton.setAttribute("aria-disabled",i.toString());},this._rotateCompassArrow=()=>{this._compassIcon.style.transform=this.options.visualizePitch&&this.options.visualizeRoll?`scale(${1/Math.pow(Math.cos(this._map.transform.pitchInRadians),.5)}) rotateZ(${-this._map.transform.roll}deg) rotateX(${this._map.transform.pitch}deg) rotateZ(${-this._map.transform.bearing}deg)`:this.options.visualizePitch?`scale(${1/Math.pow(Math.cos(this._map.transform.pitchInRadians),.5)}) rotateX(${this._map.transform.pitch}deg) rotateZ(${-this._map.transform.bearing}deg)`:this.options.visualizeRoll?`rotate(${-this._map.transform.bearing-this._map.transform.roll}deg)`:`rotate(${-this._map.transform.bearing}deg)`;},this._setButtonTitle=(e,t)=>{const i=this._map._getUIString(`NavigationControl.${t}`);e.title=i,e.setAttribute("aria-label",i);},this.options=t.e({},_s,e),this._container=d.create("div","maplibregl-ctrl maplibregl-ctrl-group"),this._container.addEventListener("contextmenu",(e=>e.preventDefault())),this.options.showZoom&&(this._zoomInButton=this._createButton("maplibregl-ctrl-zoom-in",(e=>this._map.zoomIn({},{originalEvent:e}))),d.create("span","maplibregl-ctrl-icon",this._zoomInButton).setAttribute("aria-hidden","true"),this._zoomOutButton=this._createButton("maplibregl-ctrl-zoom-out",(e=>this._map.zoomOut({},{originalEvent:e}))),d.create("span","maplibregl-ctrl-icon",this._zoomOutButton).setAttribute("aria-hidden","true")),this.options.showCompass&&(this._compass=this._createButton("maplibregl-ctrl-compass",(e=>{this.options.visualizePitch?this._map.resetNorthPitch({},{originalEvent:e}):this._map.resetNorth({},{originalEvent:e});})),this._compassIcon=d.create("span","maplibregl-ctrl-icon",this._compass),this._compassIcon.setAttribute("aria-hidden","true"));}onAdd(e){return this._map=e,this.options.showZoom&&(this._setButtonTitle(this._zoomInButton,"ZoomIn"),this._setButtonTitle(this._zoomOutButton,"ZoomOut"),this._map.on("zoom",this._updateZoomButtons),this._updateZoomButtons()),this.options.showCompass&&(this._setButtonTitle(this._compass,"ResetBearing"),this.options.visualizePitch&&this._map.on("pitch",this._rotateCompassArrow),this.options.visualizeRoll&&this._map.on("roll",this._rotateCompassArrow),this._map.on("rotate",this._rotateCompassArrow),this._rotateCompassArrow(),this._handler=new ps(this._map,this._compass,this.options.visualizePitch)),this._container}onRemove(){this._container.remove(),this.options.showZoom&&this._map.off("zoom",this._updateZoomButtons),this.options.showCompass&&(this.options.visualizePitch&&this._map.off("pitch",this._rotateCompassArrow),this.options.visualizeRoll&&this._map.off("roll",this._rotateCompassArrow),this._map.off("rotate",this._rotateCompassArrow),this._handler.off(),delete this._handler),delete this._map;}_createButton(e,t){const i=d.create("button",e,this._container);return i.type="button",i.addEventListener("click",t),i}},e.Popup=class extends t.E{constructor(e){super(),this._updateOpacity=()=>{void 0!==this.options.locationOccludedOpacity&&(this._container.style.opacity=this._map.transform.isLocationOccluded(this.getLngLat())?`${this.options.locationOccludedOpacity}`:"");},this.remove=()=>(this._content&&this._content.remove(),this._container&&(this._container.remove(),delete this._container),this._map&&(this._map.off("move",this._update),this._map.off("move",this._onClose),this._map.off("click",this._onClose),this._map.off("remove",this.remove),this._map.off("terrain",this._update),this._map.off("projectiontransition",this._update),this._map.off("mousemove",this._update),this._map.off("mouseup",this._update),this._map.off("drag",this._update),this._map._canvasContainer.classList.remove("maplibregl-track-pointer"),delete this._map,this.fire(new t.n("close"))),this),this._update=e=>{if(!this._map||!this._lngLat&&!this._trackPointer||!this._content)return;if(!this._container){if(this._container=d.create("div","maplibregl-popup",this._map.getContainer()),this._tip=d.create("div","maplibregl-popup-tip",this._container),this._container.appendChild(this._content),this.options.className)for(const e of this.options.className.split(" "))this._container.classList.add(e);this._closeButton&&this._closeButton.setAttribute("aria-label",this._map._getUIString("Popup.Close")),this._trackPointer&&this._container.classList.add("maplibregl-popup-track-pointer");}let t;if(this.options.maxWidth&&this._container.style.maxWidth!==this.options.maxWidth&&(this._container.style.maxWidth=this.options.maxWidth),this._lngLat=fs(this._lngLat,this._flatPos,this._map.transform,this._trackPointer),e&&"point"in e&&e.point&&(t=e.point),this._trackPointer&&!t)return;const i=this._flatPos=this._pos=this._trackPointer&&t?t:this._map.project(this._lngLat);this._map.terrain&&(this._flatPos=this._trackPointer&&t?t:this._map.transform.locationToScreenPoint(this._lngLat));let o=this.options.anchor;const a=Es(this.options.offset);if(!o){const e=this._container.offsetWidth,t=this._container.offsetHeight,r=function(e){var t,i,o,a;return e?{top:null!==(t=e.top)&&void 0!==t?t:0,right:null!==(i=e.right)&&void 0!==i?i:0,bottom:null!==(o=e.bottom)&&void 0!==o?o:0,left:null!==(a=e.left)&&void 0!==a?a:0}:{top:0,right:0,bottom:0,left:0}}(this.options.padding);let s;s=i.y+a.bottom.ythis._map.transform.height-t-r.bottom?["bottom"]:[],i.xthis._map.transform.width-e/2-r.right&&s.push("right"),o=0===s.length?"bottom":s.join("-");}let r=i.add(a[o]);this.options.subpixelPositioning||(r=r.round()),this._container.style.transform=`${gs[o]} translate(${r.x}px,${r.y}px)`,vs(this._container,o,"popup"),this._updateOpacity();},this._onClose=()=>{this.remove();},this.options=t.e(Object.create(Ms),e);}addTo(e){return this._map&&this.remove(),this._map=e,this.options.closeOnClick&&this._map.on("click",this._onClose),this.options.closeOnMove&&this._map.on("move",this._onClose),this._map.on("remove",this.remove),this._map.on("terrain",this._update),this._map.on("projectiontransition",this._update),this._update(),this._focusFirstElement(),this._trackPointer?(this._map.on("mousemove",this._update),this._map.on("mouseup",this._update),this._container&&this._container.classList.add("maplibregl-popup-track-pointer"),this._map._canvasContainer.classList.add("maplibregl-track-pointer")):this._map.on("move",this._update),this.fire(new t.n("open")),this}isOpen(){return !!this._map}getLngLat(){return this._lngLat}setLngLat(e){return this._lngLat=t.W.convert(e),this._pos=null,this._flatPos=null,this._trackPointer=!1,this._update(),this._map&&(this._map.on("move",this._update),this._map.off("mousemove",this._update),this._container&&this._container.classList.remove("maplibregl-popup-track-pointer"),this._map._canvasContainer.classList.remove("maplibregl-track-pointer")),this}trackPointer(){return this._trackPointer=!0,this._pos=null,this._flatPos=null,this._update(),this._map&&(this._map.off("move",this._update),this._map.on("mousemove",this._update),this._map.on("drag",this._update),this._container&&this._container.classList.add("maplibregl-popup-track-pointer"),this._map._canvasContainer.classList.add("maplibregl-track-pointer")),this}getElement(){return this._container}setText(e){return this.setDOMContent(document.createTextNode(e))}setHTML(e){const t=document.createDocumentFragment(),i=document.createElement("body");let o;for(i.innerHTML=e;o=i.firstChild,o;)t.appendChild(o);return this.setDOMContent(t)}getMaxWidth(){var e;return null===(e=this._container)||void 0===e?void 0:e.style.maxWidth}setMaxWidth(e){return this.options.maxWidth=e,this._update(),this}setDOMContent(e){if(this._content)for(;this._content.hasChildNodes();)this._content.firstChild&&this._content.removeChild(this._content.firstChild);else this._content=d.create("div","maplibregl-popup-content",this._container);return this._content.appendChild(e),this._createCloseButton(),this._update(),this._focusFirstElement(),this}addClassName(e){return this._container&&this._container.classList.add(e),this}removeClassName(e){return this._container&&this._container.classList.remove(e),this}setOffset(e){return this.options.offset=e,this._update(),this}toggleClassName(e){if(this._container)return this._container.classList.toggle(e)}setSubpixelPositioning(e){this.options.subpixelPositioning=e;}setPadding(e){this.options.padding=e,this._update();}_createCloseButton(){this.options.closeButton&&(this._closeButton=d.create("button","maplibregl-popup-close-button",this._content),this._closeButton.type="button",this._closeButton.innerHTML="×",this._closeButton.addEventListener("click",this._onClose));}_focusFirstElement(){if(!this.options.focusAfterOpen||!this._container)return;const e=this._container.querySelector(Is);e&&e.focus();}},e.RasterDEMTileSource=$,e.RasterTileSource=q,e.ScaleControl=class{constructor(e){this._onMove=()=>{Ps(this._map,this._container,this.options);},this.setUnit=e=>{this.options.unit=e,Ps(this._map,this._container,this.options);},this.options=Object.assign(Object.assign({},Ts),e);}getDefaultPosition(){return "bottom-left"}onAdd(e){return this._map=e,this._container=d.create("div","maplibregl-ctrl maplibregl-ctrl-scale",e.getContainer()),this._map.on("move",this._onMove),this._onMove(),this._container}onRemove(){this._container.remove(),this._map.off("move",this._onMove),this._map=void 0;}},e.ScrollZoomHandler=Nr,e.Style=ki,e.TerrainControl=class{constructor(e){this._toggleTerrain=()=>{this._map.getTerrain()?this._map.setTerrain(null):this._map.setTerrain(this.options),this._updateTerrainIcon();},this._updateTerrainIcon=()=>{this._terrainButton.classList.remove("maplibregl-ctrl-terrain"),this._terrainButton.classList.remove("maplibregl-ctrl-terrain-enabled"),this._map.terrain?(this._terrainButton.classList.add("maplibregl-ctrl-terrain-enabled"),this._terrainButton.title=this._map._getUIString("TerrainControl.Disable")):(this._terrainButton.classList.add("maplibregl-ctrl-terrain"),this._terrainButton.title=this._map._getUIString("TerrainControl.Enable"));},this.options=e;}onAdd(e){return this._map=e,this._container=d.create("div","maplibregl-ctrl maplibregl-ctrl-group"),this._terrainButton=d.create("button","maplibregl-ctrl-terrain",this._container),d.create("span","maplibregl-ctrl-icon",this._terrainButton).setAttribute("aria-hidden","true"),this._terrainButton.type="button",this._terrainButton.addEventListener("click",this._toggleTerrain),this._updateTerrainIcon(),this._map.on("terrain",this._updateTerrainIcon),this._container}onRemove(){this._container.remove(),this._map.off("terrain",this._updateTerrainIcon),this._map=void 0;}},e.TwoFingersTouchPitchHandler=Lr,e.TwoFingersTouchRotateHandler=Dr,e.TwoFingersTouchZoomHandler=zr,e.TwoFingersTouchZoomRotateHandler=qr,e.VectorTileSource=W,e.VideoSource=ie,e.addSourceType=(e,i)=>t._(void 0,void 0,void 0,(function*(){if(re(e))throw new Error(`A source type called "${e}" already exists.`);((e,t)=>{ae[e]=t;})(e,i);})),e.clearPrewarmedResources=function(){const e=A;e&&(e.isPreloaded()&&1===e.numActive()?(e.release(z),A=null):console.warn("Could not clear WebWorkers since there are active Map instances that still reference it. The pre-warmed WebWorker pool can only be cleared when all map instances have been removed with map.remove()"));},e.createTileMesh=ci,e.getGlobalDispatcher=B,e.getMaxParallelImageRequests=function(){return t.c.MAX_PARALLEL_IMAGE_REQUESTS},e.getRTLTextPluginStatus=function(){return ce().getRTLTextPluginStatus()},e.getVersion=function(){return Ss},e.getWorkerCount=function(){return R.workerCount},e.getWorkerUrl=function(){return t.c.WORKER_URL},e.importScriptInWorkers=function(e){return B().broadcast("IS",e)},e.isTimeFrozen=function(){return l.isFrozen()},e.now=c,e.prewarm=function(){F().acquire(z);},e.restoreNow=function(){l.restoreNow();},e.setMaxParallelImageRequests=function(e){t.c.MAX_PARALLEL_IMAGE_REQUESTS=e;},e.setNow=function(e){l.setNow(e);},e.setRTLTextPlugin=function(e,t){return ce().setRTLTextPlugin(e,t)},e.setWorkerCount=function(e){R.workerCount=e;},e.setWorkerUrl=function(e){t.c.WORKER_URL=e;};})); + +// +// Our custom intro provides a specialized "define()" function, called by the +// AMD modules below, that sets up the worker blob URL and then executes the +// main module, storing its exported value as 'maplibregl' + + +var maplibregl$1 = maplibregl; + +return maplibregl$1; + +})); +//# sourceMappingURL=maplibre-gl.js.map diff --git a/frontend/static/vendors/pmtiles.js b/frontend/static/vendors/pmtiles.js new file mode 100644 index 00000000..fb67dadd --- /dev/null +++ b/frontend/static/vendors/pmtiles.js @@ -0,0 +1,2 @@ +"use strict";var pmtiles=(()=>{var k=Object.defineProperty;var Je=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var Qe=Object.prototype.hasOwnProperty;var Ce=Math.pow;var f=(r,e)=>k(r,"name",{value:e,configurable:!0});var Xe=(r,e)=>{for(var t in e)k(r,t,{get:e[t],enumerable:!0})},_e=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ye(e))!Qe.call(r,i)&&i!==t&&k(r,i,{get:()=>e[i],enumerable:!(n=Je(e,i))||n.enumerable});return r};var et=r=>_e(k({},"__esModule",{value:!0}),r);var m=(r,e,t)=>new Promise((n,i)=>{var a=l=>{try{s(t.next(l))}catch(c){i(c)}},o=l=>{try{s(t.throw(l))}catch(c){i(c)}},s=l=>l.done?n(l.value):Promise.resolve(l.value).then(a,o);s((t=t.apply(r,e)).next())});var Dt={};Xe(Dt,{Compression:()=>Oe,EtagMismatch:()=>B,FetchSource:()=>V,FileSource:()=>se,PMTiles:()=>S,Protocol:()=>ne,ResolvedValueCache:()=>oe,SharedPromiseCache:()=>K,TileType:()=>ae,bytesToHeader:()=>$e,findTile:()=>Fe,getUint64:()=>b,leafletRasterLayer:()=>wt,readVarint:()=>R,tileIdToZxy:()=>At,tileTypeExt:()=>Ze,zxyToTileId:()=>Ie});var w=Uint8Array,E=Uint16Array,tt=Int32Array,Me=new w([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Ue=new w([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),rt=new w([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Ee=f(function(r,e){for(var t=new E(31),n=0;n<31;++n)t[n]=e+=1<>1|(g&21845)<<1,T=(T&52428)>>2|(T&13107)<<2,T=(T&61680)>>4|(T&3855)<<4,te[g]=((T&65280)>>8|(T&255)<<8)>>1;var T,g,Z=f(function(r,e,t){for(var n=r.length,i=0,a=new E(e);i>l]=c}else for(s=new E(n),i=0;i>15-r[i]);return s},"hMap"),F=new w(288);for(g=0;g<144;++g)F[g]=8;var g;for(g=144;g<256;++g)F[g]=9;var g;for(g=256;g<280;++g)F[g]=7;var g;for(g=280;g<288;++g)F[g]=8;var g,Re=new w(32);for(g=0;g<32;++g)Re[g]=5;var g;var at=Z(F,9,1);var st=Z(Re,5,1),_=f(function(r){for(var e=r[0],t=1;te&&(e=r[t]);return e},"max"),z=f(function(r,e,t){var n=e/8|0;return(r[n]|r[n+1]<<8)>>(e&7)&t},"bits"),ee=f(function(r,e){var t=e/8|0;return(r[t]|r[t+1]<<8|r[t+2]<<16)>>(e&7)},"bits16"),ot=f(function(r){return(r+7)/8|0},"shft"),ut=f(function(r,e,t){return(e==null||e<0)&&(e=0),(t==null||t>r.length)&&(t=r.length),new w(r.subarray(e,t))},"slc");var ft=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],y=f(function(r,e,t){var n=new Error(e||ft[r]);if(n.code=r,Error.captureStackTrace&&Error.captureStackTrace(n,y),!t)throw n;return n},"err"),re=f(function(r,e,t,n){var i=r.length,a=n?n.length:0;if(!i||e.f&&!e.l)return t||new w(0);var o=!t,s=o||e.i!=2,l=e.i;o&&(t=new w(i*3));var c=f(function(Ae){var Te=t.length;if(Ae>Te){var De=new w(Math.max(Te*2,Ae));De.set(t),t=De}},"cbuf"),h=e.f||0,u=e.p||0,v=e.b||0,d=e.l,p=e.d,C=e.m,H=e.n,G=i*8;do{if(!d){h=z(r,u,1);var q=z(r,u+1,3);if(u+=3,q)if(q==1)d=at,p=st,C=9,H=5;else if(q==2){var N=z(r,u,31)+257,pe=z(r,u+10,15)+4,me=N+z(r,u+5,31)+1;u+=14;for(var I=new w(me),J=new w(19),x=0;x>4;if(A<16)I[x++]=A;else{var M=0,$=0;for(A==16?($=3+z(r,u,3),u+=2,M=I[x-1]):A==17?($=3+z(r,u,7),u+=3):A==18&&($=11+z(r,u,127),u+=7);$--;)I[x++]=M}}var we=I.subarray(0,N),D=I.subarray(N);C=_(we),H=_(D),d=Z(we,C,1),p=Z(D,H,1)}else y(1);else{var A=ot(u)+4,j=r[A-4]|r[A-3]<<8,W=A+j;if(W>i){l&&y(0);break}s&&c(v+j),t.set(r.subarray(A,W),v),e.b=v+=j,e.p=u=W*8,e.f=h;continue}if(u>G){l&&y(0);break}}s&&c(v+131072);for(var je=(1<>4;if(u+=M&15,u>G){l&&y(0);break}if(M||y(2),U<256)t[v++]=U;else if(U==256){Y=u,d=null;break}else{var xe=U-254;if(U>264){var x=U-257,O=Me[x];xe=z(r,u,(1<>4;Q||y(3),u+=Q&15;var D=it[X];if(X>3){var O=Ue[X];D+=ee(r,u)&(1<G){l&&y(0);break}s&&c(v+131072);var be=v+xe;if(v>3&1)+(e>>4&1);n>0;n-=!r[t++]);return t+(e&2)},"gzs"),ct=f(function(r){var e=r.length;return(r[e-4]|r[e-3]<<8|r[e-2]<<16|r[e-1]<<24)>>>0},"gzl");var vt=f(function(r,e){return((r[0]&15)!=8||r[0]>>4>7||(r[0]<<8|r[1])%31)&&y(6,"invalid zlib data"),(r[1]>>5&1)==+!e&&y(6,"invalid zlib data: "+(r[1]&32?"need":"unexpected")+" dictionary"),(r[1]>>3&4)+2},"zls");function gt(r,e){return re(r,{i:2},e&&e.out,e&&e.dictionary)}f(gt,"inflateSync");function pt(r,e){var t=ht(r);return t+8>r.length&&y(6,"invalid gzip data"),re(r.subarray(t,-8),{i:2},e&&e.out||new w(ct(r)),e&&e.dictionary)}f(pt,"gunzipSync");function mt(r,e){return re(r.subarray(vt(r,e&&e.dictionary),-4),{i:2},e&&e.out,e&&e.dictionary)}f(mt,"unzlibSync");function Be(r,e){return r[0]==31&&r[1]==139&&r[2]==8?pt(r,e):(r[0]&15)!=8||r[0]>>4>7||(r[0]<<8|r[1])%31?gt(r,e):mt(r,e)}f(Be,"decompressSync");var dt=typeof TextDecoder!="undefined"&&new TextDecoder,yt=0;try{dt.decode(lt,{stream:!0}),yt=1}catch(r){}var wt=f((r,e)=>{let t=!1,n="",i=L.GridLayer.extend({createTile:f((a,o)=>{let s=document.createElement("img"),l=new AbortController,c=l.signal;return s.cancel=()=>{l.abort()},t||(r.getHeader().then(h=>{h.tileType===1||h.tileType===6?console.error("Error: archive contains vector tiles, but leafletRasterLayer is for displaying raster tiles. See https://github.com/protomaps/PMTiles/tree/main/js for details."):h.tileType===2?n="image/png":h.tileType===3?n="image/jpeg":h.tileType===4?n="image/webp":h.tileType===5&&(n="image/avif")}),t=!0),r.getZxy(a.z,a.x,a.y,c).then(h=>{if(h){let u=new Blob([h.data],{type:n}),v=window.URL.createObjectURL(u);s.src=v}else s.style.display="none";s.cancel=void 0,o(void 0,s)}).catch(h=>{if(h.name!=="AbortError")throw h}),s},"createTile"),_removeTile:f(function(a){let o=this._tiles[a];o&&(o.el.cancel&&o.el.cancel(),o.el.src&&window.URL.revokeObjectURL(o.el.src),o.el.width=0,o.el.height=0,o.el.deleted=!0,L.DomUtil.remove(o.el),delete this._tiles[a],this.fire("tileunload",{tile:o.el,coords:this._keyToTileCoords(a)}))},"_removeTile")});return new i(e)},"leafletRasterLayer"),xt=f(r=>(e,t)=>{if(t instanceof AbortController)return r(e,t);let n=new AbortController;return r(e,n).then(i=>t(void 0,i.data,i.cacheControl||"",i.expires||""),i=>t(i)).catch(i=>t(i)),{cancel:f(()=>n.abort(),"cancel")}},"v3compat"),ie=class ie{constructor(e){this.tilev4=f((e,t)=>m(this,null,function*(){if(e.type==="json"){let v=e.url.substr(10),d=this.tiles.get(v);if(d||(d=new S(v),this.tiles.set(v,d)),this.metadata){let C=yield d.getTileJson(e.url);return t.signal.throwIfAborted(),{data:C}}let p=yield d.getHeader();return t.signal.throwIfAborted(),(p.minLon>=p.maxLon||p.minLat>=p.maxLat)&&console.error(`Bounds of PMTiles archive ${p.minLon},${p.minLat},${p.maxLon},${p.maxLat} are not valid.`),{data:{tiles:[`${e.url}/{z}/{x}/{y}`],minzoom:p.minZoom,maxzoom:p.maxZoom,bounds:[p.minLon,p.minLat,p.maxLon,p.maxLat]}}}let n=new RegExp(/pmtiles:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/),i=e.url.match(n);if(!i)throw new Error("Invalid PMTiles protocol URL");let a=i[1],o=this.tiles.get(a);o||(o=new S(a),this.tiles.set(a,o));let s=i[2],l=i[3],c=i[4],h=yield o==null?void 0:o.getZxy(+s,+l,+c,t.signal);if(t.signal.throwIfAborted(),h)return{data:new Uint8Array(h.data),cacheControl:h.cacheControl,expires:h.expires};let u=yield o.getHeader();if(u.tileType===1||u.tileType===6){if(this.errorOnMissingTile)throw new Error("Tile not found.");return{data:new Uint8Array}}return{data:null}}),"tilev4");this.tile=xt(this.tilev4);this.tiles=new Map,this.metadata=(e==null?void 0:e.metadata)||!1,this.errorOnMissingTile=(e==null?void 0:e.errorOnMissingTile)||!1}add(e){this.tiles.set(e.source.getKey(),e)}get(e){return this.tiles.get(e)}};f(ie,"Protocol");var ne=ie;function P(r,e){return(e>>>0)*4294967296+(r>>>0)}f(P,"toNum");function bt(r,e){let t=e.buf,n=t[e.pos++],i=(n&112)>>4;if(n<128||(n=t[e.pos++],i|=(n&127)<<3,n<128)||(n=t[e.pos++],i|=(n&127)<<10,n<128)||(n=t[e.pos++],i|=(n&127)<<17,n<128)||(n=t[e.pos++],i|=(n&127)<<24,n<128)||(n=t[e.pos++],i|=(n&1)<<31,n<128))return P(r,i);throw new Error("Expected varint not more than 10 bytes")}f(bt,"readVarintRemainder");function R(r){let e=r.buf,t=e[r.pos++],n=t&127;return t<128||(t=e[r.pos++],n|=(t&127)<<7,t<128)||(t=e[r.pos++],n|=(t&127)<<14,t<128)||(t=e[r.pos++],n|=(t&127)<<21,t<128)?n:(t=e[r.pos],n|=(t&15)<<28,bt(n,r))}f(R,"readVarint");function He(r,e,t,n,i){return i===0?n!==0?[r-1-t,r-1-e]:[t,e]:[e,t]}f(He,"rotate");function Ie(r,e,t){if(r>26)throw new Error("Tile zoom level exceeds max safe number limit (26)");if(e>=1<=1<0;s>>=1){let l=a&s,c=o&s;n+=(3*l^c)*(1<>1;if(e>26)throw new Error("Tile zoom level exceeds max safe number limit (26)");let t=((1<(a[a.Unknown=0]="Unknown",a[a.None=1]="None",a[a.Gzip=2]="Gzip",a[a.Brotli=3]="Brotli",a[a.Zstd=4]="Zstd",a))(Oe||{});function ue(r,e){return m(this,null,function*(){if(e===1||e===0)return r;if(e===2){if(typeof globalThis.DecompressionStream=="undefined")return Be(new Uint8Array(r));let t=new Response(r).body;if(!t)throw new Error("Failed to read response stream");let n=t.pipeThrough(new globalThis.DecompressionStream("gzip"));return new Response(n).arrayBuffer()}throw new Error("Compression method not supported")})}f(ue,"defaultDecompress");var ae=(s=>(s[s.Unknown=0]="Unknown",s[s.Mvt=1]="Mvt",s[s.Png=2]="Png",s[s.Jpeg=3]="Jpeg",s[s.Webp=4]="Webp",s[s.Avif=5]="Avif",s[s.Mlt=6]="Mlt",s))(ae||{});function Ze(r){return r===1?".mvt":r===2?".png":r===3?".jpg":r===4?".webp":r===5?".avif":r===6?".mlt":""}f(Ze,"tileTypeExt");var Tt=127;function Fe(r,e){let t=0,n=r.length-1;for(;t<=n;){let i=n+t>>1,a=e-r[i].tileId;if(a>0)t=i+1;else if(a<0)n=i-1;else return r[i]}return n>=0&&(r[n].runLength===0||e-r[n].tileId-1,o=/Chrome|Chromium|Edg|OPR|Brave/.test(i);this.chromeWindowsNoCache=!1,a&&o&&(this.chromeWindowsNoCache=!0)}getKey(){return this.url}setHeaders(e){this.customHeaders=e}getBytes(e,t,n,i){return m(this,null,function*(){let a,o;n?o=n:(a=new AbortController,o=a.signal);let s=new Headers(this.customHeaders);s.set("range",`bytes=${e}-${e+t-1}`);let l;this.mustReload?l="reload":this.chromeWindowsNoCache&&(l="no-store");let c=yield fetch(this.url,{signal:o,cache:l,headers:s,credentials:this.credentials});if(e===0&&c.status===416){let d=c.headers.get("Content-Range");if(!d||!d.startsWith("bytes */"))throw new Error("Missing content-length on 416 response");let p=+d.substr(8);s.set("range",`bytes=0-${p-1}`),c=yield fetch(this.url,{signal:o,cache:"reload",headers:s,credentials:this.credentials})}let h=c.headers.get("Etag");if(h!=null&&h.startsWith("W/")&&(h=null),c.status===416||i&&h&&h!==i)throw this.mustReload=!0,new B(`Server returned non-matching ETag ${i} after one retry. Check browser extensions and servers for issues that may affect correct ETag headers.`);if(c.status>=300)throw new Error(`Bad response code: ${c.status}`);let u=c.headers.get("Content-Length");if(c.status===200&&(!u||+u>t))throw a&&a.abort(),new Error("Server returned no content-length header or content-length exceeding request. Check that your storage backend supports HTTP Byte Serving.");return{data:yield c.arrayBuffer(),etag:h||void 0,cacheControl:c.headers.get("Cache-Control")||void 0,expires:c.headers.get("Expires")||void 0}})}};f(le,"FetchSource");var V=le;function b(r,e){let t=r.getUint32(e+4,!0),n=r.getUint32(e+0,!0);return t*Ce(2,32)+n}f(b,"getUint64");function $e(r,e){let t=new DataView(r),n=t.getUint8(7);if(n>3)throw new Error(`Archive is spec version ${n} but this library supports up to spec version 3`);return{specVersion:n,rootDirectoryOffset:b(t,8),rootDirectoryLength:b(t,16),jsonMetadataOffset:b(t,24),jsonMetadataLength:b(t,32),leafDirectoryOffset:b(t,40),leafDirectoryLength:b(t,48),tileDataOffset:b(t,56),tileDataLength:b(t,64),numAddressedTiles:b(t,72),numTileEntries:b(t,80),numTileContents:b(t,88),clustered:t.getUint8(96)===1,internalCompression:t.getUint8(97),tileCompression:t.getUint8(98),tileType:t.getUint8(99),minZoom:t.getUint8(100),maxZoom:t.getUint8(101),minLon:t.getInt32(102,!0)/1e7,minLat:t.getInt32(106,!0)/1e7,maxLon:t.getInt32(110,!0)/1e7,maxLat:t.getInt32(114,!0)/1e7,centerZoom:t.getUint8(118),centerLon:t.getInt32(119,!0)/1e7,centerLat:t.getInt32(123,!0)/1e7,etag:e}}f($e,"bytesToHeader");function ke(r){let e={buf:new Uint8Array(r),pos:0},t=R(e),n=[],i=0;for(let a=0;a0?n[a].offset=n[a-1].offset+n[a-1].length:n[a].offset=o-1}return n}f(ke,"deserializeIndex");var he=class he extends Error{};f(he,"EtagMismatch");var B=he;function Ve(r,e){return m(this,null,function*(){let t=yield r.getBytes(0,16384);if(new DataView(t.data).getUint16(0,!0)!==19792)throw new Error("Wrong magic number for PMTiles archive");let i=t.data.slice(0,Tt),a=$e(i,t.etag),o=t.data.slice(a.rootDirectoryOffset,a.rootDirectoryOffset+a.rootDirectoryLength),s=`${r.getKey()}|${a.etag||""}|${a.rootDirectoryOffset}|${a.rootDirectoryLength}`,l=ke(yield e(o,a.internalCompression));return[a,[s,l.length,l]]})}f(Ve,"getHeaderAndRoot");function Ke(r,e,t,n,i){return m(this,null,function*(){let a=yield r.getBytes(t,n,void 0,i.etag),o=yield e(a.data,i.internalCompression),s=ke(o);if(s.length===0)throw new Error("Empty directory is invalid");return s})}f(Ke,"getDirectory");var ce=class ce{constructor(e=100,t=!0,n=ue){this.cache=new Map,this.maxCacheEntries=e,this.counter=1,this.decompress=n}getHeader(e){return m(this,null,function*(){let t=e.getKey(),n=this.cache.get(t);if(n)return n.lastUsed=this.counter++,n.data;let i=yield Ve(e,this.decompress);return i[1]&&this.cache.set(i[1][0],{lastUsed:this.counter++,data:i[1][2]}),this.cache.set(t,{lastUsed:this.counter++,data:i[0]}),this.prune(),i[0]})}getDirectory(e,t,n,i){return m(this,null,function*(){let a=`${e.getKey()}|${i.etag||""}|${t}|${n}`,o=this.cache.get(a);if(o)return o.lastUsed=this.counter++,o.data;let s=yield Ke(e,this.decompress,t,n,i);return this.cache.set(a,{lastUsed:this.counter++,data:s}),this.prune(),s})}prune(){if(this.cache.size>this.maxCacheEntries){let e=1/0,t;this.cache.forEach((n,i)=>{n.lastUsed{Ve(e,this.decompress).then(s=>{s[1]&&this.cache.set(s[1][0],{lastUsed:this.counter++,data:Promise.resolve(s[1][2])}),a(s[0]),this.prune()}).catch(s=>{o(s)})});return this.cache.set(t,{lastUsed:this.counter++,data:i}),i})}getDirectory(e,t,n,i){return m(this,null,function*(){let a=`${e.getKey()}|${i.etag||""}|${t}|${n}`,o=this.cache.get(a);if(o)return o.lastUsed=this.counter++,yield o.data;let s=new Promise((l,c)=>{Ke(e,this.decompress,t,n,i).then(h=>{l(h),this.prune()}).catch(h=>{c(h)})});return this.cache.set(a,{lastUsed:this.counter++,data:s}),s})}prune(){if(this.cache.size>=this.maxCacheEntries){let e=1/0,t;this.cache.forEach((n,i)=>{n.lastUsed{this.getHeader(e).then(o=>{i(),this.invalidations.delete(t)}).catch(o=>{a(o)})});this.invalidations.set(t,n)})}};f(ve,"SharedPromiseCache");var K=ve,ge=class ge{constructor(e,t,n){typeof e=="string"?this.source=new V(e):this.source=e,n?this.decompress=n:this.decompress=ue,t?this.cache=t:this.cache=new K}getHeader(){return m(this,null,function*(){return yield this.cache.getHeader(this.source)})}getZxyAttempt(e,t,n,i){return m(this,null,function*(){let a=Ie(e,t,n),o=yield this.cache.getHeader(this.source);if(eo.maxZoom)return;let s=o.rootDirectoryOffset,l=o.rootDirectoryLength;for(let c=0;c<=3;c++){let h=yield this.cache.getDirectory(this.source,s,l,o),u=Fe(h,a);if(u){if(u.runLength>0){let v=yield this.source.getBytes(o.tileDataOffset+u.offset,u.length,i,o.etag);return{data:yield this.decompress(v.data,o.tileCompression),cacheControl:v.cacheControl,expires:v.expires}}s=o.leafDirectoryOffset+u.offset,l=u.length}else return}throw new Error("Maximum directory depth exceeded")})}getZxy(e,t,n,i){return m(this,null,function*(){try{return yield this.getZxyAttempt(e,t,n,i)}catch(a){if(a instanceof B)return this.cache.invalidate(this.source),yield this.getZxyAttempt(e,t,n,i);throw a}})}getMetadataAttempt(){return m(this,null,function*(){let e=yield this.cache.getHeader(this.source),t=yield this.source.getBytes(e.jsonMetadataOffset,e.jsonMetadataLength,void 0,e.etag),n=yield this.decompress(t.data,e.internalCompression),i=new TextDecoder("utf-8");return JSON.parse(i.decode(n))})}getMetadata(){return m(this,null,function*(){try{return yield this.getMetadataAttempt()}catch(e){if(e instanceof B)return this.cache.invalidate(this.source),yield this.getMetadataAttempt();throw e}})}getTileJson(e){return m(this,null,function*(){let t=yield this.getHeader(),n=yield this.getMetadata(),i=Ze(t.tileType);return{tilejson:"3.0.0",scheme:"xyz",tiles:[`${e}/{z}/{x}/{y}${i}`],vector_layers:n.vector_layers,attribution:n.attribution,description:n.description,name:n.name,version:n.version,bounds:[t.minLon,t.minLat,t.maxLon,t.maxLat],center:[t.centerLon,t.centerLat,t.centerZoom],minzoom:t.minZoom,maxzoom:t.maxZoom}})}};f(ge,"PMTiles");var S=ge;return et(Dt);})(); +//# sourceMappingURL=pmtiles.js.map \ No newline at end of file From c2b53fcda9016f5007dd9af835ddb961ea4e0e4a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 13:04:32 +0000 Subject: [PATCH 6/6] fix(frontend): align sharing UI with the merged role-grants backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge brought in main's ReBAC→role-grants migration, which changed the grant contract the Svelte sharing UI (branched before it) was written against: GrantDto dropped `permission` and now carries an explicit `role` (owner/editor/viewer/commenter/contributor), and the "admin" role was renamed "owner". Left unchanged, the share UI derived roles from a now-absent `permission` field and showed every member as "viewer". - grants.ts: ShareRole is now viewer|editor|owner; Grant carries `role` (not `permission`); `roleFromPermissions` → `displayRole`, which collapses the unexposed commenter→viewer and contributor→editor. - ShareDialog.svelte: read each subject's role directly (role-grants emits one row per subject); role picker exposes Owner instead of Admin. - shared/+page.svelte (My Shares): same owner rename; role badges run through displayRole so server-only roles render sensibly. Create/update already POST `role`, so only the read/display path and the role literal needed fixing. npm run check, test:unit (36) and build pass. --- frontend/src/lib/api/endpoints/grants.ts | 26 ++++++++++++++----- .../src/lib/components/ShareDialog.svelte | 21 ++++++++++----- frontend/src/routes/shared/+page.svelte | 5 ++-- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/frontend/src/lib/api/endpoints/grants.ts b/frontend/src/lib/api/endpoints/grants.ts index 6c880e8a..cc96c0e1 100644 --- a/frontend/src/lib/api/endpoints/grants.ts +++ b/frontend/src/lib/api/endpoints/grants.ts @@ -7,7 +7,9 @@ import type { ResourceBody, ResourcePage } from './resources'; const JSON_HEADERS = { 'Content-Type': 'application/json' }; export type SubjectType = 'user' | 'group' | 'email' | 'token'; -export type ShareRole = 'viewer' | 'editor' | 'admin'; +/** Roles the share UI exposes. The backend role enum also has `commenter` and + * `contributor`, which {@link displayRole} collapses to the nearest of these. */ +export type ShareRole = 'viewer' | 'editor' | 'owner'; export interface GrantSubject { type: SubjectType; @@ -25,13 +27,17 @@ export type GrantSubjectInput = | { type: 'token'; id: string } | { type: 'email'; email: string }; -/** One grant carries a single permission; a subject's role is derived from all of theirs. */ +/** + * One role grant for a (subject, resource). Role-keyed since the role-grants + * migration: each row carries an explicit `role` (the backend enum, which may + * be `owner`/`editor`/`viewer`/`commenter`/`contributor`). + */ export interface Grant { id: string; granted_at?: string; granted_by?: string; subject: GrantSubject; - permission: string; + role: string; resource: { type: ItemType; id: string }; expires_at?: string | null; } @@ -56,10 +62,16 @@ export interface CreateGrantResponse { notification: NotifyOutcomeSet; } -export function roleFromPermissions(perms: Iterable): ShareRole { - const set = new Set(perms); - if (set.has('delete') || set.has('share')) return 'admin'; - if (set.has('create') || set.has('update')) return 'editor'; +/** + * Map a backend role string to the role the UI exposes. The server may emit the + * full enum (`owner`/`editor`/`viewer`/`commenter`/`contributor`); the picker + * only shows Owner/Editor/Viewer, so collapse the two unexposed roles to their + * closest neighbour rather than render an unknown option. + */ +export function displayRole(role: string | undefined): ShareRole { + if (role === 'owner' || role === 'editor' || role === 'viewer') return role; + if (role === 'contributor') return 'editor'; + if (role === 'commenter') return 'viewer'; return 'viewer'; } diff --git a/frontend/src/lib/components/ShareDialog.svelte b/frontend/src/lib/components/ShareDialog.svelte index a054e141..8dbd6403 100644 --- a/frontend/src/lib/components/ShareDialog.svelte +++ b/frontend/src/lib/components/ShareDialog.svelte @@ -10,10 +10,10 @@ import { createGrant, expiryToIso, + displayRole, fetchGrantsForResource, notifyGrantRecipient, revokeGrant, - roleFromPermissions, updateGrantRole, type Grant, type GrantSubject, @@ -51,11 +51,11 @@ let directoryAvailable = $state(true); const ROLES: { v: ShareRole; l: string; icon: string }[] = [ - { v: 'admin', l: t('share.role.canManage', 'Can manage'), icon: 'crown' }, + { v: 'owner', l: t('share.role.canManage', 'Can manage'), icon: 'crown' }, { v: 'editor', l: t('share.role.canEdit', 'Can edit'), icon: 'pencil-alt' }, { v: 'viewer', l: t('share.role.canView', 'Can view'), icon: 'eye' } ]; - const ROLE_ORDER: ShareRole[] = ['admin', 'editor', 'viewer']; + const ROLE_ORDER: ShareRole[] = ['owner', 'editor', 'viewer']; function roleLabel(r: ShareRole): string { return ROLES.find((x) => x.v === r)?.l ?? r; } @@ -89,13 +89,20 @@ function groupGrants(grants: Grant[]): Member[] { const bySubject = new Map< string, - { subject: GrantSubject; perms: string[]; ids: string[]; expiry: string | null } + { subject: GrantSubject; role: ShareRole; ids: string[]; expiry: string | null } >(); for (const g of grants) { if (g.subject.type === 'token') continue; const key = `${g.subject.type}:${g.subject.id}`; - const entry = bySubject.get(key) ?? { subject: g.subject, perms: [], ids: [], expiry: null }; - entry.perms.push(g.permission); + const entry = bySubject.get(key) ?? { + subject: g.subject, + role: 'viewer' as ShareRole, + ids: [], + expiry: null + }; + // Role-grants emit one row per (subject, resource), so the row's role + // is the subject's role directly. + entry.role = displayRole(g.role); entry.ids.push(g.id); if (g.expires_at && !entry.expiry) entry.expiry = isoToDate(g.expires_at); bySubject.set(key, entry); @@ -103,7 +110,7 @@ return [...bySubject.values()].map((e) => ({ subject: e.subject, recipient: resolveRecipient(e.subject.type as 'user' | 'group', e.subject.id), - role: roleFromPermissions(e.perms), + role: e.role, grantIds: e.ids, notifyGrantId: e.ids[0], expiry: e.expiry, diff --git a/frontend/src/routes/shared/+page.svelte b/frontend/src/routes/shared/+page.svelte index a86ba371..d333ecb7 100644 --- a/frontend/src/routes/shared/+page.svelte +++ b/frontend/src/routes/shared/+page.svelte @@ -4,6 +4,7 @@ import { goto } from '$app/navigation'; import { onMount } from 'svelte'; import { + displayRole, expiryToIso, fetchMyShares, notifyGrantRecipient, @@ -33,12 +34,12 @@ ]; const ROLES: { v: ShareRole; l: string; icon: string }[] = [ - { v: 'admin', l: t('share.role.canManage', 'Can manage'), icon: 'crown' }, + { v: 'owner', l: t('share.role.canManage', 'Can manage'), icon: 'crown' }, { v: 'editor', l: t('share.role.canEdit', 'Can edit'), icon: 'pencil-alt' }, { v: 'viewer', l: t('share.role.canView', 'Can view'), icon: 'eye' } ]; function roleMeta(r: string) { - return ROLES.find((x) => x.v === r) ?? ROLES[2]; + return ROLES.find((x) => x.v === displayRole(r)) ?? ROLES[2]; } let raw = $state([]);