mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
Compare commits
187 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce57f66136 | |||
| 1a6eb5a7f4 | |||
| 9cda8ca6f4 | |||
| 663573062d | |||
| 00eab7ac61 | |||
| 24be90b95f | |||
| 3ecf27c6b0 | |||
| 980521f957 | |||
| fe44dc92a9 | |||
| fad410ef23 | |||
| 15b8908b1a | |||
| 8e15b22d8c | |||
| ec83fc82d5 | |||
| a573acedd0 | |||
| 28b46dc0cb | |||
| ba9e4de036 | |||
| e0788dc654 | |||
| dba8e315ab | |||
| 81ae7a17a6 | |||
| 271f855761 | |||
| 3e6d915227 | |||
| a6a7e4370a | |||
| cc00e77dfb | |||
| 66c70c0e76 | |||
| 0e8b3bbfb3 | |||
| a3a9f35005 | |||
| 4056bd0104 | |||
| bd68e47e03 | |||
| d6068310b4 | |||
| e02661974e | |||
| 1113f17a43 | |||
| d42091ccb1 | |||
| 57efb91bd3 | |||
| da9b43681e | |||
| 4966f9b152 | |||
| e1bbceb9a6 | |||
| 895c1817ae | |||
| 642024ba9d | |||
| 147d028036 | |||
| 992691e6e0 | |||
| 9aaa6c731c | |||
| fd91b11c6c | |||
| af8b0ddf3a | |||
| 879aa2c3d8 | |||
| c180d0e487 | |||
| a062f7a165 | |||
| cbd0dd4a0b | |||
| 2d6d829581 | |||
| 5cea30cc5c | |||
| bca85a49d6 | |||
| c9cdfa0f17 | |||
| 412962204c | |||
| a42ac3d450 | |||
| 642c92f779 | |||
| ccb35517bb | |||
| cbdb37ed0a | |||
| aa27d57624 | |||
| 3829b6cbef | |||
| 17da762984 | |||
| 859f16740b | |||
| 7981ef462e | |||
| 2d835da0e3 | |||
| a3559b7c33 | |||
| 803f1f0b81 | |||
| 4e8f533b91 | |||
| 7b0d8fe140 | |||
| 2f92278a9d | |||
| 53608eae35 | |||
| 0e4a1e7419 | |||
| 9125996e97 | |||
| fa4872e89e | |||
| 6d6f3a8a8e | |||
| 975b4dcaab | |||
| 6683c515cf | |||
| cc5c800238 | |||
| cfaee93af9 | |||
| 74eddb0638 | |||
| 7c83a9d4f0 | |||
| 2678c4e279 | |||
| b0bde4b375 | |||
| 724e37d5b7 | |||
| 33184e9d8d | |||
| 7520c329d0 | |||
| d7a5fda53c | |||
| 236a63dadc | |||
| 89b94e5d19 | |||
| 97c459be67 | |||
| d0ed6865cb | |||
| 65b89a1b24 | |||
| 1fdee33206 | |||
| 7b69727a30 | |||
| 66c26af34b | |||
| b4f009513e | |||
| fcffa3dfa0 | |||
| 1980b94825 | |||
| bea1637519 | |||
| 37355452e1 | |||
| 057360c6be | |||
| f12bfc1ff7 | |||
| f5d794220e | |||
| a3c1c6cccd | |||
| 4b105586a9 | |||
| d2641db895 | |||
| 1111df65cd | |||
| e455154b7d | |||
| ef24b3c07d | |||
| 2352f3c5d9 | |||
| 568dd4c321 | |||
| b6478fee84 | |||
| 5d2aad3668 | |||
| 9331ac2df8 | |||
| 9f4728e279 | |||
| 628b08339a | |||
| 68842dbea2 | |||
| b1510cd6d7 | |||
| af92224e10 | |||
| c24ff44e09 | |||
| 90c190df78 | |||
| 17ec2f4ac5 | |||
| 9881c53f00 | |||
| 721651e2e2 | |||
| a3fd79dae8 | |||
| 616d9297eb | |||
| ee6b98edaa | |||
| cf43e2b4fe | |||
| 614baf153b | |||
| 4f3577f009 | |||
| d5e4b8bb59 | |||
| 1a897faaa2 | |||
| 6f1a91cc05 | |||
| 60848ea903 | |||
| 2309d1434b | |||
| dcc2bacb22 | |||
| 69d7532c6c | |||
| 85ce0d32bf | |||
| fc0997fd90 | |||
| df64de5306 | |||
| ea44468fad | |||
| 59e945562d | |||
| 22f33bab7c | |||
| e0a8521566 | |||
| b5803f42da | |||
| 5de1c8e3ed | |||
| ef87210b3d | |||
| c172d3bd5e | |||
| 53132acb0a | |||
| d6472f0876 | |||
| 873c963043 | |||
| 03a70d768a | |||
| 0aeaa43112 | |||
| 92d5d0b237 | |||
| 0ce74d34de | |||
| 00b5328676 | |||
| 2ebdc2baea | |||
| 621ef4f0cf | |||
| 26b9338da5 | |||
| 618f56577d | |||
| 0a05ce6133 | |||
| cb9d6be3b9 | |||
| b76f5adaad | |||
| 41fa77b29d | |||
| 05b3c65b0f | |||
| e0ab9d9b5e | |||
| 55280db672 | |||
| 32bbc6911f | |||
| 5814542128 | |||
| 18b5781522 | |||
| 49ab9875ba | |||
| 25f4b8c2b4 | |||
| 4d43f86c51 | |||
| f170ede8da | |||
| 7861b5b186 | |||
| 3a9bdfbb06 | |||
| ab7999a946 | |||
| 0f02261ee6 | |||
| aff8dba2cb | |||
| f6a8247c48 | |||
| 7879e1f600 | |||
| 3cb70f0696 | |||
| fbb44df548 | |||
| bc3ce893c4 | |||
| ae96352189 | |||
| 1ad53c2581 | |||
| 2f97a3debc | |||
| 40b5346f9e | |||
| d6b4573b79 | |||
| 4878850b25 |
@@ -43,6 +43,9 @@ POSTMARK_TOKEN=
|
||||
# for custom drawio server
|
||||
DRAWIO_URL=
|
||||
|
||||
# Gotenberg URL for server-side PDF export
|
||||
GOTENBERG_URL=
|
||||
|
||||
DISABLE_TELEMETRY=false
|
||||
|
||||
# Enable debug logging in production (default: false)
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version tag (e.g. v0.25.3)'
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
VERSION: ${{ inputs.version || github.ref_name }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
suffix: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
suffix: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Generate token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.BUILD_APP_ID }}
|
||||
private-key: ${{ secrets.BUILD_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Checkout with submodules
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,name=docmost/docmost,push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=${{ matrix.suffix }}
|
||||
cache-to: type=gha,scope=${{ matrix.suffix }},mode=max
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digest-${{ matrix.suffix }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Strip v prefix
|
||||
id: strip-v
|
||||
run: echo "version=${VERSION#v}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Export Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: false
|
||||
tags: |
|
||||
docmost/docmost:latest
|
||||
docmost/docmost:${{ steps.strip-v.outputs.version }}
|
||||
outputs: type=docker,dest=docmost-${{ matrix.suffix }}.docker.tar
|
||||
cache-from: type=gha,scope=${{ matrix.suffix }}
|
||||
|
||||
- name: Compress image
|
||||
run: gzip docmost-${{ matrix.suffix }}.docker.tar
|
||||
|
||||
- name: Upload image archive
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docker-image-${{ matrix.suffix }}
|
||||
path: docmost-${{ matrix.suffix }}.docker.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: digest-*
|
||||
path: /tmp/digests
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: docmost/docmost
|
||||
tags: |
|
||||
type=semver,pattern={{version}},value=${{ env.VERSION }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ env.VERSION }},enable=${{ !contains(env.VERSION, '-') }}
|
||||
type=raw,value=latest,enable=${{ !contains(env.VERSION, '-') }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf 'docmost/docmost@sha256:%s ' *)
|
||||
|
||||
- name: Download image archives
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: docker-image-*
|
||||
path: /tmp/images
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ env.VERSION }}
|
||||
files: |
|
||||
/tmp/images/docmost-amd64.docker.tar.gz
|
||||
/tmp/images/docmost-arm64.docker.tar.gz
|
||||
draft: true
|
||||
+42
-40
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "client",
|
||||
"private": true,
|
||||
"version": "0.25.1",
|
||||
"version": "0.80.1",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
@@ -10,74 +10,76 @@
|
||||
"format": "prettier --write \"src/**/*.tsx\" \"src/**/*.ts\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@casl/react": "^4.0.0",
|
||||
"@casl/react": "^5.0.1",
|
||||
"@docmost/editor-ext": "workspace:*",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@excalidraw/excalidraw": "0.18.0-c158187",
|
||||
"@mantine/core": "^8.3.12",
|
||||
"@mantine/dates": "^8.3.12",
|
||||
"@mantine/form": "^8.3.12",
|
||||
"@mantine/hooks": "^8.3.12",
|
||||
"@mantine/modals": "^8.3.12",
|
||||
"@mantine/notifications": "^8.3.12",
|
||||
"@mantine/spotlight": "^8.3.12",
|
||||
"@tabler/icons-react": "^3.36.1",
|
||||
"@tanstack/react-query": "^5.90.17",
|
||||
"@excalidraw/excalidraw": "0.18.0-3a5ef40",
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@mantine/dates": "^8.3.18",
|
||||
"@mantine/form": "^8.3.18",
|
||||
"@mantine/hooks": "^8.3.18",
|
||||
"@mantine/modals": "^8.3.18",
|
||||
"@mantine/notifications": "^8.3.18",
|
||||
"@mantine/spotlight": "^8.3.18",
|
||||
"@tabler/icons-react": "^3.40.0",
|
||||
"@tanstack/react-query": "5.90.17",
|
||||
"alfaaz": "^1.1.0",
|
||||
"axios": "^1.13.2",
|
||||
"axios": "1.15.0",
|
||||
"blueimp-load-image": "^5.16.0",
|
||||
"clsx": "^2.1.1",
|
||||
"emoji-mart": "^5.6.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"highlightjs-sap-abap": "^0.3.0",
|
||||
"i18next": "^23.16.8",
|
||||
"i18next-http-backend": "^2.7.3",
|
||||
"jotai": "^2.16.2",
|
||||
"i18next": "25.10.1",
|
||||
"i18next-http-backend": "3.0.6",
|
||||
"jotai": "^2.18.1",
|
||||
"jotai-optics": "^0.4.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"katex": "0.16.27",
|
||||
"katex": "0.16.40",
|
||||
"lowlight": "^3.3.0",
|
||||
"mantine-form-zod-resolver": "^1.3.0",
|
||||
"mermaid": "^11.12.2",
|
||||
"mermaid": "^11.13.0",
|
||||
"mitt": "^3.0.1",
|
||||
"posthog-js": "^1.255.1",
|
||||
"posthog-js": "1.372.2",
|
||||
"react": "^18.3.1",
|
||||
"react-arborist": "3.4.0",
|
||||
"react-clear-modal": "^2.0.17",
|
||||
"react-clear-modal": "^2.0.18",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-drawio": "^1.0.7",
|
||||
"react-error-boundary": "^4.1.2",
|
||||
"react-helmet-async": "^2.0.5",
|
||||
"react-i18next": "^15.0.1",
|
||||
"react-router-dom": "^7.12.0",
|
||||
"semver": "^7.7.3",
|
||||
"react-error-boundary": "^6.1.1",
|
||||
"react-helmet-async": "^3.0.0",
|
||||
"react-i18next": "16.5.8",
|
||||
"react-router-dom": "^7.13.1",
|
||||
"semver": "^7.7.4",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"tiptap-extension-global-drag-handle": "^0.1.18",
|
||||
"zod": "^3.25.76"
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.16.0",
|
||||
"@tanstack/eslint-plugin-query": "^5.62.1",
|
||||
"@eslint/js": "^9.28.0",
|
||||
"@tanstack/eslint-plugin-query": "^5.94.4",
|
||||
"@types/blueimp-load-image": "^5.16.6",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/katex": "^0.16.8",
|
||||
"@types/node": "22.19.1",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"eslint": "^9.15.0",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^15.13.0",
|
||||
"optics-ts": "^2.4.1",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss": "^8.5.12",
|
||||
"postcss-preset-mantine": "^1.18.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "^3.4.1",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.17.0",
|
||||
"vite": "^7.2.4"
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.57.1",
|
||||
"vite": "8.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Mitglieder hinzufügen",
|
||||
"Add to groups": "Zu Gruppen hinzufügen",
|
||||
"Add space members": "Bereichsmitglieder hinzufügen",
|
||||
"Add to favorites": "Zu Favoriten hinzufügen",
|
||||
"Admin": "Administrator",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Sind Sie sicher, dass Sie diese Gruppe löschen möchten? Mitglieder verlieren den Zugang zu den Ressourcen, auf die diese Gruppe zugreifen kann.",
|
||||
"Are you sure you want to delete this page?": "Sind Sie sicher, dass Sie diese Seite löschen möchten?",
|
||||
@@ -41,32 +42,32 @@
|
||||
"Date": "Datum",
|
||||
"Delete": "Löschen",
|
||||
"Delete group": "Gruppe löschen",
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Sind Sie sicher, dass Sie diese Seite löschen möchten? Dadurch werden ihre Unterseiten und die Seitengeschichte gelöscht. Diese Aktion ist unwiderruflich.",
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Sind Sie sicher, dass Sie diese Seite löschen möchten? Dabei werden auch alle Unterseiten und der Seitenverlauf gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"Description": "Beschreibung",
|
||||
"Details": "Details",
|
||||
"e.g ACME": "z.B. ACME",
|
||||
"e.g ACME Inc": "z.B. ACME Inc.",
|
||||
"e.g Developers": "z.B. Entwickler",
|
||||
"e.g Group for developers": "z.B. Gruppe für Entwickler",
|
||||
"e.g product": "z.B. Produkt",
|
||||
"e.g Product Team": "z.B. Produktteam",
|
||||
"e.g Sales": "z.B. Vertrieb",
|
||||
"e.g Space for product team": "z.B. Bereich für das Produktteam",
|
||||
"e.g Space for sales team to collaborate": "z.B. Bereich für das Vertriebsteam zur Zusammenarbeit",
|
||||
"e.g ACME": "z. B. ACME",
|
||||
"e.g ACME Inc": "z. B. ACME GmbH",
|
||||
"e.g Developers": "z. B. Entwickler",
|
||||
"e.g Group for developers": "z. B. Gruppe für Entwickler",
|
||||
"e.g product": "z. B. Produkt",
|
||||
"e.g Product Team": "z. B. Produktteam",
|
||||
"e.g Sales": "z. B. Vertrieb",
|
||||
"e.g Space for product team": "z. B. Bereich für das Produktteam",
|
||||
"e.g Space for sales team to collaborate": "z. B. Bereich zur Zusammenarbeit für das Vertriebsteam",
|
||||
"Edit": "Bearbeiten",
|
||||
"Read": "Lesen",
|
||||
"Edit group": "Gruppe bearbeiten",
|
||||
"Email": "E-Mail",
|
||||
"Enter a strong password": "Geben Sie ein starkes Passwort ein",
|
||||
"Enter valid email addresses separated by comma or space max_50": "Geben Sie gültige E-Mail-Adressen ein, getrennt durch Kommas oder Leerzeichen [max: 50]",
|
||||
"enter valid emails addresses": "gültige E-Mail-Adressen eingeben",
|
||||
"enter valid emails addresses": "Geben Sie gültige E-Mail-Adressen ein",
|
||||
"Enter your current password": "Geben Sie Ihr aktuelles Passwort ein",
|
||||
"enter your full name": "Geben Sie Ihren vollständigen Namen ein",
|
||||
"Enter your new password": "Geben Sie Ihr neues Passwort ein",
|
||||
"Enter your new preferred email": "Geben Sie Ihre neue bevorzugte E-Mail ein",
|
||||
"Enter your password": "Geben Sie Ihr Passwort ein",
|
||||
"Error fetching page data.": "Fehler beim Abrufen der Seitendaten.",
|
||||
"Error loading page history.": "Fehler beim Laden der Seitengeschichte.",
|
||||
"Error loading page history.": "Fehler beim Laden des Seitenverlaufs.",
|
||||
"Export": "Exportieren",
|
||||
"Failed to create page": "Erstellung der Seite fehlgeschlagen",
|
||||
"Failed to delete page": "Löschen der Seite fehlgeschlagen",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Import der Seiten fehlgeschlagen",
|
||||
"Failed to load page. An error occurred.": "Seite konnte nicht geladen werden. Es ist ein Fehler aufgetreten.",
|
||||
"Failed to update data": "Aktualisierung der Daten fehlgeschlagen",
|
||||
"Favorite spaces": "Favorisierte Bereiche",
|
||||
"Favorite spaces appear here": "Favorisierte Bereiche werden hier angezeigt",
|
||||
"Favorites": "Favoriten",
|
||||
"Full access": "Voller Zugriff",
|
||||
"Full page width": "Volle Seitenbreite",
|
||||
"Full width": "Volle Breite",
|
||||
@@ -87,11 +91,12 @@
|
||||
"Import pages": "Seiten importieren",
|
||||
"Import pages & space settings": "Seiten und Bereichseinstellungen importieren",
|
||||
"Importing pages": "Seiten werden importiert",
|
||||
"invalid invitation link": "ungültiger Einladungslink",
|
||||
"invalid invitation link": "Ungültiger Einladungslink",
|
||||
"Invitation signup": "Einladung zur Anmeldung",
|
||||
"Invite by email": "Einladen per E-Mail",
|
||||
"Invite members": "Mitglieder einladen",
|
||||
"Invite new members": "Neue Mitglieder einladen",
|
||||
"Invite People": "Personen einladen",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Eingeladene Mitglieder, die ihre Einladung noch nicht angenommen haben, werden hier angezeigt.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Eingeladene Mitglieder erhalten Zugriff auf die Bereiche, auf die die Gruppen zugreifen können",
|
||||
"Join the workspace": "Dem Arbeitsbereich beitreten",
|
||||
@@ -114,15 +119,16 @@
|
||||
"New page": "Neue Seite",
|
||||
"New password": "Neues Passwort",
|
||||
"No group found": "Keine Gruppe gefunden",
|
||||
"No page history saved yet.": "Es wurde noch keine Seitengeschichte gespeichert.",
|
||||
"No page history saved yet.": "Es wurde noch kein Seitenverlauf gespeichert.",
|
||||
"No pages yet": "Noch keine Seiten",
|
||||
"No shared pages": "Keine freigegebenen Seiten",
|
||||
"No results found...": "Keine Ergebnisse gefunden...",
|
||||
"No user found": "Kein Benutzer gefunden",
|
||||
"Overview": "Überblick",
|
||||
"Owner": "Besitzer",
|
||||
"page": "Seite",
|
||||
"Page deleted successfully": "Seite erfolgreich gelöscht",
|
||||
"Page history": "Seitengeschichte",
|
||||
"Page history": "Seitenverlauf",
|
||||
"Select version": "Version auswählen",
|
||||
"Highlight changes": "Änderungen hervorheben",
|
||||
"Page import is in progress. Please do not close this tab.": "Der Seitenimport läuft. Bitte schließen Sie diesen Tab nicht.",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "Seiten",
|
||||
"Password": "Passwort",
|
||||
"Password changed successfully": "Passwort erfolgreich geändert",
|
||||
"People": "Personen",
|
||||
"Pending": "Ausstehend",
|
||||
"Please confirm your action": "Bitte bestätigen Sie Ihre Aktion",
|
||||
"Preferences": "Vorlieben",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Profil",
|
||||
"Recently updated": "Kürzlich aktualisiert",
|
||||
"Remove": "Entfernen",
|
||||
"Remove from favorites": "Aus Favoriten entfernen",
|
||||
"Remove group member": "Gruppenmitglied entfernen",
|
||||
"Remove space member": "Bereichsmitglied entfernen",
|
||||
"Restore": "Wiederherstellen",
|
||||
@@ -149,12 +157,12 @@
|
||||
"Search...": "Suche...",
|
||||
"Select language": "Sprache auswählen",
|
||||
"Select role": "Rolle auswählen",
|
||||
"Select role to assign to all invited members": "Rolle für alle eingeladenen Mitglieder auswählen",
|
||||
"Select role to assign to all invited members": "Wählen Sie die Rolle aus, die allen eingeladenen Mitgliedern zugewiesen werden soll",
|
||||
"Select theme": "Design auswählen",
|
||||
"Send invitation": "Einladung senden",
|
||||
"Invitation sent": "Einladung gesendet",
|
||||
"Settings": "Einstellungen",
|
||||
"Setup workspace": "Arbeitsbereich einrichten",
|
||||
"Setup workspace": "Workspace einrichten",
|
||||
"Sign In": "Anmelden",
|
||||
"Sign Up": "Registrieren",
|
||||
"Slug": "Slug",
|
||||
@@ -163,16 +171,17 @@
|
||||
"Space menu": "Bereichsmenü",
|
||||
"Space name": "Bereichsname",
|
||||
"Space settings": "Bereichseinstellungen",
|
||||
"Space slug": "Slug des Bereichs",
|
||||
"Space slug": "Bereichs-Slug",
|
||||
"Spaces": "Bereiche",
|
||||
"Spaces you belong to": "Bereiche, denen Sie angehören",
|
||||
"No space found": "Keine Bereiche gefunden",
|
||||
"Spaces you belong to": "Bereiche, zu denen Sie gehören",
|
||||
"No space found": "Kein Bereich gefunden",
|
||||
"Search for spaces": "Nach Bereichen suchen",
|
||||
"Start typing to search...": "Anfangen zu tippen, um zu suchen...",
|
||||
"Status": "Status",
|
||||
"Successfully imported": "Erfolgreich importiert",
|
||||
"Successfully restored": "Erfolgreich wiederhergestellt",
|
||||
"System settings": "Systemeinstellungen",
|
||||
"Templates": "Vorlagen",
|
||||
"Theme": "Design",
|
||||
"To change your email, you have to enter your password and new email.": "Um Ihre E-Mail-Adresse zu ändern, müssen Sie Ihr Passwort und Ihre neue E-Mail-Adresse eingeben.",
|
||||
"Toggle full page width": "Volle Seitenbreite umschalten",
|
||||
@@ -181,9 +190,9 @@
|
||||
"Untitled": "Ohne Titel",
|
||||
"Updated successfully": "Erfolgreich aktualisiert",
|
||||
"User": "Benutzer",
|
||||
"Workspace": "Arbeitsbereich",
|
||||
"Workspace Name": "Arbeitsbereichsname",
|
||||
"Workspace settings": "Arbeitsbereich-Einstellungen",
|
||||
"Workspace": "Workspace",
|
||||
"Workspace Name": "Workspace-Name",
|
||||
"Workspace settings": "Workspace-Einstellungen",
|
||||
"You can change your password here.": "Hier können Sie Ihr Passwort ändern.",
|
||||
"Your Email": "Ihre E-Mail",
|
||||
"Your import is complete.": "Ihr Import ist abgeschlossen.",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Antworten...",
|
||||
"Error loading comments.": "Fehler beim Laden der Kommentare.",
|
||||
"No comments yet.": "Noch keine Kommentare.",
|
||||
"No open comments.": "Keine offenen Kommentare.",
|
||||
"No resolved comments.": "Keine gelösten Kommentare.",
|
||||
"Add a comment...": "Kommentar hinzufügen...",
|
||||
"Edit comment": "Kommentar bearbeiten",
|
||||
"Delete comment": "Kommentar löschen",
|
||||
"Are you sure you want to delete this comment?": "Sind Sie sicher, dass Sie diesen Kommentar löschen möchten?",
|
||||
"Delete chat": "Chat löschen",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Sind Sie sicher, dass Sie '{{title}}' löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"Comment created successfully": "Kommentar erfolgreich erstellt",
|
||||
"Error creating comment": "Fehler beim Erstellen des Kommentars",
|
||||
"Comment updated successfully": "Kommentar erfolgreich aktualisiert",
|
||||
@@ -218,17 +232,16 @@
|
||||
"Failed to delete comment": "Löschen des Kommentars fehlgeschlagen",
|
||||
"Comment resolved successfully": "Kommentar erfolgreich gelöst",
|
||||
"Comment re-opened successfully": "Kommentar erfolgreich wieder geöffnet",
|
||||
"Comment unresolved successfully": "Kommentar erfolgreich ungelöst",
|
||||
"Comment unresolved successfully": "Kommentar erfolgreich als ungelöst markiert",
|
||||
"Failed to resolve comment": "Lösen des Kommentars fehlgeschlagen",
|
||||
"Resolve comment": "Kommentar lösen",
|
||||
"Unresolve comment": "Kommentar nicht lösen",
|
||||
"Unresolve comment": "Kommentar als ungelöst markieren",
|
||||
"Resolve Comment Thread": "Kommentarthread lösen",
|
||||
"Unresolve Comment Thread": "Kommentarthread nicht lösen",
|
||||
"Unresolve Comment Thread": "Kommentarthread als ungelöst markieren",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Sind Sie sicher, dass Sie diesen Kommentarthread lösen möchten? Dies wird als abgeschlossen markiert.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Sind Sie sicher, dass Sie diesen Kommentarthread nicht lösen möchten?",
|
||||
"Resolved": "Gelöst",
|
||||
"No active comments.": "Keine aktiven Kommentare.",
|
||||
"No resolved comments.": "Keine gelösten Kommentare.",
|
||||
"Revoke invitation": "Einladung widerrufen",
|
||||
"Revoke": "Widerrufen",
|
||||
"Don't": "Nicht",
|
||||
@@ -237,7 +250,7 @@
|
||||
"Anyone with this link can join this workspace.": "Jeder mit diesem Link kann dem Arbeitsbereich beitreten.",
|
||||
"Invite link": "Einladungslink",
|
||||
"Copy": "Kopieren",
|
||||
"Copy to space": "In Raum kopieren",
|
||||
"Copy to space": "In Bereich kopieren",
|
||||
"Copied": "Kopiert",
|
||||
"Duplicate": "Duplizieren",
|
||||
"Select a user": "Benutzer auswählen",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "Sind Sie sicher, dass Sie diesen Bereich löschen möchten?",
|
||||
"Delete this space with all its pages and data.": "Diesen Bereich mit allen Seiten und Daten löschen.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "Alle Seiten, Kommentare, Anhänge und Berechtigungen in diesem Bereich werden unwiderruflich gelöscht.",
|
||||
"Confirm space name": "Bestätigen Sie den Namen des Arbeitsbereichs",
|
||||
"Confirm space name": "Bereichsnamen bestätigen",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Geben Sie den Namen des Bereichs <b>{{spaceName}}</b> ein, um Ihre Aktion zu bestätigen.",
|
||||
"Format": "Format",
|
||||
"Include subpages": "Unterseiten einbeziehen",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Zeile unten hinzufügen",
|
||||
"Delete table": "Tabelle löschen",
|
||||
"Info": "Info",
|
||||
"Note": "Hinweis",
|
||||
"Success": "Erfolg",
|
||||
"Warning": "Warnung",
|
||||
"Danger": "Gefahr",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Speichern & Beenden",
|
||||
"Double-click to edit Excalidraw diagram": "Zum Bearbeiten des Excalidraw-Diagramms doppelklicken",
|
||||
"Paste link": "Link einfügen",
|
||||
"Paste link or search pages": "Link einfügen oder Seiten durchsuchen",
|
||||
"Link to web page": "Link zur Webseite",
|
||||
"Recents": "Zuletzt verwendet",
|
||||
"Page or URL": "Seite oder URL",
|
||||
"Link title": "Linktitel",
|
||||
"Edit link": "Link bearbeiten",
|
||||
"Remove link": "Link entfernen",
|
||||
"Add link": "Link hinzufügen",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Horizontale Trennlinie einfügen",
|
||||
"Upload any image from your device.": "Laden Sie ein beliebiges Bild von Ihrem Gerät hoch.",
|
||||
"Upload any video from your device.": "Laden Sie ein beliebiges Video von Ihrem Gerät hoch.",
|
||||
"Upload any audio from your device.": "Laden Sie beliebige Audiodateien von Ihrem Gerät hoch.",
|
||||
"Upload any file from your device.": "Laden Sie eine beliebige Datei von Ihrem Gerät hoch.",
|
||||
"Uploading {{name}}": "Lade {{name}} hoch",
|
||||
"Uploading file": "Datei wird hochgeladen",
|
||||
@@ -341,29 +361,49 @@
|
||||
"Divider": "Trennlinie",
|
||||
"Quote": "Zitat",
|
||||
"Image": "Bild",
|
||||
"Audio": "Audio",
|
||||
"Embed PDF": "PDF einbetten",
|
||||
"Upload and embed a PDF file.": "Laden Sie eine PDF-Datei hoch und betten Sie sie ein.",
|
||||
"Embed as PDF": "Als PDF einbetten",
|
||||
"Failed to load PDF": "Fehler beim Laden der PDF",
|
||||
"Convert to attachment": "In Anhang umwandeln",
|
||||
"File attachment": "Dateianhang",
|
||||
"Toggle block": "Block umschalten",
|
||||
"Callout": "Hinweisbox",
|
||||
"Toggle block": "Umschaltblock",
|
||||
"Callout": "Hinweisblock",
|
||||
"Insert callout notice.": "Hinweisbox einfügen.",
|
||||
"Math inline": "Mathe inline",
|
||||
"Insert inline math equation.": "Mathe-Gleichung inline einfügen.",
|
||||
"Math block": "Matheblock",
|
||||
"Insert math equation": "Mathe-Gleichung einfügen",
|
||||
"Insert math equation": "Mathematische Gleichung einfügen",
|
||||
"Mermaid diagram": "Mermaid-Diagramm",
|
||||
"Insert mermaid diagram": "Mermaid-Diagramm einfügen",
|
||||
"Insert and design Drawio diagrams": "Drawio-Diagramme einfügen und gestalten",
|
||||
"Insert current date": "Aktuelles Datum einfügen",
|
||||
"Draw and sketch excalidraw diagrams": "Excalidraw-Diagramme zeichnen und skizzieren",
|
||||
"Multiple": "Mehrere",
|
||||
"Multiple": "Mehrfach",
|
||||
"Turn into": "In verwandeln",
|
||||
"Text align": "Text ausrichten",
|
||||
"This page may have been deleted, moved, or you may not have access.": "\"Diese Seite wurde möglicherweise gelöscht, verschoben oder Sie haben keinen Zugriff darauf.\"",
|
||||
"Go to homepage": "Zur Startseite",
|
||||
"Pages you create will show up here.": "\"Die von Ihnen erstellten Seiten werden hier angezeigt.\"",
|
||||
"Heading {{level}}": "Überschrift {{level}}",
|
||||
"Toggle title": "Titel umschalten",
|
||||
"Write anything. Enter \"/\" for commands": "Schreiben Sie irgendetwas. Geben Sie \"/\" für Befehle ein",
|
||||
"Write anything. Enter \"/\" for commands": "Schreiben Sie etwas. Geben Sie \"/\" für Befehle ein",
|
||||
"Write...": "\"Schreiben...\"",
|
||||
"Column count": "Spaltenanzahl",
|
||||
"{{count}} Columns": "{count, plural, one {# Spalte} other {# Spalten}}",
|
||||
"Equal columns": "Gleich breite Spalten",
|
||||
"Left sidebar": "Linke Seitenleiste",
|
||||
"Right sidebar": "Rechte Seitenleiste",
|
||||
"Wide center": "Breiter Mittelbereich",
|
||||
"Left wide": "Breiter linker Bereich",
|
||||
"Right wide": "Breiter rechter Bereich",
|
||||
"Names do not match": "Namen stimmen nicht überein",
|
||||
"Today, {{time}}": "Heute, {{time}}",
|
||||
"Yesterday, {{time}}": "Gestern, {{time}}",
|
||||
"Space created successfully": "Der Bereich wurde erfolgreich erstellt",
|
||||
"Space updated successfully": "Der Bereich wurde erfolgreich aktualisiert",
|
||||
"Space deleted successfully": "Der Bereich wurde erfolgreich gelöscht",
|
||||
"Space created successfully": "Bereich erfolgreich erstellt",
|
||||
"Space updated successfully": "Bereich erfolgreich aktualisiert",
|
||||
"Space deleted successfully": "Bereich erfolgreich gelöscht",
|
||||
"Members added successfully": "Mitglieder erfolgreich hinzugefügt",
|
||||
"Member removed successfully": "Mitglied erfolgreich entfernt",
|
||||
"Member role updated successfully": "Mitgliederrolle erfolgreich aktualisiert",
|
||||
@@ -371,15 +411,22 @@
|
||||
"Created at: {{time}}": "Erstellt am: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Bearbeitet von {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Wortanzahl: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Zeichenzahl: {{characterCount}}",
|
||||
"Character count: {{characterCount}}": "Zeichenanzahl: {{characterCount}}",
|
||||
"New update": "Neues Update",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} ist verfügbar",
|
||||
"Default page edit mode": "Standard-Seitenbearbeitungsmodus",
|
||||
"Default page edit mode": "Standard-Bearbeitungsmodus für Seiten",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Wählen Sie Ihren bevorzugten Seitenbearbeitungsmodus. Vermeiden Sie versehentliche Bearbeitungen.",
|
||||
"Reading": "Lesen",
|
||||
"Delete member": "Mitglied löschen",
|
||||
"Member deleted successfully": "Mitglied erfolgreich gelöscht",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Sind Sie sicher, dass Sie dieses Arbeitsbereichsmitglied löschen möchten? Diese Aktion ist unwiderruflich.",
|
||||
"Deactivate member": "Mitglied deaktivieren",
|
||||
"Activate member": "Mitglied aktivieren",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Sind Sie sicher, dass Sie dieses Mitglied des Arbeitsbereichs deaktivieren möchten? Dieses Mitglied kann danach nicht mehr auf diesen Arbeitsbereich zugreifen.",
|
||||
"Are you sure you want to activate this workspace member?": "Sind Sie sicher, dass Sie dieses Mitglied des Arbeitsbereichs aktivieren möchten?",
|
||||
"Deactivate": "Deaktivieren",
|
||||
"Activate": "Aktivieren",
|
||||
"Deactivated": "Deaktiviert",
|
||||
"Move": "Verschieben",
|
||||
"Move page": "Seite verschieben",
|
||||
"Move page to a different space.": "Seite in einen anderen Bereich verschieben.",
|
||||
@@ -387,109 +434,129 @@
|
||||
"Table of contents": "Inhaltsverzeichnis",
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Fügen Sie Überschriften (H1, H2, H3) hinzu, um ein Inhaltsverzeichnis zu erstellen.",
|
||||
"Share": "Teilen",
|
||||
"Public sharing": "Öffentliches Teilen",
|
||||
"Public sharing": "Öffentliche Freigabe",
|
||||
"Shared by": "Geteilt von",
|
||||
"Shared at": "Geteilt am",
|
||||
"Inherits public sharing from": "Erbt das öffentliche Teilen von",
|
||||
"Inherits public sharing from": "Übernimmt öffentliche Freigabe von",
|
||||
"Share to web": "Im Web teilen",
|
||||
"Shared to web": "Im Web geteilt",
|
||||
"Anyone with the link can view this page": "Jeder mit dem Link kann diese Seite ansehen",
|
||||
"Make this page publicly accessible": "Diese Seite öffentlich zugänglich machen",
|
||||
"Include sub-pages": "Unterseiten einbeziehen",
|
||||
"Make sub-pages public too": "Unterseiten auch öffentlich machen",
|
||||
"Allow search engines to index page": "Suchmaschinen erlauben, die Seite zu indexieren",
|
||||
"Include sub-pages": "Unterseiten einschließen",
|
||||
"Make sub-pages public too": "Unterseiten ebenfalls öffentlich machen",
|
||||
"Allow search engines to index page": "Suchmaschinen das Indexieren der Seite erlauben",
|
||||
"Open page": "Seite öffnen",
|
||||
"Page": "Seite",
|
||||
"Delete public share link": "Öffentlichen Freigabelink löschen",
|
||||
"Delete share": "Freigabe löschen",
|
||||
"Are you sure you want to delete this shared link?": "Möchten Sie diesen Freigabelink wirklich löschen?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Öffentlich geteilte Seiten aus Bereichen, in denen Sie Mitglied sind, erscheinen hier",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Öffentlich freigegebene Seiten aus Bereichen, in denen Sie Mitglied sind, werden hier angezeigt",
|
||||
"Share deleted successfully": "Freigabe erfolgreich gelöscht",
|
||||
"Share not found": "Freigabe nicht gefunden",
|
||||
"Failed to share page": "Fehler beim Teilen der Seite",
|
||||
"Failed to share page": "Seite konnte nicht geteilt werden",
|
||||
"Disable public sharing": "Öffentliches Teilen deaktivieren",
|
||||
"Prevent members from sharing pages publicly.": "Verhindern Sie, dass Mitglieder Seiten öffentlich teilen.",
|
||||
"Toggle public sharing": "Öffentliches Teilen umschalten",
|
||||
"Toggle space public sharing": "Öffentliches Teilen im Bereich umschalten",
|
||||
"Allow viewers to comment": "Zuschauern erlauben, Kommentare zu hinterlassen",
|
||||
"Allow viewers to add comments on pages in this space.": "Erlauben Sie Zuschauern, Kommentare auf Seiten in diesem Bereich hinzuzufügen.",
|
||||
"Toggle viewer comments": "Zuschauerkommentare umschalten",
|
||||
"Public sharing is disabled at the workspace level": "Öffentliches Teilen ist auf der Arbeitsbereichsebene deaktiviert",
|
||||
"Prevent pages in this space from being shared publicly.": "Verhindern Sie, dass Seiten in diesem Bereich öffentlich geteilt werden.",
|
||||
"Page permissions": "Seitenberechtigungen",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Steuern Sie, wer einzelne Seiten ansehen und bearbeiten kann. Verfügbar mit einer Enterprise-Lizenz.",
|
||||
"Enable public sharing": "Öffentliches Teilen aktivieren",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Sind Sie sicher, dass Sie das öffentliche Teilen aktivieren möchten? Mitglieder können Seiten öffentlich teilen.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Sind Sie sicher, dass Sie das öffentliche Teilen deaktivieren möchten? Alle bestehenden Freigabelinks in diesem Arbeitsbereich werden gelöscht.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Sind Sie sicher, dass Sie das öffentliche Teilen für diesen Bereich aktivieren möchten?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Sind Sie sicher, dass Sie das öffentliche Teilen deaktivieren möchten? Alle bestehenden Freigabelinks in diesem Bereich werden gelöscht.",
|
||||
"Public sharing is disabled": "Öffentliches Teilen ist deaktiviert",
|
||||
"Public sharing has been disabled at the workspace level.": "Das öffentliche Teilen wurde auf der Arbeitsbereichsebene deaktiviert.",
|
||||
"Public sharing has been disabled for this space.": "Das öffentliche Teilen wurde für diesen Bereich deaktiviert.",
|
||||
"Copy page": "Seite kopieren",
|
||||
"Copy page to a different space.": "Seite in einen anderen Bereich kopieren.",
|
||||
"Page copied successfully": "Seite erfolgreich kopiert",
|
||||
"Page duplicated successfully": "Seite erfolgreich dupliziert",
|
||||
"Find": "Finden",
|
||||
"Find": "Suchen",
|
||||
"Not found": "Nicht gefunden",
|
||||
"Previous Match (Shift+Enter)": "Vorheriger Treffer (Shift+Enter)",
|
||||
"Next match (Enter)": "Nächster Treffer (Enter)",
|
||||
"Previous Match (Shift+Enter)": "Vorheriger Treffer (Umschalt+Eingabe)",
|
||||
"Next match (Enter)": "Nächster Treffer (Eingabe)",
|
||||
"Match case (Alt+C)": "Groß-/Kleinschreibung beachten (Alt+C)",
|
||||
"Replace": "Ersetzen",
|
||||
"Close (Escape)": "Schließen (Escape)",
|
||||
"Replace (Enter)": "Ersetzen (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Alle ersetzen (Ctrl+Alt+Enter)",
|
||||
"Replace (Enter)": "Ersetzen (Eingabe)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Alle ersetzen (Strg+Alt+Eingabe)",
|
||||
"Replace all": "Alle ersetzen",
|
||||
"View all spaces": "Alle Räume anzeigen",
|
||||
"View all": "Alle anzeigen",
|
||||
"View all spaces": "Alle Bereiche anzeigen",
|
||||
"Error": "Fehler",
|
||||
"Failed to disable MFA": "Deaktivierung der MFA fehlgeschlagen",
|
||||
"Failed to disable MFA": "MFA konnte nicht deaktiviert werden",
|
||||
"Disable two-factor authentication": "Zwei-Faktor-Authentifizierung deaktivieren",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Die Deaktivierung der Zwei-Faktor-Authentifizierung macht Ihr Konto weniger sicher. Sie benötigen nur Ihr Passwort, um sich anzumelden.",
|
||||
"Please enter your password to disable two-factor authentication:": "Bitte geben Sie Ihr Passwort ein, um die Zwei-Faktor-Authentifizierung zu deaktivieren:",
|
||||
"Two-factor authentication has been enabled": "Zwei-Faktor-Authentifizierung wurde aktiviert",
|
||||
"Two-factor authentication has been disabled": "Zwei-Faktor-Authentifizierung wurde deaktiviert",
|
||||
"2-step verification": "2-Schritt-Verifizierung",
|
||||
"Two-factor authentication has been enabled": "Die Zwei-Faktor-Authentifizierung wurde aktiviert",
|
||||
"Two-factor authentication has been disabled": "Die Zwei-Faktor-Authentifizierung wurde deaktiviert",
|
||||
"2-step verification": "Bestätigung in zwei Schritten",
|
||||
"Protect your account with an additional verification layer when signing in.": "Schützen Sie Ihr Konto mit einer zusätzlichen Verifizierungsschicht beim Anmelden.",
|
||||
"Two-factor authentication is active on your account.": "Die Zwei-Faktor-Authentifizierung ist auf Ihrem Konto aktiv.",
|
||||
"Add 2FA method": "2FA-Methode hinzufügen",
|
||||
"Backup codes": "Sicherungscodes",
|
||||
"Backup codes": "Backup-Codes",
|
||||
"Disable": "Deaktivieren",
|
||||
"Invalid verification code": "Ungültiger Bestätigungscode",
|
||||
"New backup codes have been generated": "Neue Sicherungscodes wurden generiert",
|
||||
"Failed to regenerate backup codes": "Fehler beim Generieren neuer Sicherungscodes",
|
||||
"About backup codes": "Über Sicherungscodes",
|
||||
"New backup codes have been generated": "Neue Backup-Codes wurden erstellt",
|
||||
"Failed to regenerate backup codes": "Backup-Codes konnten nicht neu erstellt werden",
|
||||
"About backup codes": "Über Backup-Codes",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Sicherungscodes können verwendet werden, um auf Ihr Konto zuzugreifen, wenn Sie den Zugang zu Ihrer Authenticator-App verlieren. Jeder Code kann nur einmal verwendet werden.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Sie können jederzeit neue Sicherungscodes generieren. Dies wird alle vorhandenen Codes ungültig machen.",
|
||||
"Confirm password": "Passwort bestätigen",
|
||||
"Generate new backup codes": "Neue Sicherungscodes generieren",
|
||||
"Save your new backup codes": "Speichern Sie Ihre neuen Sicherungscodes",
|
||||
"Generate new backup codes": "Neue Backup-Codes erstellen",
|
||||
"Save your new backup codes": "Speichern Sie Ihre neuen Backup-Codes",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Speichern Sie diese Codes an einem sicheren Ort. Ihre alten Sicherungscodes sind nicht mehr gültig.",
|
||||
"Your new backup codes": "Ihre neuen Sicherungscodes",
|
||||
"I've saved my backup codes": "Ich habe meine Sicherungscodes gespeichert",
|
||||
"Failed to setup MFA": "Fehler beim Einrichten der MFA",
|
||||
"Setup & Verify": "Einrichten & Überprüfen",
|
||||
"Your new backup codes": "Ihre neuen Backup-Codes",
|
||||
"I've saved my backup codes": "Ich habe meine Backup-Codes gespeichert",
|
||||
"Failed to setup MFA": "MFA konnte nicht eingerichtet werden",
|
||||
"Setup & Verify": "Einrichten und bestätigen",
|
||||
"Add to authenticator": "Zum Authenticator hinzufügen",
|
||||
"1. Scan this QR code with your authenticator app": "1. Scannen Sie diesen QR-Code mit Ihrer Authenticator-App",
|
||||
"Can't scan the code?": "Code kann nicht gescannt werden?",
|
||||
"Enter this code manually in your authenticator app:": "Geben Sie diesen Code manuell in Ihrer Authenticator-App ein:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Geben Sie den 6-stelligen Code aus Ihrem Authenticator ein",
|
||||
"Verify and enable": "Überprüfen und aktivieren",
|
||||
"Verify and enable": "Bestätigen und aktivieren",
|
||||
"Failed to generate QR code. Please try again.": "Fehler beim Generieren des QR-Codes. Bitte versuchen Sie es erneut.",
|
||||
"Backup": "Sicherung",
|
||||
"Backup": "Backup",
|
||||
"Save codes": "Codes speichern",
|
||||
"Save your backup codes": "Speichern Sie Ihre Sicherungscodes",
|
||||
"Save your backup codes": "Speichern Sie Ihre Backup-Codes",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Diese Codes können verwendet werden, um auf Ihr Konto zuzugreifen, wenn Sie den Zugang zu Ihrer Authenticator-App verlieren. Jeder Code kann nur einmal verwendet werden.",
|
||||
"Print": "Drucken",
|
||||
"Two-factor authentication has been set up. Please log in again.": "Zwei-Faktor-Authentifizierung wurde eingerichtet. Bitte melden Sie sich erneut an.",
|
||||
"Two-Factor authentication required": "Zwei-Faktor-Authentifizierung erforderlich",
|
||||
"Your workspace requires two-factor authentication for all users": "Ihr Arbeitsbereich erfordert die Zwei-Faktor-Authentifizierung für alle Benutzer",
|
||||
"Your workspace requires two-factor authentication for all users": "Ihr Workspace erfordert Zwei-Faktor-Authentifizierung für alle Benutzer",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Um weiterhin auf Ihren Arbeitsbereich zuzugreifen, müssen Sie die Zwei-Faktor-Authentifizierung einrichten. Dies fügt Ihrem Konto eine zusätzliche Sicherheitsebene hinzu.",
|
||||
"Set up two-factor authentication": "Zwei-Faktor-Authentifizierung einrichten",
|
||||
"Cancel and logout": "Abbrechen und abmelden",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Ihr Arbeitsbereich erfordert eine Zwei-Faktor-Authentifizierung. Bitte richten Sie diese ein, um fortzufahren.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Dadurch wird Ihrem Konto eine zusätzliche Sicherheitsebene hinzugefügt, indem ein Bestätigungscode von Ihrer Authenticator-App verlangt wird.",
|
||||
"Password is required": "Passwort erforderlich",
|
||||
"Password must be at least 8 characters": "Passwort muss mindestens 8 Zeichen lang sein",
|
||||
"Password is required": "Passwort ist erforderlich",
|
||||
"Password must be at least 8 characters": "Das Passwort muss mindestens 8 Zeichen lang sein",
|
||||
"Please enter a 6-digit code": "Bitte geben Sie einen 6-stelligen Code ein",
|
||||
"Code must be exactly 6 digits": "Code muss genau 6-stellig sein",
|
||||
"Enter the 6-digit code found in your authenticator app": "Geben Sie den 6-stelligen Code ein, der in Ihrer Authenticator-App zu finden ist",
|
||||
"Code must be exactly 6 digits": "Der Code muss genau 6 Ziffern haben",
|
||||
"Enter the 6-digit code found in your authenticator app": "Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein",
|
||||
"Need help authenticating?": "Brauchen Sie Hilfe bei der Authentifizierung?",
|
||||
"MFA QR Code": "MFA QR-Code",
|
||||
"MFA QR Code": "MFA-QR-Code",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Konto erfolgreich erstellt. Bitte melden Sie sich an, um die Zwei-Faktor-Authentifizierung einzurichten.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "Passwort erfolgreich zurückgesetzt. Bitte melden Sie sich mit Ihrem neuen Passwort an und führen Sie die Zwei-Faktor-Authentifizierung durch.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Passwort erfolgreich zurückgesetzt. Bitte melden Sie sich mit Ihrem neuen Passwort an, um die Zwei-Faktor-Authentifizierung einzurichten.",
|
||||
"Password reset was successful. Please log in with your new password.": "Passwort erfolgreich zurückgesetzt. Bitte melden Sie sich mit Ihrem neuen Passwort an.",
|
||||
"Two-factor authentication": "Zwei-Faktor-Authentifizierung",
|
||||
"Use authenticator app instead": "Stattdessen Authenticator-App verwenden",
|
||||
"Verify backup code": "Sicherungscode überprüfen",
|
||||
"Use backup code": "Sicherungscode verwenden",
|
||||
"Enter one of your backup codes": "Geben Sie einen Ihrer Sicherungscodes ein",
|
||||
"Backup code": "Sicherungscode",
|
||||
"Verify backup code": "Backup-Code bestätigen",
|
||||
"Use backup code": "Backup-Code verwenden",
|
||||
"Enter one of your backup codes": "Geben Sie einen Ihrer Backup-Codes ein",
|
||||
"Backup code": "Backup-Code",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Geben Sie einen Ihrer Sicherungscodes ein. Jeder Sicherungscode kann nur einmal verwendet werden.",
|
||||
"Verify": "Überprüfen",
|
||||
"Verify": "Bestätigen",
|
||||
"Trash": "Papierkorb",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Seiten im Papierkorb werden nach 30 Tagen endgültig gelöscht.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "Seiten im Papierkorb werden nach {{count}} Tagen endgültig gelöscht.",
|
||||
"Deleted": "Gelöscht",
|
||||
"No pages in trash": "Keine Seiten im Papierkorb",
|
||||
"Permanently delete page?": "Seite endgültig löschen?",
|
||||
@@ -504,37 +571,37 @@
|
||||
"Deleted at": "Gelöscht am",
|
||||
"Preview": "Vorschau",
|
||||
"Subpages": "Unterseiten",
|
||||
"Failed to load subpages": "Fehler beim Laden von Unterseiten",
|
||||
"Failed to load subpages": "Unterseiten konnten nicht geladen werden",
|
||||
"No subpages": "Keine Unterseiten",
|
||||
"Subpages (Child pages)": "Unterseiten (Untergeordnete Seiten)",
|
||||
"Subpages (Child pages)": "Unterseiten (untergeordnete Seiten)",
|
||||
"List all subpages of the current page": "Alle Unterseiten der aktuellen Seite auflisten",
|
||||
"Attachments": "Anhänge",
|
||||
"All spaces": "Alle Bereiche",
|
||||
"Unknown": "Unbekannt",
|
||||
"Find a space": "Einen Bereich finden",
|
||||
"Search in all your spaces": "In all deinen Bereichen suchen",
|
||||
"Type": "Art",
|
||||
"Enterprise": "Unternehmen",
|
||||
"Search in all your spaces": "In all Ihren Bereichen suchen",
|
||||
"Type": "Typ",
|
||||
"Enterprise": "Enterprise",
|
||||
"Download attachment": "Anhang herunterladen",
|
||||
"Allowed email domains": "Erlaubte E-Mail-Domains",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Nur Benutzer mit E-Mail-Adressen aus diesen Domains können sich über SSO registrieren.",
|
||||
"Enter valid domain names separated by comma or space": "Geben Sie gültige Domainnamen ein, durch Kommas oder Leerzeichen getrennt",
|
||||
"Enforce two-factor authentication": "Erzwingen der Zwei-Faktor-Authentifizierung",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Nur Benutzer mit E-Mail-Adressen aus diesen Domains können sich per SSO registrieren.",
|
||||
"Enter valid domain names separated by comma or space": "Geben Sie gültige Domainnamen ein, getrennt durch Komma oder Leerzeichen",
|
||||
"Enforce two-factor authentication": "Zwei-Faktor-Authentifizierung erzwingen",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Sobald es erzwungen wird, müssen alle Mitglieder die Zwei-Faktor-Authentifizierung aktivieren, um auf den Arbeitsbereich zugreifen zu können.",
|
||||
"Toggle MFA enforcement": "Umschalten der MFA-Erzwingung",
|
||||
"Toggle MFA enforcement": "MFA-Erzwingung umschalten",
|
||||
"Display name": "Anzeigename",
|
||||
"Allow signup": "Registrierung erlauben",
|
||||
"Enabled": "Aktiviert",
|
||||
"Advanced Settings": "Erweiterte Einstellungen",
|
||||
"Enable TLS/SSL": "TLS/SSL aktivieren",
|
||||
"Use secure connection to LDAP server": "Sichere Verbindung zum LDAP-Server verwenden",
|
||||
"Group sync": "Gruppensynchronisation",
|
||||
"Group sync": "Gruppensynchronisierung",
|
||||
"No SSO providers found.": "Keine SSO-Anbieter gefunden.",
|
||||
"Delete SSO provider": "SSO-Anbieter löschen",
|
||||
"Are you sure you want to delete this SSO provider?": "Sind Sie sicher, dass Sie diesen SSO-Anbieter löschen möchten?",
|
||||
"Action": "Aktion",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}}-Konfiguration",
|
||||
"Icon": "Icon",
|
||||
"Icon": "Symbol",
|
||||
"Upload image": "Bild hochladen",
|
||||
"Remove image": "Bild entfernen",
|
||||
"Failed to remove image": "Fehler beim Entfernen des Bildes",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Diese Aktion kann nicht rückgängig gemacht werden. Alle Anwendungen, die diesen API-Schlüssel verwenden, werden nicht mehr funktionieren.",
|
||||
"Update API key": "API-Schlüssel aktualisieren",
|
||||
"Manage API keys for all users in the workspace": "Verwalten Sie API-Schlüssel für alle Benutzer im Arbeitsbereich",
|
||||
"Restrict API key creation to admins": "API-Schlüsselerstellung auf Administratoren beschränken",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Nur Administratoren und Eigentümer können neue API-Schlüssel erstellen. Bestehende Mitgliederschlüssel funktionieren weiterhin.",
|
||||
"Toggle restrict API keys to admins": "Beschränkung der API-Schlüssel auf Administratoren umschalten",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "Die Erstellung von API-Schlüsseln ist durch Ihren Workspace-Administrator auf Administratoren beschränkt.",
|
||||
"AI settings": "KI-Einstellungen",
|
||||
"AI search": "KI-Suche",
|
||||
"AI Answer": "KI-Antwort",
|
||||
"Ask AI": "KI fragen",
|
||||
"AI is thinking...": "Die KI überlegt...",
|
||||
"Thinking": "Denkt nach",
|
||||
"Ask a question...": "Fragen stellen...",
|
||||
"AI-powered search (Ask AI)": "KI-gestützte Suche (KI fragen)",
|
||||
"AI Answers": "KI-Antworten",
|
||||
"AI-powered search (AI Answers)": "KI-unterstützte Suche (KI-Antworten)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "Die KI-Suche verwendet Vektor-Einbettungen, um semantische Suchfunktionen in Ihrem Arbeitsbereich bereitzustellen.",
|
||||
"Toggle AI search": "KI-Suche umschalten",
|
||||
"Generative AI (Ask AI)": "Generative KI (KI fragen)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Aktivieren Sie die KI-unterstützte Inhaltserstellung im Editor. Ermöglicht Benutzern das Erzeugen, Verbessern, Übersetzen und Transformieren von Text.",
|
||||
"Toggle generative AI": "Generative KI umschalten",
|
||||
"Upgrade your plan": "Upgrade Ihres Plans",
|
||||
"Available with a paid license": "Verfügbar mit einer kostenpflichtigen Lizenz",
|
||||
"Upgrade your license tier.": "Stufen Sie Ihre Lizenz hoch.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "KI ist nur in der Docmost Enterprise-Edition verfügbar. Kontaktieren Sie sales@docmost.com.",
|
||||
"AI & MCP": "KI & MCP",
|
||||
"AI": "KI",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Model Context Protocol (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Aktivieren Sie den MCP-Server, damit KI-Assistenten und -Tools mit den Inhalten Ihres Arbeitsbereichs interagieren können.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP ist nur in der Docmost Enterprise-Edition verfügbar. Kontaktieren Sie sales@docmost.com.",
|
||||
"MCP Server URL": "MCP-Server-URL",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Verwenden Sie Ihren API-Schlüssel zur Authentifizierung. API-Schlüssel können in Ihren Kontoeinstellungen verwaltet werden.",
|
||||
"Supported tools": "Unterstützte Tools",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "In Ihrem Arbeitsbereich ist MCP aktiviert. Verwenden Sie Ihren API-Schlüssel, um KI-Assistenten anzubinden.",
|
||||
"MCP server URL:": "MCP-Server-URL:",
|
||||
"Learn more": "Mehr erfahren",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Verwalten Sie API-Schlüssel für alle Nutzer im Arbeitsbereich. Siehe die <anchor>API-Dokumentation</anchor> für Details zur Verwendung.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Siehe die <anchor>API-Dokumentation</anchor> für Details zur Verwendung.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Sehen Sie die <anchor>MCP-Dokumentation</anchor> ein.",
|
||||
"Sources": "Quellen",
|
||||
"Ask AI not available for attachments": "KI fragen nicht für Anhänge verfügbar",
|
||||
"AI Answers not available for attachments": "KI-Antworten sind für Anhänge nicht verfügbar",
|
||||
"No answer available": "Keine Antwort verfügbar",
|
||||
"Background color": "Hintergrundfarbe",
|
||||
"Highlight color": "Hervorhebungsfarbe",
|
||||
"Remove color": "Farbe entfernen"
|
||||
"Remove color": "Farbe entfernen",
|
||||
"Notifications": "Benachrichtigungen",
|
||||
"No notifications": "Keine Benachrichtigungen",
|
||||
"No unread notifications": "Keine ungelesenen Benachrichtigungen",
|
||||
"All notifications": "Alle Benachrichtigungen",
|
||||
"Unread only": "Nur ungelesen",
|
||||
"Mark all as read": "Alle als gelesen markieren",
|
||||
"Mark as read": "Als gelesen markieren",
|
||||
"More options": "Weitere Optionen",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> hat Sie in einem Kommentar erwähnt",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> hat einen Kommentar auf einer Seite hinterlassen",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> hat einen Kommentar gelöst",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> hat Sie auf einer Seite erwähnt",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> hat Ihnen Bearbeitungszugriff auf eine Seite gegeben",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> hat Ihnen Ansichtszugriff auf eine Seite gegeben",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> hat eine Seite aktualisiert",
|
||||
"Watch page": "Seite beobachten",
|
||||
"Stop watching": "Nicht mehr beobachten",
|
||||
"Watch space": "Bereich beobachten",
|
||||
"Stop watching space": "Bereich nicht mehr beobachten",
|
||||
"Email notifications": "E-Mail-Benachrichtigungen",
|
||||
"Page updates": "Seitenaktualisierungen",
|
||||
"Get notified when pages you watch are updated.": "Erhalten Sie eine Benachrichtigung, wenn Seiten, die Sie beobachten, aktualisiert werden.",
|
||||
"Page mentions": "Seiten-Erwähnungen",
|
||||
"Get notified when someone mentions you on a page.": "Erhalten Sie eine Benachrichtigung, wenn Sie jemand auf einer Seite erwähnt.",
|
||||
"Comment mentions": "Kommentar-Erwähnungen",
|
||||
"Get notified when someone mentions you in a comment.": "Erhalten Sie eine Benachrichtigung, wenn Sie jemand in einem Kommentar erwähnt.",
|
||||
"New comments": "Neue Kommentare",
|
||||
"Get notified about new comments on threads you participate in.": "Erhalten Sie eine Benachrichtigung über neue Kommentare in Threads, an denen Sie teilnehmen.",
|
||||
"Resolved comments": "Erledigte Kommentare",
|
||||
"Get notified when your comment is resolved.": "Erhalten Sie eine Benachrichtigung, wenn Ihr Kommentar erledigt wurde.",
|
||||
"You are now watching this page": "Sie beobachten diese Seite jetzt",
|
||||
"You are no longer watching this page": "Sie beobachten diese Seite nicht mehr",
|
||||
"You are now watching this space": "Sie beobachten diesen Bereich jetzt",
|
||||
"You are no longer watching this space": "Sie beobachten diesen Bereich nicht mehr",
|
||||
"Direct": "Direkt",
|
||||
"Updates": "Aktualisierungen",
|
||||
"Today": "Heute",
|
||||
"Yesterday": "Gestern",
|
||||
"This week": "Diese Woche",
|
||||
"Older": "Älter",
|
||||
"Restricted page": "Eingeschränkte Seite",
|
||||
"Restricted pages cannot be shared publicly.": "Eingeschränkte Seiten können nicht öffentlich geteilt werden.",
|
||||
"Restricted by parent": "Eingeschränkt durch die übergeordnete Seite",
|
||||
"Restricted": "Eingeschränkt",
|
||||
"Open": "Offen",
|
||||
"Inherits restrictions from ancestor page": "Erbt Einschränkungen von einer übergeordneten Seite",
|
||||
"Only people listed below can access this page": "Nur die unten aufgeführten Personen können auf diese Seite zugreifen.",
|
||||
"Everyone in this space can access": "Jeder in diesem Bereich kann darauf zugreifen.",
|
||||
"No additional restrictions on this page": "Keine zusätzlichen Einschränkungen auf dieser Seite",
|
||||
"Only specific people can access": "Nur bestimmte Personen können zugreifen",
|
||||
"Use only inherited restrictions": "Nur geerbte Einschränkungen verwenden",
|
||||
"Add restrictions on top of inherited": "Einschränkungen zusätzlich zu den geerbten hinzufügen",
|
||||
"Inherited restriction": "Geerbte Einschränkung",
|
||||
"Access limited by": "Zugriff beschränkt durch",
|
||||
"Restrict access to control who can view and edit this page": "Beschränken Sie den Zugriff, um festzulegen, wer diese Seite ansehen und bearbeiten kann.",
|
||||
"Add additional restrictions specific to this page": "Fügen Sie zusätzliche, für diese Seite spezifische Einschränkungen hinzu.",
|
||||
"Access": "Zugriff",
|
||||
"People with access": "Personen mit Zugriff",
|
||||
"Remove all": "Alle entfernen",
|
||||
"Remove access": "Zugriff entfernen",
|
||||
"Remove all access": "Alle Zugriffsrechte entfernen",
|
||||
"Are you sure you want to remove this member's access to the page?": "Sind Sie sicher, dass Sie den Zugriff dieses Mitglieds auf die Seite entfernen möchten?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Sind Sie sicher, dass Sie alle spezifischen Zugriffsrechte entfernen möchten? Dadurch wird die Seite für alle in diesem Bereich zugänglich.",
|
||||
"Trash retention": "Aufbewahrungsdauer des Papierkorbs",
|
||||
"Pages in trash will be permanently deleted after this period.": "Seiten im Papierkorb werden nach Ablauf dieses Zeitraums endgültig gelöscht.",
|
||||
"Trash retention updated": "Aufbewahrungsdauer des Papierkorbs aktualisiert",
|
||||
"Failed to update trash retention": "Aktualisierung der Aufbewahrungsdauer des Papierkorbs fehlgeschlagen",
|
||||
"Removed page restriction": "Seitenbeschränkung entfernt",
|
||||
"Added page permission": "Seitenberechtigung hinzugefügt",
|
||||
"Removed page permission": "Seitenberechtigung entfernt",
|
||||
"day": "Tag",
|
||||
"days": "Tage",
|
||||
"week": "Woche",
|
||||
"weeks": "Wochen",
|
||||
"month": "Monat",
|
||||
"months": "Monate",
|
||||
"year": "Jahr",
|
||||
"years": "Jahre",
|
||||
"Period": "Zeitraum",
|
||||
"Fixed date": "Festes Datum",
|
||||
"Indefinitely": "Unbegrenzt",
|
||||
"Days": "Tage",
|
||||
"Weeks": "Wochen",
|
||||
"Months": "Monate",
|
||||
"Years": "Jahre",
|
||||
"Pick a date": "Datum auswählen",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Das Maximum für diese Einheit beträgt {{max}} {{unit}}",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Läuft nie ab. Prüfer können die Seite jederzeit erneut verifizieren.",
|
||||
"Verified": "Verifiziert",
|
||||
"Review needed": "Prüfung erforderlich",
|
||||
"Verification expired": "Verifizierung abgelaufen",
|
||||
"Draft": "Entwurf",
|
||||
"In Approval": "In Genehmigung",
|
||||
"In approval": "In Genehmigung",
|
||||
"Approved": "Genehmigt",
|
||||
"Obsolete": "Veraltet",
|
||||
"Expiring": "Läuft bald ab",
|
||||
"Set up verification": "Verifizierung einrichten",
|
||||
"Verify page": "Seite verifizieren",
|
||||
"Page verification": "Seitenverifizierung",
|
||||
"Add verification": "Verifizierung hinzufügen",
|
||||
"Edit verification": "Verifizierung bearbeiten",
|
||||
"Search by title": "Nach Titel suchen",
|
||||
"Choose how this page should stay accurate.": "Wählen Sie aus, wie diese Seite aktuell gehalten werden soll.",
|
||||
"Recurring verification": "Wiederkehrende Verifizierung",
|
||||
"Verifiers re-confirm this page on a schedule.": "Prüfer bestätigen diese Seite nach einem Zeitplan erneut.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Nach einem Zeitplan erneut verifizieren (z. B. alle 30 Tage)",
|
||||
"Page stays editable at all times": "Die Seite bleibt jederzeit bearbeitbar",
|
||||
"Best for runbooks, FAQs, living documentation": "Am besten für Runbooks, FAQs und lebende Dokumentation geeignet",
|
||||
"Approval workflow": "Genehmigungsworkflow",
|
||||
"Formal document lifecycle with named approvers.": "Formaler Dokumentenlebenszyklus mit benannten Genehmigern.",
|
||||
"Draft → In approval → Approved → Obsolete": "Entwurf → In Genehmigung → Genehmigt → Veraltet",
|
||||
"Locked once approved, with full history": "Nach der Genehmigung gesperrt, mit vollständiger Historie",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Entwickelt für ISO 9001, ISO 13485 und FDA",
|
||||
"Best for SOPs and controlled documents": "Am besten für SOPs und kontrollierte Dokumente geeignet",
|
||||
"Back": "Zurück",
|
||||
"Quality management": "Qualitätsmanagement",
|
||||
"Recurring": "Wiederkehrend",
|
||||
"Pages move through draft, approval, and approved stages.": "Seiten durchlaufen die Phasen Entwurf, Genehmigung und Genehmigt.",
|
||||
"Verifiers": "Prüfer",
|
||||
"Add verifier": "Prüfer hinzufügen",
|
||||
"I've reviewed this page for accuracy": "Ich habe diese Seite auf Richtigkeit geprüft",
|
||||
"Set up": "Einrichten",
|
||||
"Remove verification": "Verifizierung entfernen",
|
||||
"Are you sure you want to remove verification from this page?": "Möchten Sie die Verifizierung wirklich von dieser Seite entfernen?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Zugewiesene Prüfer müssen diese Seite regelmäßig erneut verifizieren.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Zuletzt von {{name}} {{time}} verifiziert (abgelaufen)",
|
||||
"The fixed expiration date has passed.": "Das feste Ablaufdatum ist überschritten.",
|
||||
"Verified by {{name}} {{time}}": "Verifiziert von {{name}} {{time}}",
|
||||
"Expires {{date}}": "Läuft ab am {{date}}",
|
||||
"Expired {{date}}": "Abgelaufen am {{date}}",
|
||||
"Mark as obsolete": "Als veraltet markieren",
|
||||
"Mark obsolete": "Als veraltet markieren",
|
||||
"Returned by {{name}} {{time}}": "Zurückgegeben von {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Es wurde noch keine Genehmigung angefordert.",
|
||||
"Submitted by {{name}} {{time}}": "Eingereicht von {{name}} {{time}}",
|
||||
"Someone": "Jemand",
|
||||
"Approved by {{name}} {{time}}": "Genehmigt von {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Dieses Dokument wurde als veraltet markiert.",
|
||||
"Rejection comment": "Ablehnungskommentar",
|
||||
"Reason for returning this document...": "Grund für die Rückgabe dieses Dokuments...",
|
||||
"Confirm rejection": "Ablehnung bestätigen",
|
||||
"Submit for approval": "Zur Genehmigung einreichen",
|
||||
"Reject": "Ablehnen",
|
||||
"Approve": "Genehmigen",
|
||||
"Re-submit for approval": "Erneut zur Genehmigung einreichen",
|
||||
"Verified until": "Verifiziert bis",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "Verifizierte Seiten",
|
||||
"Search pages...": "Seiten suchen...",
|
||||
"Filter by space": "Nach Bereich filtern",
|
||||
"Filter by type": "Nach Typ filtern",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> hat eine Seite verifiziert",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> hat eine Seite zu Ihrer Genehmigung eingereicht",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> hat eine Seite zur Überarbeitung zurückgegeben",
|
||||
"Page verification expires soon": "Die Seitenverifizierung läuft bald ab",
|
||||
"Page verification has expired": "Die Seitenverifizierung ist abgelaufen",
|
||||
"Verifying your email": "Ihre E-Mail wird bestätigt",
|
||||
"Please wait...": "Bitte warten...",
|
||||
"Verification failed. The link may have expired.": "Überprüfung fehlgeschlagen. Der Link ist möglicherweise abgelaufen.",
|
||||
"Check your email": "Prüfen Sie Ihre E-Mails",
|
||||
"We sent a verification link to {{email}}.": "Wir haben einen Bestätigungslink an {{email}} gesendet.",
|
||||
"We sent a verification link to your email.": "Wir haben einen Bestätigungslink an Ihre E-Mail-Adresse gesendet.",
|
||||
"Click the link to verify your email and access your workspace.": "Klicken Sie auf den Link, um Ihre E-Mail zu bestätigen und auf Ihren Arbeitsbereich zuzugreifen.",
|
||||
"Resend verification email": "Bestätigungs-E-Mail erneut senden",
|
||||
"Verification email sent. Please check your inbox.": "Bestätigungs-E-Mail gesendet. Bitte überprüfen Sie Ihr Postfach.",
|
||||
"Failed to resend verification email. Please try again.": "Fehler beim erneuten Senden der Bestätigungs-E-Mail. Bitte versuchen Sie es erneut.",
|
||||
"We've sent you an email with your associated workspaces.": "Wir haben Ihnen eine E-Mail mit Ihren zugehörigen Arbeitsbereichen gesendet.",
|
||||
"Load more": "Mehr laden",
|
||||
"Log out of all devices": "Auf allen Geräten abmelden",
|
||||
"Log out of all sessions except this device": "Von allen Sitzungen außer diesem Gerät abmelden",
|
||||
"This Device": "Dieses Gerät",
|
||||
"Unknown device": "Unbekanntes Gerät",
|
||||
"No active sessions": "Keine aktiven Sitzungen",
|
||||
"Session revoked": "Sitzung widerrufen",
|
||||
"All other sessions revoked": "Alle anderen Sitzungen widerrufen",
|
||||
"Last used": "Zuletzt verwendet",
|
||||
"Created": "Erstellt",
|
||||
"Rename": "Umbenennen",
|
||||
"Publish": "Veröffentlichen",
|
||||
"Security": "Sicherheit",
|
||||
"Enforce SSO": "SSO erzwingen",
|
||||
"Once enforced, members will not be able to login with email and password.": "Sobald dies erzwungen wird, können sich Mitglieder nicht mehr mit E-Mail und Passwort anmelden.",
|
||||
"AI-generated content may not be accurate.": "KI-generierte Inhalte sind möglicherweise nicht korrekt.",
|
||||
"AI Chat": "KI-Chat",
|
||||
"Analyze for insights": "Für Erkenntnisse analysieren",
|
||||
"Ask anything...": "Fragen Sie irgendetwas...",
|
||||
"Chat history": "Chatverlauf",
|
||||
"Chat name": "Chatname",
|
||||
"Close": "Schließen",
|
||||
"Docmost AI": "Docmost KI",
|
||||
"Failed to load chat. An error occurred.": "Chat konnte nicht geladen werden. Ein Fehler ist aufgetreten.",
|
||||
"Failed to render this message.": "Diese Nachricht konnte nicht dargestellt werden.",
|
||||
"How can I help you today?": "Wie kann ich Ihnen heute helfen?",
|
||||
"New chat": "Neuer Chat",
|
||||
"No chat history": "Kein Chatverlauf",
|
||||
"No chats found": "Keine Chats gefunden",
|
||||
"No conversations yet": "Noch keine Unterhaltungen",
|
||||
"Open full page": "Ganze Seite öffnen",
|
||||
"Previous 7 days": "Letzte 7 Tage",
|
||||
"Previous 30 days": "Letzte 30 Tage",
|
||||
"Search chats...": "Chats durchsuchen...",
|
||||
"Start a new chat to see it here.": "Starten Sie einen neuen Chat, damit er hier angezeigt wird.",
|
||||
"Summarize this page": "Diese Seite zusammenfassen",
|
||||
"Toggle AI Chat": "KI-Chat umschalten",
|
||||
"Translate this page": "Diese Seite übersetzen",
|
||||
"Try a different search term.": "Versuchen Sie einen anderen Suchbegriff.",
|
||||
"Try again": "Erneut versuchen",
|
||||
"Untitled chat": "Chat ohne Titel",
|
||||
"What can I help you with?": "Womit kann ich Ihnen helfen?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Add members",
|
||||
"Add to groups": "Add to groups",
|
||||
"Add space members": "Add space members",
|
||||
"Add to favorites": "Add to favorites",
|
||||
"Admin": "Admin",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Are you sure you want to delete this group? Members will lose access to resources this group has access to.",
|
||||
"Are you sure you want to delete this page?": "Are you sure you want to delete this page?",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Failed to import pages",
|
||||
"Failed to load page. An error occurred.": "Failed to load page. An error occurred.",
|
||||
"Failed to update data": "Failed to update data",
|
||||
"Favorite spaces": "Favorite spaces",
|
||||
"Favorite spaces appear here": "Favorite spaces appear here",
|
||||
"Favorites": "Favorites",
|
||||
"Full access": "Full access",
|
||||
"Full page width": "Full page width",
|
||||
"Full width": "Full width",
|
||||
@@ -92,6 +96,7 @@
|
||||
"Invite by email": "Invite by email",
|
||||
"Invite members": "Invite members",
|
||||
"Invite new members": "Invite new members",
|
||||
"Invite People": "Invite People",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Invited members who are yet to accept their invitation will appear here.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Invited members will be granted access to spaces the groups can access",
|
||||
"Join the workspace": "Join the workspace",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "No group found",
|
||||
"No page history saved yet.": "No page history saved yet.",
|
||||
"No pages yet": "No pages yet",
|
||||
"No shared pages": "No shared pages",
|
||||
"No results found...": "No results found...",
|
||||
"No user found": "No user found",
|
||||
"Overview": "Overview",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "pages",
|
||||
"Password": "Password",
|
||||
"Password changed successfully": "Password changed successfully",
|
||||
"People": "People",
|
||||
"Pending": "Pending",
|
||||
"Please confirm your action": "Please confirm your action",
|
||||
"Preferences": "Preferences",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Profile",
|
||||
"Recently updated": "Recently updated",
|
||||
"Remove": "Remove",
|
||||
"Remove from favorites": "Remove from favorites",
|
||||
"Remove group member": "Remove group member",
|
||||
"Remove space member": "Remove space member",
|
||||
"Restore": "Restore",
|
||||
@@ -173,6 +181,7 @@
|
||||
"Successfully imported": "Successfully imported",
|
||||
"Successfully restored": "Successfully restored",
|
||||
"System settings": "System settings",
|
||||
"Templates": "Templates",
|
||||
"Theme": "Theme",
|
||||
"To change your email, you have to enter your password and new email.": "To change your email, you have to enter your password and new email.",
|
||||
"Toggle full page width": "Toggle full page width",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Reply...",
|
||||
"Error loading comments.": "Error loading comments.",
|
||||
"No comments yet.": "No comments yet.",
|
||||
"No open comments.": "No open comments.",
|
||||
"No resolved comments.": "No resolved comments.",
|
||||
"Add a comment...": "Add a comment...",
|
||||
"Edit comment": "Edit comment",
|
||||
"Delete comment": "Delete comment",
|
||||
"Are you sure you want to delete this comment?": "Are you sure you want to delete this comment?",
|
||||
"Delete chat": "Delete chat",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Are you sure you want to delete '{{title}}'? This action cannot be undone.",
|
||||
"Comment created successfully": "Comment created successfully",
|
||||
"Error creating comment": "Error creating comment",
|
||||
"Comment updated successfully": "Comment updated successfully",
|
||||
@@ -228,7 +242,6 @@
|
||||
"Are you sure you want to unresolve this comment thread?": "Are you sure you want to unresolve this comment thread?",
|
||||
"Resolved": "Resolved",
|
||||
"No active comments.": "No active comments.",
|
||||
"No resolved comments.": "No resolved comments.",
|
||||
"Revoke invitation": "Revoke invitation",
|
||||
"Revoke": "Revoke",
|
||||
"Don't": "Don't",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Add row below",
|
||||
"Delete table": "Delete table",
|
||||
"Info": "Info",
|
||||
"Note": "Note",
|
||||
"Success": "Success",
|
||||
"Warning": "Warning",
|
||||
"Danger": "Danger",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Save & Exit",
|
||||
"Double-click to edit Excalidraw diagram": "Double-click to edit Excalidraw diagram",
|
||||
"Paste link": "Paste link",
|
||||
"Paste link or search pages": "Paste link or search pages",
|
||||
"Link to web page": "Link to web page",
|
||||
"Recents": "Recents",
|
||||
"Page or URL": "Page or URL",
|
||||
"Link title": "Link title",
|
||||
"Edit link": "Edit link",
|
||||
"Remove link": "Remove link",
|
||||
"Add link": "Add link",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Insert horizontal rule divider",
|
||||
"Upload any image from your device.": "Upload any image from your device.",
|
||||
"Upload any video from your device.": "Upload any video from your device.",
|
||||
"Upload any audio from your device.": "Upload any audio from your device.",
|
||||
"Upload any file from your device.": "Upload any file from your device.",
|
||||
"Uploading {{name}}": "Uploading {{name}}",
|
||||
"Uploading file": "Uploading file",
|
||||
@@ -341,6 +361,12 @@
|
||||
"Divider": "Divider",
|
||||
"Quote": "Quote",
|
||||
"Image": "Image",
|
||||
"Audio": "Audio",
|
||||
"Embed PDF": "Embed PDF",
|
||||
"Upload and embed a PDF file.": "Upload and embed a PDF file.",
|
||||
"Embed as PDF": "Embed as PDF",
|
||||
"Failed to load PDF": "Failed to load PDF",
|
||||
"Convert to attachment": "Convert to attachment",
|
||||
"File attachment": "File attachment",
|
||||
"Toggle block": "Toggle block",
|
||||
"Callout": "Callout",
|
||||
@@ -355,9 +381,23 @@
|
||||
"Insert current date": "Insert current date",
|
||||
"Draw and sketch excalidraw diagrams": "Draw and sketch excalidraw diagrams",
|
||||
"Multiple": "Multiple",
|
||||
"Turn into": "Turn into",
|
||||
"Text align": "Text align",
|
||||
"This page may have been deleted, moved, or you may not have access.": "This page may have been deleted, moved, or you may not have access.",
|
||||
"Go to homepage": "Go to homepage",
|
||||
"Pages you create will show up here.": "Pages you create will show up here.",
|
||||
"Heading {{level}}": "Heading {{level}}",
|
||||
"Toggle title": "Toggle title",
|
||||
"Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands",
|
||||
"Write...": "Write...",
|
||||
"Column count": "Column count",
|
||||
"{{count}} Columns": "{{count}} Columns",
|
||||
"Equal columns": "Equal columns",
|
||||
"Left sidebar": "Left sidebar",
|
||||
"Right sidebar": "Right sidebar",
|
||||
"Wide center": "Wide center",
|
||||
"Left wide": "Left wide",
|
||||
"Right wide": "Right wide",
|
||||
"Names do not match": "Names do not match",
|
||||
"Today, {{time}}": "Today, {{time}}",
|
||||
"Yesterday, {{time}}": "Yesterday, {{time}}",
|
||||
@@ -380,6 +420,13 @@
|
||||
"Delete member": "Delete member",
|
||||
"Member deleted successfully": "Member deleted successfully",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Are you sure you want to delete this workspace member? This action is irreversible.",
|
||||
"Deactivate member": "Deactivate member",
|
||||
"Activate member": "Activate member",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.",
|
||||
"Are you sure you want to activate this workspace member?": "Are you sure you want to activate this workspace member?",
|
||||
"Deactivate": "Deactivate",
|
||||
"Activate": "Activate",
|
||||
"Deactivated": "Deactivated",
|
||||
"Move": "Move",
|
||||
"Move page": "Move page",
|
||||
"Move page to a different space.": "Move page to a different space.",
|
||||
@@ -407,6 +454,25 @@
|
||||
"Share deleted successfully": "Share deleted successfully",
|
||||
"Share not found": "Share not found",
|
||||
"Failed to share page": "Failed to share page",
|
||||
"Disable public sharing": "Disable public sharing",
|
||||
"Prevent members from sharing pages publicly.": "Prevent members from sharing pages publicly.",
|
||||
"Toggle public sharing": "Toggle public sharing",
|
||||
"Toggle space public sharing": "Toggle space public sharing",
|
||||
"Allow viewers to comment": "Allow viewers to comment",
|
||||
"Allow viewers to add comments on pages in this space.": "Allow viewers to add comments on pages in this space.",
|
||||
"Toggle viewer comments": "Toggle viewer comments",
|
||||
"Public sharing is disabled at the workspace level": "Public sharing is disabled at the workspace level",
|
||||
"Prevent pages in this space from being shared publicly.": "Prevent pages in this space from being shared publicly.",
|
||||
"Page permissions": "Page permissions",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Control who can view and edit individual pages. Available with an enterprise license.",
|
||||
"Enable public sharing": "Enable public sharing",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Are you sure you want to enable public sharing? Members will be able to share pages publicly.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Are you sure you want to enable public sharing for this space?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.",
|
||||
"Public sharing is disabled": "Public sharing is disabled",
|
||||
"Public sharing has been disabled at the workspace level.": "Public sharing has been disabled at the workspace level.",
|
||||
"Public sharing has been disabled for this space.": "Public sharing has been disabled for this space.",
|
||||
"Copy page": "Copy page",
|
||||
"Copy page to a different space.": "Copy page to a different space.",
|
||||
"Page copied successfully": "Page copied successfully",
|
||||
@@ -421,6 +487,7 @@
|
||||
"Replace (Enter)": "Replace (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Replace all (Ctrl+Alt+Enter)",
|
||||
"Replace all": "Replace all",
|
||||
"View all": "View all",
|
||||
"View all spaces": "View all spaces",
|
||||
"Error": "Error",
|
||||
"Failed to disable MFA": "Failed to disable MFA",
|
||||
@@ -489,7 +556,7 @@
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Enter one of your backup codes. Each backup code can only be used once.",
|
||||
"Verify": "Verify",
|
||||
"Trash": "Trash",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Pages in trash will be permanently deleted after 30 days.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "Pages in trash will be permanently deleted after {{count}} days.",
|
||||
"Deleted": "Deleted",
|
||||
"No pages in trash": "No pages in trash",
|
||||
"Permanently delete page?": "Permanently delete page?",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "This action cannot be undone. Any applications using this API key will stop working.",
|
||||
"Update API key": "Update API key",
|
||||
"Manage API keys for all users in the workspace": "Manage API keys for all users in the workspace",
|
||||
"Restrict API key creation to admins": "Restrict API key creation to admins",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Only admins and owners can create new API keys. Existing member keys will continue to work.",
|
||||
"Toggle restrict API keys to admins": "Toggle restrict API keys to admins",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "API key creation is restricted to admins by your workspace administrator.",
|
||||
"AI settings": "AI settings",
|
||||
"AI search": "AI search",
|
||||
"AI Answer": "AI Answer",
|
||||
"Ask AI": "Ask AI",
|
||||
"AI is thinking...": "AI is thinking...",
|
||||
"Thinking": "Thinking",
|
||||
"Ask a question...": "Ask a question...",
|
||||
"AI-powered search (Ask AI)": "AI-powered search (Ask AI)",
|
||||
"AI Answers": "AI Answers",
|
||||
"AI-powered search (AI Answers)": "AI-powered search (AI Answers)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI search uses vector embeddings to provide semantic search capabilities across your workspace content.",
|
||||
"Toggle AI search": "Toggle AI search",
|
||||
"Generative AI (Ask AI)": "Generative AI (Ask AI)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.",
|
||||
"Toggle generative AI": "Toggle generative AI",
|
||||
"Upgrade your plan": "Upgrade your plan",
|
||||
"Available with a paid license": "Available with a paid license",
|
||||
"Upgrade your license tier.": "Upgrade your license tier.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.",
|
||||
"AI & MCP": "AI & MCP",
|
||||
"AI": "AI",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Model Context Protocol (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.",
|
||||
"MCP Server URL": "MCP Server URL",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Use your API key for authentication. You can manage API keys in your account settings.",
|
||||
"Supported tools": "Supported tools",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Your workspace has MCP enabled. Use your API key to connect AI assistants.",
|
||||
"MCP server URL:": "MCP server URL:",
|
||||
"Learn more": "Learn more",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "View the <anchor>API documentation</anchor> for usage details.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "View the <anchor>MCP documentation</anchor>.",
|
||||
"Sources": "Sources",
|
||||
"Ask AI not available for attachments": "Ask AI not available for attachments",
|
||||
"AI Answers not available for attachments": "AI Answers not available for attachments",
|
||||
"No answer available": "No answer available",
|
||||
"Background color": "Background color",
|
||||
"Highlight color": "Highlight color",
|
||||
"Remove color": "Remove color"
|
||||
"Remove color": "Remove color",
|
||||
"Notifications": "Notifications",
|
||||
"No notifications": "No notifications",
|
||||
"No unread notifications": "No unread notifications",
|
||||
"All notifications": "All notifications",
|
||||
"Unread only": "Unread only",
|
||||
"Mark all as read": "Mark all as read",
|
||||
"Mark as read": "Mark as read",
|
||||
"More options": "More options",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> mentioned you in a comment",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> commented on a page",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolved a comment",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> mentioned you on a page",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> gave you edit access to a page",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> gave you view access to a page",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> updated a page",
|
||||
"Watch page": "Watch page",
|
||||
"Stop watching": "Stop watching",
|
||||
"Watch space": "Watch space",
|
||||
"Stop watching space": "Stop watching space",
|
||||
"Email notifications": "Email notifications",
|
||||
"Page updates": "Page updates",
|
||||
"Get notified when pages you watch are updated.": "Receive notifications when the pages you watch are updated.",
|
||||
"Page mentions": "Page mentions",
|
||||
"Get notified when someone mentions you on a page.": "Receive notifications when someone mentions you on a page.",
|
||||
"Comment mentions": "Comment mentions",
|
||||
"Get notified when someone mentions you in a comment.": "Receive notifications when someone mentions you in a comment.",
|
||||
"New comments": "New comments",
|
||||
"Get notified about new comments on threads you participate in.": "Receive notifications about new comments in threads you are participating in.",
|
||||
"Resolved comments": "Resolved comments",
|
||||
"Get notified when your comment is resolved.": "Receive a notification when your comment is resolved.",
|
||||
"You are now watching this page": "You’re now watching this page",
|
||||
"You are no longer watching this page": "You’re no longer watching this page",
|
||||
"You are now watching this space": "You’re now watching this space",
|
||||
"You are no longer watching this space": "You’re no longer watching this space",
|
||||
"Direct": "Direct",
|
||||
"Updates": "Updates",
|
||||
"Today": "Today",
|
||||
"Yesterday": "Yesterday",
|
||||
"This week": "This week",
|
||||
"Older": "Older",
|
||||
"Restricted page": "Restricted page",
|
||||
"Restricted pages cannot be shared publicly.": "Restricted pages cannot be shared publicly.",
|
||||
"Restricted by parent": "Restricted by parent",
|
||||
"Restricted": "Restricted",
|
||||
"Open": "Open",
|
||||
"Inherits restrictions from ancestor page": "Inherits restrictions from ancestor page",
|
||||
"Only people listed below can access this page": "Only people listed below can access this page",
|
||||
"Everyone in this space can access": "Everyone in this space can access",
|
||||
"No additional restrictions on this page": "No additional restrictions on this page",
|
||||
"Only specific people can access": "Only specific people can access",
|
||||
"Use only inherited restrictions": "Use only inherited restrictions",
|
||||
"Add restrictions on top of inherited": "Add restrictions on top of inherited",
|
||||
"Inherited restriction": "Inherited restriction",
|
||||
"Access limited by": "Access limited by",
|
||||
"Restrict access to control who can view and edit this page": "Restrict access to control who can view and edit this page",
|
||||
"Add additional restrictions specific to this page": "Add additional restrictions specific to this page",
|
||||
"Access": "Access",
|
||||
"People with access": "People with access",
|
||||
"Remove all": "Remove all",
|
||||
"Remove access": "Remove access",
|
||||
"Remove all access": "Remove all access",
|
||||
"Are you sure you want to remove this member's access to the page?": "Are you sure you want to remove this member's access to the page?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Are you sure you want to remove all specific access? This will make the page open to everyone in the space.",
|
||||
"Trash retention": "Trash retention",
|
||||
"Pages in trash will be permanently deleted after this period.": "Pages in trash will be permanently deleted after this period.",
|
||||
"Trash retention updated": "Trash retention updated",
|
||||
"Failed to update trash retention": "Failed to update trash retention",
|
||||
"Removed page restriction": "Removed page restriction",
|
||||
"Added page permission": "Added page permission",
|
||||
"Removed page permission": "Removed page permission",
|
||||
"day": "day",
|
||||
"days": "days",
|
||||
"week": "week",
|
||||
"weeks": "weeks",
|
||||
"month": "month",
|
||||
"months": "months",
|
||||
"year": "year",
|
||||
"years": "years",
|
||||
"Period": "Period",
|
||||
"Fixed date": "Fixed date",
|
||||
"Indefinitely": "Indefinitely",
|
||||
"Days": "Days",
|
||||
"Weeks": "Weeks",
|
||||
"Months": "Months",
|
||||
"Years": "Years",
|
||||
"Pick a date": "Pick a date",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Maximum is {{max}} {{unit}} for this unit",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Never expires. Verifiers can re-verify at any time.",
|
||||
"Verified": "Verified",
|
||||
"Review needed": "Review needed",
|
||||
"Verification expired": "Verification expired",
|
||||
"Draft": "Draft",
|
||||
"In Approval": "In Approval",
|
||||
"In approval": "In approval",
|
||||
"Approved": "Approved",
|
||||
"Obsolete": "Obsolete",
|
||||
"Expiring": "Expiring",
|
||||
"Set up verification": "Set up verification",
|
||||
"Verify page": "Verify page",
|
||||
"Page verification": "Page verification",
|
||||
"Add verification": "Add verification",
|
||||
"Edit verification": "Edit verification",
|
||||
"Search by title": "Search by title",
|
||||
"Choose how this page should stay accurate.": "Choose how this page should stay accurate.",
|
||||
"Recurring verification": "Recurring verification",
|
||||
"Verifiers re-confirm this page on a schedule.": "Verifiers re-confirm this page on a schedule.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Re-verify on a schedule (e.g every 30 days )",
|
||||
"Page stays editable at all times": "Page stays editable at all times",
|
||||
"Best for runbooks, FAQs, living documentation": "Best for runbooks, FAQs, living documentation",
|
||||
"Approval workflow": "Approval workflow",
|
||||
"Formal document lifecycle with named approvers.": "Formal document lifecycle with named approvers.",
|
||||
"Draft → In approval → Approved → Obsolete": "Draft → In approval → Approved → Obsolete",
|
||||
"Locked once approved, with full history": "Locked once approved, with full history",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Designed for ISO 9001, ISO 13485, and FDA",
|
||||
"Best for SOPs and controlled documents": "Best for SOPs and controlled documents",
|
||||
"Back": "Back",
|
||||
"Quality management": "Quality management",
|
||||
"Recurring": "Recurring",
|
||||
"Pages move through draft, approval, and approved stages.": "Pages move through draft, approval, and approved stages.",
|
||||
"Verifiers": "Verifiers",
|
||||
"Add verifier": "Add verifier",
|
||||
"I've reviewed this page for accuracy": "I've reviewed this page for accuracy",
|
||||
"Set up": "Set up",
|
||||
"Remove verification": "Remove verification",
|
||||
"Are you sure you want to remove verification from this page?": "Are you sure you want to remove verification from this page?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Assigned verifiers must periodically re-verify this page.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Last verified by {{name}} {{time}} (expired)",
|
||||
"The fixed expiration date has passed.": "The fixed expiration date has passed.",
|
||||
"Verified by {{name}} {{time}}": "Verified by {{name}} {{time}}",
|
||||
"Expires {{date}}": "Expires {{date}}",
|
||||
"Expired {{date}}": "Expired {{date}}",
|
||||
"Mark as obsolete": "Mark as obsolete",
|
||||
"Mark obsolete": "Mark obsolete",
|
||||
"Returned by {{name}} {{time}}": "Returned by {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "No approval has been requested yet.",
|
||||
"Submitted by {{name}} {{time}}": "Submitted by {{name}} {{time}}",
|
||||
"Someone": "Someone",
|
||||
"Approved by {{name}} {{time}}": "Approved by {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "This document has been marked as obsolete.",
|
||||
"Rejection comment": "Rejection comment",
|
||||
"Reason for returning this document...": "Reason for returning this document...",
|
||||
"Confirm rejection": "Confirm rejection",
|
||||
"Submit for approval": "Submit for approval",
|
||||
"Reject": "Reject",
|
||||
"Approve": "Approve",
|
||||
"Re-submit for approval": "Re-submit for approval",
|
||||
"Verified until": "Verified until",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "Verified pages",
|
||||
"Search pages...": "Search pages...",
|
||||
"Filter by space": "Filter by space",
|
||||
"Filter by type": "Filter by type",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> verified a page",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> submitted a page for your approval",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> returned a page for revision",
|
||||
"Page verification expires soon": "Page verification expires soon",
|
||||
"Page verification has expired": "Page verification has expired",
|
||||
"Verifying your email": "Verifying your email",
|
||||
"Please wait...": "Please wait...",
|
||||
"Verification failed. The link may have expired.": "Verification failed. The link may have expired.",
|
||||
"Check your email": "Check your email",
|
||||
"We sent a verification link to {{email}}.": "We sent a verification link to {{email}}.",
|
||||
"We sent a verification link to your email.": "We sent a verification link to your email.",
|
||||
"Click the link to verify your email and access your workspace.": "Click the link to verify your email and access your workspace.",
|
||||
"Resend verification email": "Resend verification email",
|
||||
"Verification email sent. Please check your inbox.": "Verification email sent. Please check your inbox.",
|
||||
"Failed to resend verification email. Please try again.": "Failed to resend verification email. Please try again.",
|
||||
"We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces.",
|
||||
"Load more": "Load more",
|
||||
"Log out of all devices": "Log out of all devices",
|
||||
"Log out of all sessions except this device": "Log out of all sessions except this device",
|
||||
"This Device": "This Device",
|
||||
"Unknown device": "Unknown device",
|
||||
"No active sessions": "No active sessions",
|
||||
"Session revoked": "Session revoked",
|
||||
"All other sessions revoked": "All other sessions revoked",
|
||||
"Last used": "Last used",
|
||||
"Created": "Created",
|
||||
"Rename": "Rename",
|
||||
"Publish": "Publish",
|
||||
"Security": "Security",
|
||||
"Enforce SSO": "Enforce SSO",
|
||||
"Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password.",
|
||||
"AI-generated content may not be accurate.": "AI-generated content may not be accurate.",
|
||||
"AI Chat": "AI Chat",
|
||||
"Analyze for insights": "Analyze for insights",
|
||||
"Ask anything...": "Ask anything...",
|
||||
"Chat history": "Chat history",
|
||||
"Chat name": "Chat name",
|
||||
"Close": "Close",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Failed to load chat. An error occurred.",
|
||||
"Failed to render this message.": "Failed to render this message.",
|
||||
"How can I help you today?": "How can I help you today?",
|
||||
"New chat": "New chat",
|
||||
"No chat history": "No chat history",
|
||||
"No chats found": "No chats found",
|
||||
"No conversations yet": "No conversations yet",
|
||||
"Open full page": "Open full page",
|
||||
"Previous 7 days": "Previous 7 days",
|
||||
"Previous 30 days": "Previous 30 days",
|
||||
"Search chats...": "Search chats...",
|
||||
"Start a new chat to see it here.": "Start a new chat to see it here.",
|
||||
"Summarize this page": "Summarize this page",
|
||||
"Toggle AI Chat": "Toggle AI Chat",
|
||||
"Translate this page": "Translate this page",
|
||||
"Try a different search term.": "Try a different search term.",
|
||||
"Try again": "Try again",
|
||||
"Untitled chat": "Untitled chat",
|
||||
"What can I help you with?": "What can I help you with?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Agregar miembros",
|
||||
"Add to groups": "Agregar a grupos",
|
||||
"Add space members": "Agregar miembros al espacio",
|
||||
"Add to favorites": "Agregar a favoritos",
|
||||
"Admin": "Administrador",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "¿Estás seguro de que deseas eliminar este grupo? Los miembros perderán acceso a los recursos a los que este grupo tiene acceso.",
|
||||
"Are you sure you want to delete this page?": "¿Está seguro de que desea eliminar esta página?",
|
||||
@@ -44,15 +45,15 @@
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "¿Está seguro de que desea eliminar esta página? Esto eliminará sus dependientes y el historial de la página. Esta acción es irreversible.",
|
||||
"Description": "Descripción",
|
||||
"Details": "Detalles",
|
||||
"e.g ACME": "ej: ACME",
|
||||
"e.g ACME Inc": "ej: ACME Inc",
|
||||
"e.g Developers": "ej: Desarrolladores",
|
||||
"e.g Group for developers": "ej: Grupo para desarrolladores",
|
||||
"e.g product": "ej: producto",
|
||||
"e.g Product Team": "ej: Equipo de Producto",
|
||||
"e.g Sales": "ej: Ventas",
|
||||
"e.g Space for product team": "ej: Espacio para el equipo de producto",
|
||||
"e.g Space for sales team to collaborate": "ej: Espacio para que el equipo de ventas colabore",
|
||||
"e.g ACME": "p. ej., ACME",
|
||||
"e.g ACME Inc": "p. ej., ACME Inc",
|
||||
"e.g Developers": "p. ej., Desarrolladores",
|
||||
"e.g Group for developers": "p. ej., Grupo para desarrolladores",
|
||||
"e.g product": "p. ej., producto",
|
||||
"e.g Product Team": "p. ej., Equipo de producto",
|
||||
"e.g Sales": "p. ej., Ventas",
|
||||
"e.g Space for product team": "p. ej., Espacio para el equipo de producto",
|
||||
"e.g Space for sales team to collaborate": "p. ej., Espacio para que el equipo de ventas colabore",
|
||||
"Edit": "Editar",
|
||||
"Read": "Leer",
|
||||
"Edit group": "Editar grupo",
|
||||
@@ -61,7 +62,7 @@
|
||||
"Enter valid email addresses separated by comma or space max_50": "Ingrese direcciones de correo electrónico válidas separadas por coma o espacio [max: 50]",
|
||||
"enter valid emails addresses": "introduce direcciones de correo electrónico válidas",
|
||||
"Enter your current password": "Introduce tu contraseña actual",
|
||||
"enter your full name": "introduzca su nombre completo",
|
||||
"enter your full name": "introduce tu nombre completo",
|
||||
"Enter your new password": "Ingrese su nueva contraseña",
|
||||
"Enter your new preferred email": "Introduce tu nuevo correo electrónico preferido",
|
||||
"Enter your password": "Introduce tu contraseña",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "No se pudieron importar las páginas",
|
||||
"Failed to load page. An error occurred.": "Error al cargar la página. Se produjo un error.",
|
||||
"Failed to update data": "No se pudo actualizar los datos",
|
||||
"Favorite spaces": "Espacios favoritos",
|
||||
"Favorite spaces appear here": "Los espacios favoritos aparecen aquí",
|
||||
"Favorites": "Favoritos",
|
||||
"Full access": "Acceso completo",
|
||||
"Full page width": "Ancho de página completa",
|
||||
"Full width": "Ancho completo",
|
||||
@@ -92,6 +96,7 @@
|
||||
"Invite by email": "Invitar por correo electrónico",
|
||||
"Invite members": "Invitar a miembros",
|
||||
"Invite new members": "Invitar a nuevos miembros",
|
||||
"Invite People": "Invitar personas",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Los miembros invitados que aún no han aceptado su invitación aparecerán aquí.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Los miembros invitados recibirán acceso a los espacios a los que los grupos pueden acceder",
|
||||
"Join the workspace": "Unirse al espacio de trabajo",
|
||||
@@ -113,9 +118,10 @@
|
||||
"New email": "Nuevo correo electrónico",
|
||||
"New page": "Nueva página",
|
||||
"New password": "Nueva contraseña",
|
||||
"No group found": "No se encontró grupo",
|
||||
"No group found": "No se encontró ningún grupo",
|
||||
"No page history saved yet.": "No hay historial de la página guardado aún.",
|
||||
"No pages yet": "No hay páginas todavía",
|
||||
"No shared pages": "No hay páginas compartidas",
|
||||
"No results found...": "No se encontraron resultados...",
|
||||
"No user found": "No se encontró usuario",
|
||||
"Overview": "Visión general",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "páginas",
|
||||
"Password": "Contraseña",
|
||||
"Password changed successfully": "Contraseña cambiada con éxito",
|
||||
"People": "Personas",
|
||||
"Pending": "Pendiente",
|
||||
"Please confirm your action": "Por favor, confirme su acción",
|
||||
"Preferences": "Preferencias",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Perfil",
|
||||
"Recently updated": "Recientemente actualizado",
|
||||
"Remove": "Eliminar",
|
||||
"Remove from favorites": "Quitar de favoritos",
|
||||
"Remove group member": "Eliminar miembro del grupo",
|
||||
"Remove space member": "Eliminar miembro del espacio",
|
||||
"Restore": "Restaurar",
|
||||
@@ -149,54 +157,55 @@
|
||||
"Search...": "Buscar...",
|
||||
"Select language": "Seleccionar idioma",
|
||||
"Select role": "Seleccionar rol",
|
||||
"Select role to assign to all invited members": "Seleccionar rol para asignar a todos los miembros invitados",
|
||||
"Select role to assign to all invited members": "Selecciona el rol que se asignará a todos los miembros invitados",
|
||||
"Select theme": "Seleccionar tema",
|
||||
"Send invitation": "Enviar invitación",
|
||||
"Invitation sent": "Invitación enviada",
|
||||
"Settings": "Ajustes",
|
||||
"Settings": "Configuración",
|
||||
"Setup workspace": "Configurar espacio de trabajo",
|
||||
"Sign In": "Iniciar sesión",
|
||||
"Sign Up": "Registrarse",
|
||||
"Slug": "Identificador",
|
||||
"Slug": "Slug",
|
||||
"Space": "Espacio",
|
||||
"Space description": "Descripción del espacio",
|
||||
"Space menu": "Menú de espacio",
|
||||
"Space menu": "Menú del espacio",
|
||||
"Space name": "Nombre del espacio",
|
||||
"Space settings": "Configuración del espacio",
|
||||
"Space slug": "Identificador del espacio",
|
||||
"Space slug": "Slug del espacio",
|
||||
"Spaces": "Espacios",
|
||||
"Spaces you belong to": "Espacios a los que perteneces",
|
||||
"No space found": "No se encontró espacio",
|
||||
"No space found": "No se encontró ningún espacio",
|
||||
"Search for spaces": "Buscar espacios",
|
||||
"Start typing to search...": "Empieza a escribir para buscar...",
|
||||
"Status": "Estado",
|
||||
"Successfully imported": "Importado con éxito",
|
||||
"Successfully restored": "Restaurado con éxito",
|
||||
"Successfully imported": "Importado correctamente",
|
||||
"Successfully restored": "Restaurado correctamente",
|
||||
"System settings": "Configuración del sistema",
|
||||
"Templates": "Plantillas",
|
||||
"Theme": "Tema",
|
||||
"To change your email, you have to enter your password and new email.": "Para cambiar tu correo electrónico, debes ingresar tu contraseña y nuevo correo electrónico.",
|
||||
"Toggle full page width": "Alternar el ancho de página completa",
|
||||
"Toggle full page width": "Alternar ancho completo de la página",
|
||||
"Unable to import pages. Please try again.": "No se pueden importar las páginas. Por favor, inténtelo de nuevo.",
|
||||
"untitled": "sin título",
|
||||
"Untitled": "Sin título",
|
||||
"Updated successfully": "Actualizado con éxito",
|
||||
"Updated successfully": "Actualizado correctamente",
|
||||
"User": "Usuario",
|
||||
"Workspace": "Espacio de trabajo",
|
||||
"Workspace Name": "Nombre del espacio de trabajo",
|
||||
"Workspace settings": "Configuración del espacio de trabajo",
|
||||
"You can change your password here.": "Puede cambiar su contraseña aquí.",
|
||||
"Your Email": "Su correo electrónico",
|
||||
"Your Email": "Tu correo electrónico",
|
||||
"Your import is complete.": "Su importación está completa.",
|
||||
"Your name": "Tu nombre",
|
||||
"Your Name": "Tu Nombre",
|
||||
"Your Name": "Tu nombre",
|
||||
"Your password": "Tu contraseña",
|
||||
"Your password must be a minimum of 8 characters.": "Su contraseña debe tener un mínimo de 8 caracteres.",
|
||||
"Sidebar toggle": "Alternar barra lateral",
|
||||
"Comments": "Comentarios",
|
||||
"404 page not found": "404 página no encontrada",
|
||||
"Sorry, we can't find the page you are looking for.": "Lo sentimos, no podemos encontrar la página que buscas.",
|
||||
"Take me back to homepage": "Llévame de vuelta a la página de inicio",
|
||||
"Forgot password": "Olvidó la contraseña",
|
||||
"Take me back to homepage": "Llévame de vuelta a la página principal",
|
||||
"Forgot password": "Olvidé mi contraseña",
|
||||
"Forgot your password?": "¿Olvidó su contraseña?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "Se ha enviado un enlace para restablecer la contraseña a tu correo electrónico. Por favor, revisa tu bandeja de entrada.",
|
||||
"Send reset link": "Enviar enlace de restablecimiento",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Responder...",
|
||||
"Error loading comments.": "Error al cargar comentarios.",
|
||||
"No comments yet.": "No hay comentarios todavía.",
|
||||
"No open comments.": "No hay comentarios abiertos.",
|
||||
"No resolved comments.": "No hay comentarios resueltos.",
|
||||
"Add a comment...": "Agregar un comentario...",
|
||||
"Edit comment": "Editar comentario",
|
||||
"Delete comment": "Eliminar comentario",
|
||||
"Are you sure you want to delete this comment?": "¿Está seguro de que desea eliminar este comentario?",
|
||||
"Delete chat": "Eliminar chat",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "¿Está seguro de que desea eliminar '{{title}}'? Esta acción no se puede deshacer.",
|
||||
"Comment created successfully": "Comentario creado con éxito",
|
||||
"Error creating comment": "Error al crear comentario",
|
||||
"Comment updated successfully": "Comentario actualizado con éxito",
|
||||
@@ -217,18 +231,17 @@
|
||||
"Comment deleted successfully": "Comentario eliminado con éxito",
|
||||
"Failed to delete comment": "No se pudo eliminar el comentario",
|
||||
"Comment resolved successfully": "Comentario resuelto con éxito",
|
||||
"Comment re-opened successfully": "Comentario reabierto con éxito",
|
||||
"Comment unresolved successfully": "Comentario no resuelto con éxito",
|
||||
"Comment re-opened successfully": "Comentario reabierto correctamente",
|
||||
"Comment unresolved successfully": "Comentario marcado como no resuelto correctamente",
|
||||
"Failed to resolve comment": "No se pudo resolver el comentario",
|
||||
"Resolve comment": "Resolver comentario",
|
||||
"Unresolve comment": "No resolver comentario",
|
||||
"Unresolve comment": "Marcar comentario como no resuelto",
|
||||
"Resolve Comment Thread": "Resolver hilo de comentarios",
|
||||
"Unresolve Comment Thread": "No resolver hilo de comentarios",
|
||||
"Unresolve Comment Thread": "Marcar hilo de comentarios como no resuelto",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "¿Está seguro de que desea resolver este hilo de comentarios? Esto lo marcará como completado.",
|
||||
"Are you sure you want to unresolve this comment thread?": "¿Está seguro de que desea no resolver este hilo de comentarios?",
|
||||
"Resolved": "Resuelto",
|
||||
"No active comments.": "No hay comentarios activos.",
|
||||
"No resolved comments.": "No hay comentarios resueltos.",
|
||||
"Revoke invitation": "Revocar invitación",
|
||||
"Revoke": "Revocar",
|
||||
"Don't": "No",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Agregar fila debajo",
|
||||
"Delete table": "Eliminar tabla",
|
||||
"Info": "Información",
|
||||
"Note": "Nota",
|
||||
"Success": "Satisfactorio",
|
||||
"Warning": "Advertencia",
|
||||
"Danger": "Peligro",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Guardar y Salir",
|
||||
"Double-click to edit Excalidraw diagram": "Doble clic para editar el diagrama de Excalidraw",
|
||||
"Paste link": "Pegar enlace",
|
||||
"Paste link or search pages": "Pega un enlace o busca páginas",
|
||||
"Link to web page": "Enlazar a una página web",
|
||||
"Recents": "Recientes",
|
||||
"Page or URL": "Página o URL",
|
||||
"Link title": "Título del enlace",
|
||||
"Edit link": "Editar enlace",
|
||||
"Remove link": "Eliminar enlace",
|
||||
"Add link": "Agregar enlace",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Rosa",
|
||||
"Gray": "Gris",
|
||||
"Embed link": "Enlace adjunto",
|
||||
"Invalid {{provider}} embed link": "Enlace incrustado {{provider}} no válido",
|
||||
"Invalid {{provider}} embed link": "Enlace de inserción de {{provider}} no válido",
|
||||
"Embed {{provider}}": "Incrustar {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Introduzca el enlace de {{provider}} para incrustar",
|
||||
"Bold": "Negrita",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Insertar regla horizontal",
|
||||
"Upload any image from your device.": "Sube cualquier imagen desde tu dispositivo.",
|
||||
"Upload any video from your device.": "Sube cualquier video desde tu dispositivo.",
|
||||
"Upload any audio from your device.": "Sube cualquier audio desde tu dispositivo.",
|
||||
"Upload any file from your device.": "Sube cualquier archivo desde tu dispositivo.",
|
||||
"Uploading {{name}}": "Subiendo {{name}}",
|
||||
"Uploading file": "Subiendo archivo",
|
||||
@@ -338,82 +358,128 @@
|
||||
"Insert a table.": "Insertar una tabla.",
|
||||
"Insert collapsible block.": "Insertar bloque desplegable.",
|
||||
"Video": "Vídeo",
|
||||
"Divider": "Divisor",
|
||||
"Divider": "Separador",
|
||||
"Quote": "Cita",
|
||||
"Image": "Imagen",
|
||||
"File attachment": "Adjunto de archivo",
|
||||
"Toggle block": "Alternar bloque",
|
||||
"Callout": "Aviso",
|
||||
"Audio": "Audio",
|
||||
"Embed PDF": "Adjuntar PDF",
|
||||
"Upload and embed a PDF file.": "Sube y adjunta un archivo PDF.",
|
||||
"Embed as PDF": "Adjuntar como PDF",
|
||||
"Failed to load PDF": "Error al cargar el PDF",
|
||||
"Convert to attachment": "Convertir en adjunto",
|
||||
"File attachment": "Archivo adjunto",
|
||||
"Toggle block": "Bloque desplegable",
|
||||
"Callout": "Llamada de atención",
|
||||
"Insert callout notice.": "Insertar aviso de llamada.",
|
||||
"Math inline": "Matemáticas en línea",
|
||||
"Insert inline math equation.": "Insertar ecuación matemática en línea.",
|
||||
"Math block": "Bloque de matemáticas",
|
||||
"Math block": "Bloque matemático",
|
||||
"Insert math equation": "Insertar ecuación matemática",
|
||||
"Mermaid diagram": "Diagrama de Mermaid",
|
||||
"Insert mermaid diagram": "Insertar diagrama de Mermaid",
|
||||
"Insert and design Drawio diagrams": "Insertar y diseñar diagramas Drawio",
|
||||
"Mermaid diagram": "Diagrama Mermaid",
|
||||
"Insert mermaid diagram": "Insertar diagrama Mermaid",
|
||||
"Insert and design Drawio diagrams": "Insertar y diseñar diagramas de Drawio",
|
||||
"Insert current date": "Insertar fecha actual",
|
||||
"Draw and sketch excalidraw diagrams": "Dibujar y esbozar diagramas de Excalidraw",
|
||||
"Draw and sketch excalidraw diagrams": "Dibujar y crear bocetos de diagramas de Excalidraw",
|
||||
"Multiple": "Múltiple",
|
||||
"Turn into": "Convertir en",
|
||||
"Text align": "Alineación del texto",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Es posible que esta página haya sido eliminada, movida o que no tengas acceso.",
|
||||
"Go to homepage": "Ir a la página principal",
|
||||
"Pages you create will show up here.": "Las páginas que crees aparecerán aquí.",
|
||||
"Heading {{level}}": "Encabezado {{level}}",
|
||||
"Toggle title": "Alternar título",
|
||||
"Write anything. Enter \"/\" for commands": "Escribe cualquier cosa. Ingresa \"/\" para comandos",
|
||||
"Write anything. Enter \"/\" for commands": "Escribe cualquier cosa. Introduce \"/\" para ver los comandos",
|
||||
"Write...": "Escribe...",
|
||||
"Column count": "Número de columnas",
|
||||
"{{count}} Columns": "{count, plural, one {# columna} other {# columnas}}",
|
||||
"Equal columns": "Columnas iguales",
|
||||
"Left sidebar": "Barra lateral izquierda",
|
||||
"Right sidebar": "Barra lateral derecha",
|
||||
"Wide center": "Centro ancho",
|
||||
"Left wide": "Izquierda ancha",
|
||||
"Right wide": "Derecha ancha",
|
||||
"Names do not match": "Los nombres no coinciden",
|
||||
"Today, {{time}}": "Hoy, {{time}}",
|
||||
"Yesterday, {{time}}": "Ayer, {{time}}",
|
||||
"Space created successfully": "Espacio creado con éxito",
|
||||
"Space updated successfully": "Espacio actualizado con éxito",
|
||||
"Space deleted successfully": "Espacio eliminado con éxito",
|
||||
"Members added successfully": "Miembros añadidos con éxito",
|
||||
"Member removed successfully": "Miembro eliminado con éxito",
|
||||
"Member role updated successfully": "Rol de miembro actualizado con éxito",
|
||||
"Space created successfully": "Espacio creado correctamente",
|
||||
"Space updated successfully": "Espacio actualizado correctamente",
|
||||
"Space deleted successfully": "Espacio eliminado correctamente",
|
||||
"Members added successfully": "Miembros agregados correctamente",
|
||||
"Member removed successfully": "Miembro eliminado correctamente",
|
||||
"Member role updated successfully": "Rol del miembro actualizado correctamente",
|
||||
"Created by: <b>{{creatorName}}</b>": "Creado por: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Creado a: {{time}}",
|
||||
"Created at: {{time}}": "Creado el: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Editado por {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Conteo de palabras: {{wordCount}}",
|
||||
"Word count: {{wordCount}}": "Recuento de palabras: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Recuento de caracteres: {{characterCount}}",
|
||||
"New update": "Nueva actualización",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} está disponible",
|
||||
"Default page edit mode": "Modo de edición de página predeterminado",
|
||||
"Default page edit mode": "Modo de edición predeterminado de la página",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Elige tu modo de edición de página preferido. Evita ediciones accidentales.",
|
||||
"Reading": "Leyendo",
|
||||
"Reading": "Lectura",
|
||||
"Delete member": "Eliminar miembro",
|
||||
"Member deleted successfully": "Miembro eliminado con éxito",
|
||||
"Member deleted successfully": "Miembro eliminado correctamente",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "¿Está seguro que desea eliminar este miembro del área de trabajo? Esta acción es irreversible.",
|
||||
"Deactivate member": "Desactivar miembro",
|
||||
"Activate member": "Activar miembro",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "¿Está seguro de que desea desactivar a este miembro del espacio de trabajo? Ya no podrá acceder a este espacio de trabajo.",
|
||||
"Are you sure you want to activate this workspace member?": "¿Está seguro de que desea activar a este miembro del espacio de trabajo?",
|
||||
"Deactivate": "Desactivar",
|
||||
"Activate": "Activar",
|
||||
"Deactivated": "Desactivado",
|
||||
"Move": "Mover",
|
||||
"Move page": "Mover página",
|
||||
"Move page to a different space.": "Mover página a un espacio diferente.",
|
||||
"Real-time editor connection lost. Retrying...": "Conexión del editor en tiempo real perdida. Reintentando...",
|
||||
"Table of contents": "Índice de contenidos",
|
||||
"Table of contents": "Tabla de contenido",
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Añadir encabezados (H1, H2, H3) para generar un índice de contenidos.",
|
||||
"Share": "Compartir",
|
||||
"Public sharing": "Compartición pública",
|
||||
"Public sharing": "Uso compartido público",
|
||||
"Shared by": "Compartido por",
|
||||
"Shared at": "Compartido en",
|
||||
"Inherits public sharing from": "Hereda la compartición pública de",
|
||||
"Shared at": "Compartido el",
|
||||
"Inherits public sharing from": "Hereda el uso compartido público de",
|
||||
"Share to web": "Compartir en la web",
|
||||
"Shared to web": "Compartido en la web",
|
||||
"Anyone with the link can view this page": "Cualquiera con el enlace puede ver esta página",
|
||||
"Make this page publicly accessible": "Hacer esta página accesible públicamente",
|
||||
"Anyone with the link can view this page": "Cualquier persona con el enlace puede ver esta página",
|
||||
"Make this page publicly accessible": "Hacer que esta página sea accesible públicamente",
|
||||
"Include sub-pages": "Incluir subpáginas",
|
||||
"Make sub-pages public too": "Hacer públicas también las subpáginas",
|
||||
"Allow search engines to index page": "Permitir a los motores de búsqueda indexar la página",
|
||||
"Allow search engines to index page": "Permitir que los motores de búsqueda indexen la página",
|
||||
"Open page": "Abrir página",
|
||||
"Page": "Página",
|
||||
"Delete public share link": "Eliminar enlace de compartición pública",
|
||||
"Delete share": "Eliminar compartición",
|
||||
"Delete public share link": "Eliminar enlace público compartido",
|
||||
"Delete share": "Eliminar recurso compartido",
|
||||
"Are you sure you want to delete this shared link?": "¿Está seguro de que desea eliminar este enlace compartido?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Las páginas compartidas públicamente de los espacios a los que pertenece aparecerán aquí",
|
||||
"Share deleted successfully": "Compartición eliminada con éxito",
|
||||
"Share not found": "Compartición no encontrada",
|
||||
"Failed to share page": "Error al compartir la página",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Las páginas compartidas públicamente de los espacios de los que eres miembro aparecerán aquí",
|
||||
"Share deleted successfully": "Recurso compartido eliminado correctamente",
|
||||
"Share not found": "Recurso compartido no encontrado",
|
||||
"Failed to share page": "No se pudo compartir la página",
|
||||
"Disable public sharing": "Desactivar el uso compartido público",
|
||||
"Prevent members from sharing pages publicly.": "Evitar que los miembros compartan páginas públicamente.",
|
||||
"Toggle public sharing": "Alternar el uso compartido público",
|
||||
"Toggle space public sharing": "Alternar el uso compartido público del espacio",
|
||||
"Allow viewers to comment": "Permitir que los espectadores comenten",
|
||||
"Allow viewers to add comments on pages in this space.": "Permitir que los espectadores agreguen comentarios en las páginas de este espacio.",
|
||||
"Toggle viewer comments": "Activar/desactivar comentarios de los espectadores",
|
||||
"Public sharing is disabled at the workspace level": "El uso compartido público está desactivado a nivel de espacio de trabajo",
|
||||
"Prevent pages in this space from being shared publicly.": "Evitar que las páginas en este espacio se compartan públicamente.",
|
||||
"Page permissions": "Permisos de la página},{",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Controla quién puede ver y editar páginas individuales. Disponible con una licencia empresarial.",
|
||||
"Enable public sharing": "Activar el uso compartido público",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "¿Está seguro de que desea activar el uso compartido público? Los miembros podrán compartir páginas públicamente.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "¿Está seguro de que desea desactivar el uso compartido público? Todos los enlaces compartidos existentes en este espacio de trabajo se eliminarán.",
|
||||
"Are you sure you want to enable public sharing for this space?": "¿Está seguro de que desea activar el uso compartido público para este espacio?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "¿Está seguro de que desea desactivar el uso compartido público? Todos los enlaces compartidos existentes en este espacio se eliminarán.",
|
||||
"Public sharing is disabled": "El uso compartido público está desactivado",
|
||||
"Public sharing has been disabled at the workspace level.": "El uso compartido público se ha desactivado a nivel de espacio de trabajo.",
|
||||
"Public sharing has been disabled for this space.": "El uso compartido público se ha desactivado para este espacio.",
|
||||
"Copy page": "Copiar página",
|
||||
"Copy page to a different space.": "Copiar página en otro espacio",
|
||||
"Page copied successfully": "Página copiada exitosamente",
|
||||
"Page duplicated successfully": "Página duplicada con éxito",
|
||||
"Page copied successfully": "Página copiada correctamente",
|
||||
"Page duplicated successfully": "Página duplicada correctamente",
|
||||
"Find": "Buscar",
|
||||
"Not found": "No encontrado",
|
||||
"Previous Match (Shift+Enter)": "Coincidencia anterior (Shift+Enter)",
|
||||
"Previous Match (Shift+Enter)": "Coincidencia anterior (Mayús+Enter)",
|
||||
"Next match (Enter)": "Siguiente coincidencia (Enter)",
|
||||
"Match case (Alt+C)": "Distinguir mayúsculas y minúsculas (Alt+C)",
|
||||
"Replace": "Reemplazar",
|
||||
@@ -421,36 +487,37 @@
|
||||
"Replace (Enter)": "Reemplazar (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Reemplazar todo (Ctrl+Alt+Enter)",
|
||||
"Replace all": "Reemplazar todo",
|
||||
"View all": "Ver todo",
|
||||
"View all spaces": "Ver todos los espacios",
|
||||
"Error": "Error",
|
||||
"Failed to disable MFA": "No se pudo desactivar MFA",
|
||||
"Failed to disable MFA": "No se pudo desactivar la MFA",
|
||||
"Disable two-factor authentication": "Desactivar la autenticación de dos factores",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Desactivar la autenticación de dos factores hará que tu cuenta sea menos segura. Solo necesitarás tu contraseña para iniciar sesión.",
|
||||
"Please enter your password to disable two-factor authentication:": "Por favor ingresa tu contraseña para desactivar la autenticación de dos factores:",
|
||||
"Two-factor authentication has been enabled": "La autenticación de dos factores ha sido activada",
|
||||
"Two-factor authentication has been disabled": "La autenticación de dos factores ha sido desactivada",
|
||||
"Two-factor authentication has been enabled": "La autenticación de dos factores se ha activado",
|
||||
"Two-factor authentication has been disabled": "La autenticación de dos factores se ha desactivado",
|
||||
"2-step verification": "Verificación en 2 pasos",
|
||||
"Protect your account with an additional verification layer when signing in.": "Protege tu cuenta con una capa adicional de verificación al iniciar sesión.",
|
||||
"Two-factor authentication is active on your account.": "La autenticación de dos factores está activa en tu cuenta.",
|
||||
"Add 2FA method": "Agregar método 2FA",
|
||||
"Backup codes": "Códigos de seguridad",
|
||||
"Add 2FA method": "Agregar método de 2FA",
|
||||
"Backup codes": "Códigos de respaldo",
|
||||
"Disable": "Desactivar",
|
||||
"Invalid verification code": "Código de verificación no válido",
|
||||
"New backup codes have been generated": "Nuevos códigos de seguridad han sido generados",
|
||||
"Failed to regenerate backup codes": "No se pudo regenerar los códigos de seguridad",
|
||||
"About backup codes": "Acerca de los códigos de seguridad",
|
||||
"New backup codes have been generated": "Se han generado nuevos códigos de respaldo",
|
||||
"Failed to regenerate backup codes": "No se pudieron regenerar los códigos de respaldo",
|
||||
"About backup codes": "Acerca de los códigos de respaldo",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Los códigos de seguridad pueden usarse para acceder a tu cuenta si pierdes acceso a tu aplicación autenticadora. Cada código solo puede ser usado una vez.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Puedes regenerar nuevos códigos de seguridad en cualquier momento. Esto invalidará todos los códigos existentes.",
|
||||
"Confirm password": "Confirmar contraseña",
|
||||
"Generate new backup codes": "Generar nuevos códigos de seguridad",
|
||||
"Save your new backup codes": "Guarda tus nuevos códigos de seguridad",
|
||||
"Generate new backup codes": "Generar nuevos códigos de respaldo",
|
||||
"Save your new backup codes": "Guarda tus nuevos códigos de respaldo",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Asegúrate de guardar estos códigos en un lugar seguro. Tus viejos códigos de seguridad ya no son válidos.",
|
||||
"Your new backup codes": "Tus nuevos códigos de seguridad",
|
||||
"I've saved my backup codes": "He guardado mis códigos de seguridad",
|
||||
"Failed to setup MFA": "No se pudo configurar MFA",
|
||||
"Your new backup codes": "Tus nuevos códigos de respaldo",
|
||||
"I've saved my backup codes": "He guardado mis códigos de respaldo",
|
||||
"Failed to setup MFA": "No se pudo configurar la MFA",
|
||||
"Setup & Verify": "Configurar y verificar",
|
||||
"Add to authenticator": "Agregar al autenticador",
|
||||
"1. Scan this QR code with your authenticator app": "1. Escanea este código QR con tu aplicación autenticadora",
|
||||
"1. Scan this QR code with your authenticator app": "1. Escanea este código QR con tu aplicación de autenticación",
|
||||
"Can't scan the code?": "¿No puedes escanear el código?",
|
||||
"Enter this code manually in your authenticator app:": "Introduce este código manualmente en tu aplicación autenticadora:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Introduce el código de 6 dígitos de tu autenticador",
|
||||
@@ -458,38 +525,38 @@
|
||||
"Failed to generate QR code. Please try again.": "No se pudo generar el código QR. Por favor, intente de nuevo.",
|
||||
"Backup": "Respaldo",
|
||||
"Save codes": "Guardar códigos",
|
||||
"Save your backup codes": "Guarda tus códigos de seguridad",
|
||||
"Save your backup codes": "Guarda tus códigos de respaldo",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Estos códigos pueden usarse para acceder a tu cuenta si pierdes acceso a tu aplicación autenticadora. Cada código solo puede ser usado una vez.",
|
||||
"Print": "Imprimir",
|
||||
"Two-factor authentication has been set up. Please log in again.": "La autenticación de dos factores ha sido configurada. Por favor, inicie sesión nuevamente.",
|
||||
"Two-Factor authentication required": "Se requiere autenticación de dos factores",
|
||||
"Your workspace requires two-factor authentication for all users": "Tu espacio de trabajo requiere autenticación de dos factores para todos los usuarios",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Para continuar accediendo a tu espacio de trabajo, debes configurar la autenticación de dos factores. Esto añade una capa extra de seguridad a tu cuenta.",
|
||||
"Set up two-factor authentication": "Configurar la autenticación de dos factores",
|
||||
"Set up two-factor authentication": "Configurar autenticación de dos factores",
|
||||
"Cancel and logout": "Cancelar y cerrar sesión",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Tu espacio de trabajo requiere autenticación de dos factores. Por favor, configúralo para continuar.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Esto añade una capa extra de seguridad a tu cuenta al requerir un código de verificación de tu aplicación autenticadora.",
|
||||
"Password is required": "Se requiere contraseña",
|
||||
"Password is required": "La contraseña es obligatoria",
|
||||
"Password must be at least 8 characters": "La contraseña debe tener al menos 8 caracteres",
|
||||
"Please enter a 6-digit code": "Por favor, introduce un código de 6 dígitos",
|
||||
"Code must be exactly 6 digits": "El código debe ser exactamente de 6 dígitos",
|
||||
"Enter the 6-digit code found in your authenticator app": "Introduce el código de 6 dígitos que se encuentra en tu aplicación autenticadora",
|
||||
"Please enter a 6-digit code": "Introduce un código de 6 dígitos",
|
||||
"Code must be exactly 6 digits": "El código debe tener exactamente 6 dígitos",
|
||||
"Enter the 6-digit code found in your authenticator app": "Introduce el código de 6 dígitos que aparece en tu aplicación de autenticación",
|
||||
"Need help authenticating?": "¿Necesitas ayuda para autenticar?",
|
||||
"MFA QR Code": "Código QR MFA",
|
||||
"MFA QR Code": "Código QR de MFA",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Cuenta creada exitosamente. Por favor, inicie sesión para configurar la autenticación de dos factores.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "Restablecimiento de contraseña exitoso. Por favor, inicie sesión con su nueva contraseña y complete la autenticación de dos factores.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Restablecimiento de contraseña exitoso. Por favor, inicie sesión con su nueva contraseña para configurar la autenticación de dos factores.",
|
||||
"Password reset was successful. Please log in with your new password.": "El restablecimiento de contraseña fue exitoso. Por favor, inicie sesión con su nueva contraseña.",
|
||||
"Two-factor authentication": "Autenticación de dos factores",
|
||||
"Use authenticator app instead": "Usar la aplicación autenticadora en su lugar",
|
||||
"Verify backup code": "Verificar código de seguridad",
|
||||
"Use backup code": "Usar código de seguridad",
|
||||
"Enter one of your backup codes": "Introduce uno de tus códigos de seguridad",
|
||||
"Backup code": "Código de seguridad",
|
||||
"Use authenticator app instead": "Usar la aplicación de autenticación en su lugar",
|
||||
"Verify backup code": "Verificar código de respaldo",
|
||||
"Use backup code": "Usar código de respaldo",
|
||||
"Enter one of your backup codes": "Introduce uno de tus códigos de respaldo",
|
||||
"Backup code": "Código de respaldo",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Introduce uno de tus códigos de seguridad. Cada código de seguridad solo puede ser usado una vez.",
|
||||
"Verify": "Verificar",
|
||||
"Trash": "Papelera",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Las páginas en la papelera serán eliminadas permanentemente después de 30 días.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "{count, plural, one{Las páginas en la papelera se eliminarán permanentemente después de # día.} other{Las páginas en la papelera se eliminarán permanentemente después de # días.}}",
|
||||
"Deleted": "Eliminado",
|
||||
"No pages in trash": "No hay páginas en la papelera",
|
||||
"Permanently delete page?": "¿Eliminar página permanentemente?",
|
||||
@@ -499,35 +566,35 @@
|
||||
"Move this page to trash?": "¿Mover esta página a la papelera?",
|
||||
"Restore page": "Restaurar página",
|
||||
"Page moved to trash": "Página movida a la papelera",
|
||||
"Page restored successfully": "Página restaurada con éxito",
|
||||
"Page restored successfully": "Página restaurada correctamente",
|
||||
"Deleted by": "Eliminado por",
|
||||
"Deleted at": "Eliminado en",
|
||||
"Deleted at": "Eliminado el",
|
||||
"Preview": "Vista previa",
|
||||
"Subpages": "Subpáginas",
|
||||
"Failed to load subpages": "Error al cargar subpáginas",
|
||||
"No subpages": "Sin subpáginas",
|
||||
"Subpages (Child pages)": "Subpáginas (Páginas hijas)",
|
||||
"Failed to load subpages": "No se pudieron cargar las subpáginas",
|
||||
"No subpages": "No hay subpáginas",
|
||||
"Subpages (Child pages)": "Subpáginas (páginas hijas)",
|
||||
"List all subpages of the current page": "Listar todas las subpáginas de la página actual",
|
||||
"Attachments": "Adjuntos",
|
||||
"Attachments": "Archivos adjuntos",
|
||||
"All spaces": "Todos los espacios",
|
||||
"Unknown": "Desconocido",
|
||||
"Find a space": "Encontrar un espacio",
|
||||
"Find a space": "Buscar un espacio",
|
||||
"Search in all your spaces": "Buscar en todos tus espacios",
|
||||
"Type": "Tipo",
|
||||
"Enterprise": "Empresa",
|
||||
"Download attachment": "Descargar adjunto",
|
||||
"Enterprise": "Empresarial",
|
||||
"Download attachment": "Descargar archivo adjunto",
|
||||
"Allowed email domains": "Dominios de correo electrónico permitidos",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Solo los usuarios con direcciones de correo electrónico de estos dominios pueden registrarse a través de SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Introduce nombres de dominio válidos separados por coma o espacio",
|
||||
"Enforce two-factor authentication": "Aplicar autenticación de dos factores",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Solo los usuarios con direcciones de correo electrónico de estos dominios pueden registrarse mediante SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Introduce nombres de dominio válidos separados por comas o espacios",
|
||||
"Enforce two-factor authentication": "Exigir autenticación de dos factores",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Una vez aplicada, todos los miembros deben habilitar la autenticación de dos factores para acceder al espacio de trabajo.",
|
||||
"Toggle MFA enforcement": "Alternar la aplicación de MFA",
|
||||
"Toggle MFA enforcement": "Alternar exigencia de MFA",
|
||||
"Display name": "Nombre para mostrar",
|
||||
"Allow signup": "Permitir registro",
|
||||
"Enabled": "Habilitado",
|
||||
"Enabled": "Activado",
|
||||
"Advanced Settings": "Configuración avanzada",
|
||||
"Enable TLS/SSL": "Habilitar TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Usar conexión segura al servidor LDAP",
|
||||
"Enable TLS/SSL": "Activar TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Usar una conexión segura al servidor LDAP",
|
||||
"Group sync": "Sincronización de grupos",
|
||||
"No SSO providers found.": "No se encontraron proveedores de SSO.",
|
||||
"Delete SSO provider": "Eliminar proveedor de SSO",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Esta acción no se puede deshacer. Las aplicaciones que utilicen esta clave API dejarán de funcionar.",
|
||||
"Update API key": "Actualizar clave API",
|
||||
"Manage API keys for all users in the workspace": "Gestionar claves API para todos los usuarios en el espacio de trabajo",
|
||||
"Restrict API key creation to admins": "Restringir la creación de claves API a administradores",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Solo los administradores y propietarios pueden crear nuevas claves API. Las claves de miembros existentes seguirán funcionando.",
|
||||
"Toggle restrict API keys to admins": "Activar o desactivar la restricción de claves API solo a administradores",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "La creación de claves API está restringida a administradores por el administrador de tu espacio de trabajo.",
|
||||
"AI settings": "Configuración de IA",
|
||||
"AI search": "Búsqueda de IA",
|
||||
"AI Answer": "Respuesta de IA",
|
||||
"Ask AI": "Preguntar a IA",
|
||||
"AI is thinking...": "IA está pensando...",
|
||||
"Thinking": "Pensando",
|
||||
"Ask a question...": "Haz una pregunta...",
|
||||
"AI-powered search (Ask AI)": "Búsqueda impulsada por IA (Preguntar a IA)",
|
||||
"AI Answers": "Respuestas de IA",
|
||||
"AI-powered search (AI Answers)": "Búsqueda impulsada por IA (Respuestas de IA)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "La búsqueda de IA utiliza incrustaciones vectoriales para proporcionar capacidades de búsqueda semántica en todo el contenido de su espacio de trabajo.",
|
||||
"Toggle AI search": "Alternar búsqueda de IA",
|
||||
"Generative AI (Ask AI)": "IA generativa (Preguntar a la IA)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Habilitar la generación de contenido impulsada por IA en el editor. Permite a los usuarios generar, mejorar, traducir y transformar texto.",
|
||||
"Toggle generative AI": "Activar IA generativa",
|
||||
"Upgrade your plan": "Mejora tu plan",
|
||||
"Available with a paid license": "Disponible con una licencia de pago",
|
||||
"Upgrade your license tier.": "Mejora el nivel de tu licencia.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "La IA solo está disponible en la edición empresarial de Docmost. Contacte con sales@docmost.com.",
|
||||
"AI & MCP": "IA y MCP",
|
||||
"AI": "IA",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Protocolo de Contexto del Modelo (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Habilite el servidor MCP para permitir que asistentes de IA y herramientas interactúen con el contenido de su espacio de trabajo.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP solo está disponible en la edición empresarial de Docmost. Contacte con sales@docmost.com.",
|
||||
"MCP Server URL": "URL del servidor MCP",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Use su clave API para la autenticación. Puede gestionar las claves API en la configuración de su cuenta.",
|
||||
"Supported tools": "Herramientas compatibles",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Su espacio de trabajo tiene MCP habilitado. Use su clave API para conectar asistentes de IA.",
|
||||
"MCP server URL:": "URL del servidor MCP:",
|
||||
"Learn more": "Más información",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gestiona las claves de API para todos los usuarios en el espacio de trabajo. Consulta la <anchor>documentación de la API</anchor> para detalles de uso.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Consulta la <anchor>documentación de la API</anchor> para detalles de uso.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Consulta la <anchor>documentación de MCP</anchor>.",
|
||||
"Sources": "Fuentes",
|
||||
"Ask AI not available for attachments": "Preguntar a IA no está disponible para adjuntos",
|
||||
"AI Answers not available for attachments": "Respuestas de IA no disponibles para archivos adjuntos",
|
||||
"No answer available": "No hay respuesta disponible",
|
||||
"Background color": "Color de fondo",
|
||||
"Highlight color": "Color de resaltado",
|
||||
"Remove color": "Eliminar color"
|
||||
"Remove color": "Eliminar color",
|
||||
"Notifications": "Notificaciones",
|
||||
"No notifications": "Sin notificaciones",
|
||||
"No unread notifications": "No hay notificaciones no leídas",
|
||||
"All notifications": "Todas las notificaciones",
|
||||
"Unread only": "Solo no leídas",
|
||||
"Mark all as read": "Marcar todo como leído",
|
||||
"Mark as read": "Marcar como leído",
|
||||
"More options": "Más opciones",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> te mencionó en un comentario",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> comentó en una página",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolvió un comentario",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> te mencionó en una página",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> te dio acceso de edición a una página",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> te dio acceso de visualización a una página",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> actualizó una página",
|
||||
"Watch page": "Seguir página",
|
||||
"Stop watching": "Dejar de seguir",
|
||||
"Watch space": "Seguir espacio",
|
||||
"Stop watching space": "Dejar de seguir espacio",
|
||||
"Email notifications": "Notificaciones por correo electrónico",
|
||||
"Page updates": "Actualizaciones de página",
|
||||
"Get notified when pages you watch are updated.": "Recibe una notificación cuando se actualicen las páginas que sigues.",
|
||||
"Page mentions": "Menciones en la página",
|
||||
"Get notified when someone mentions you on a page.": "Recibe una notificación cuando alguien te mencione en una página.",
|
||||
"Comment mentions": "Menciones en comentarios",
|
||||
"Get notified when someone mentions you in a comment.": "Recibe una notificación cuando alguien te mencione en un comentario.",
|
||||
"New comments": "Nuevos comentarios",
|
||||
"Get notified about new comments on threads you participate in.": "Recibe una notificación sobre nuevos comentarios en los hilos donde participas.",
|
||||
"Resolved comments": "Comentarios resueltos",
|
||||
"Get notified when your comment is resolved.": "Recibe una notificación cuando tu comentario sea resuelto.",
|
||||
"You are now watching this page": "Ahora sigues esta página",
|
||||
"You are no longer watching this page": "Ya no sigues esta página",
|
||||
"You are now watching this space": "Ahora sigues este espacio",
|
||||
"You are no longer watching this space": "Ya no sigues este espacio",
|
||||
"Direct": "Directo",
|
||||
"Updates": "Actualizaciones",
|
||||
"Today": "Hoy",
|
||||
"Yesterday": "Ayer",
|
||||
"This week": "Esta semana",
|
||||
"Older": "Más antiguo",
|
||||
"Restricted page": "Página restringida",
|
||||
"Restricted pages cannot be shared publicly.": "Las páginas restringidas no pueden compartirse públicamente.",
|
||||
"Restricted by parent": "Restringida por la página padre",
|
||||
"Restricted": "Restringida",
|
||||
"Open": "Abierta",
|
||||
"Inherits restrictions from ancestor page": "Hereda las restricciones de una página superior.",
|
||||
"Only people listed below can access this page": "Solo las personas que figuran a continuación pueden acceder a esta página.",
|
||||
"Everyone in this space can access": "Todos en este espacio pueden acceder.",
|
||||
"No additional restrictions on this page": "No hay restricciones adicionales en esta página.",
|
||||
"Only specific people can access": "Solo determinadas personas pueden acceder.",
|
||||
"Use only inherited restrictions": "Usar solo las restricciones heredadas.",
|
||||
"Add restrictions on top of inherited": "Agregar restricciones además de las heredadas.",
|
||||
"Inherited restriction": "Restricción heredada",
|
||||
"Access limited by": "Acceso limitado por",
|
||||
"Restrict access to control who can view and edit this page": "Restringir el acceso para controlar quién puede ver y editar esta página.",
|
||||
"Add additional restrictions specific to this page": "Agregar restricciones adicionales específicas para esta página.",
|
||||
"Access": "Acceso",
|
||||
"People with access": "Personas con acceso",
|
||||
"Remove all": "Eliminar todo",
|
||||
"Remove access": "Eliminar acceso",
|
||||
"Remove all access": "Eliminar todo el acceso",
|
||||
"Are you sure you want to remove this member's access to the page?": "¿Está seguro de que desea eliminar el acceso de este miembro a la página?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "¿Está seguro de que desea eliminar todo el acceso específico? Esto hará que la página esté abierta a todos en el espacio.",
|
||||
"Trash retention": "Retención de la papelera",
|
||||
"Pages in trash will be permanently deleted after this period.": "Las páginas en la papelera se eliminarán permanentemente después de este período.",
|
||||
"Trash retention updated": "Retención de la papelera actualizada",
|
||||
"Failed to update trash retention": "No se pudo actualizar la retención de la papelera.",
|
||||
"Removed page restriction": "Restricción de página eliminada",
|
||||
"Added page permission": "Permiso de página añadido",
|
||||
"Removed page permission": "Permiso de página eliminado",
|
||||
"day": "día",
|
||||
"days": "días",
|
||||
"week": "semana",
|
||||
"weeks": "semanas",
|
||||
"month": "mes",
|
||||
"months": "meses",
|
||||
"year": "año",
|
||||
"years": "años",
|
||||
"Period": "Período",
|
||||
"Fixed date": "Fecha fija",
|
||||
"Indefinitely": "Indefinidamente",
|
||||
"Days": "Días",
|
||||
"Weeks": "Semanas",
|
||||
"Months": "Meses",
|
||||
"Years": "Años",
|
||||
"Pick a date": "Selecciona una fecha",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "El máximo es {{max}} {{unit}} para esta unidad",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Nunca caduca. Los verificadores pueden volver a verificar en cualquier momento.",
|
||||
"Verified": "Verificado",
|
||||
"Review needed": "Revisión necesaria",
|
||||
"Verification expired": "La verificación ha caducado",
|
||||
"Draft": "Borrador",
|
||||
"In Approval": "En aprobación",
|
||||
"In approval": "En aprobación",
|
||||
"Approved": "Aprobado",
|
||||
"Obsolete": "Obsoleto",
|
||||
"Expiring": "Próximo a caducar",
|
||||
"Set up verification": "Configurar verificación",
|
||||
"Verify page": "Verificar página",
|
||||
"Page verification": "Verificación de página",
|
||||
"Add verification": "Añadir verificación",
|
||||
"Edit verification": "Editar verificación",
|
||||
"Search by title": "Buscar por título",
|
||||
"Choose how this page should stay accurate.": "Elige cómo debe mantenerse precisa esta página.",
|
||||
"Recurring verification": "Verificación periódica",
|
||||
"Verifiers re-confirm this page on a schedule.": "Los verificadores vuelven a confirmar esta página según una programación.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Volver a verificar según una programación (p. ej., cada 30 días)",
|
||||
"Page stays editable at all times": "La página permanece editable en todo momento",
|
||||
"Best for runbooks, FAQs, living documentation": "Ideal para runbooks, preguntas frecuentes y documentación viva",
|
||||
"Approval workflow": "Flujo de aprobación",
|
||||
"Formal document lifecycle with named approvers.": "Ciclo de vida formal del documento con aprobadores designados.",
|
||||
"Draft → In approval → Approved → Obsolete": "Borrador → En aprobación → Aprobado → Obsoleto",
|
||||
"Locked once approved, with full history": "Bloqueado una vez aprobado, con historial completo",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Diseñado para ISO 9001, ISO 13485 y FDA",
|
||||
"Best for SOPs and controlled documents": "Ideal para SOP y documentos controlados",
|
||||
"Back": "Atrás",
|
||||
"Quality management": "Gestión de calidad",
|
||||
"Recurring": "Periódica",
|
||||
"Pages move through draft, approval, and approved stages.": "Las páginas pasan por las etapas de borrador, aprobación y aprobado.",
|
||||
"Verifiers": "Verificadores",
|
||||
"Add verifier": "Añadir verificador",
|
||||
"I've reviewed this page for accuracy": "He revisado la exactitud de esta página",
|
||||
"Set up": "Configurar",
|
||||
"Remove verification": "Eliminar verificación",
|
||||
"Are you sure you want to remove verification from this page?": "¿Seguro que quieres eliminar la verificación de esta página?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Los verificadores asignados deben volver a verificar esta página periódicamente.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Última verificación por {{name}} {{time}} (caducada)",
|
||||
"The fixed expiration date has passed.": "La fecha fija de vencimiento ya pasó.",
|
||||
"Verified by {{name}} {{time}}": "Verificado por {{name}} {{time}}",
|
||||
"Expires {{date}}": "Caduca el {{date}}",
|
||||
"Expired {{date}}": "Caducó el {{date}}",
|
||||
"Mark as obsolete": "Marcar como obsoleto",
|
||||
"Mark obsolete": "Marcar como obsoleto",
|
||||
"Returned by {{name}} {{time}}": "Devuelto por {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Aún no se ha solicitado aprobación.",
|
||||
"Submitted by {{name}} {{time}}": "Enviado por {{name}} {{time}}",
|
||||
"Someone": "Alguien",
|
||||
"Approved by {{name}} {{time}}": "Aprobado por {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Este documento ha sido marcado como obsoleto.",
|
||||
"Rejection comment": "Comentario de rechazo",
|
||||
"Reason for returning this document...": "Motivo de la devolución de este documento...",
|
||||
"Confirm rejection": "Confirmar rechazo",
|
||||
"Submit for approval": "Enviar para aprobación",
|
||||
"Reject": "Rechazar",
|
||||
"Approve": "Aprobar",
|
||||
"Re-submit for approval": "Volver a enviar para aprobación",
|
||||
"Verified until": "Verificado hasta",
|
||||
"QMS": "SGC",
|
||||
"Verified pages": "Páginas verificadas",
|
||||
"Search pages...": "Buscar páginas...",
|
||||
"Filter by space": "Filtrar por espacio",
|
||||
"Filter by type": "Filtrar por tipo",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> verificó una página",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> envió una página para tu aprobación",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> devolvió una página para revisión",
|
||||
"Page verification expires soon": "La verificación de la página caduca pronto",
|
||||
"Page verification has expired": "La verificación de la página ha caducado",
|
||||
"Verifying your email": "Verificando tu correo electrónico",
|
||||
"Please wait...": "Por favor, espera...",
|
||||
"Verification failed. The link may have expired.": "La verificación ha fallado. Es posible que el enlace haya expirado.",
|
||||
"Check your email": "Revisa tu correo electrónico",
|
||||
"We sent a verification link to {{email}}.": "Te enviamos un enlace de verificación a {{email}}.",
|
||||
"We sent a verification link to your email.": "Te enviamos un enlace de verificación a tu correo.",
|
||||
"Click the link to verify your email and access your workspace.": "Haz clic en el enlace para verificar tu correo electrónico y acceder a tu espacio de trabajo.",
|
||||
"Resend verification email": "Reenviar correo de verificación",
|
||||
"Verification email sent. Please check your inbox.": "Correo de verificación enviado. Por favor, revisa tu bandeja de entrada.",
|
||||
"Failed to resend verification email. Please try again.": "No se pudo reenviar el correo de verificación. Por favor, intente de nuevo.",
|
||||
"We've sent you an email with your associated workspaces.": "Te hemos enviado un correo electrónico con tus espacios de trabajo asociados.",
|
||||
"Load more": "Cargar más",
|
||||
"Log out of all devices": "Cerrar sesión en todos los dispositivos",
|
||||
"Log out of all sessions except this device": "Cerrar sesión en todas las sesiones excepto en este dispositivo",
|
||||
"This Device": "Este dispositivo",
|
||||
"Unknown device": "Dispositivo desconocido",
|
||||
"No active sessions": "No hay sesiones activas",
|
||||
"Session revoked": "Sesión revocada",
|
||||
"All other sessions revoked": "Todas las demás sesiones revocadas",
|
||||
"Last used": "Último uso",
|
||||
"Created": "Creado",
|
||||
"Rename": "Renombrar",
|
||||
"Publish": "Publicar",
|
||||
"Security": "Seguridad",
|
||||
"Enforce SSO": "Exigir SSO",
|
||||
"Once enforced, members will not be able to login with email and password.": "Una vez que se exija, los miembros no podrán iniciar sesión con correo electrónico y contraseña.",
|
||||
"AI-generated content may not be accurate.": "Es posible que el contenido generado por IA no sea preciso.",
|
||||
"AI Chat": "Chat de IA",
|
||||
"Analyze for insights": "Analizar para obtener información",
|
||||
"Ask anything...": "Pregunta lo que quieras...",
|
||||
"Chat history": "Historial de chat",
|
||||
"Chat name": "Nombre del chat",
|
||||
"Close": "Cerrar",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "No se pudo cargar el chat. Se produjo un error.",
|
||||
"Failed to render this message.": "No se pudo mostrar este mensaje.",
|
||||
"How can I help you today?": "¿Cómo puedo ayudarte hoy?",
|
||||
"New chat": "Nuevo chat",
|
||||
"No chat history": "No hay historial de chat",
|
||||
"No chats found": "No se encontraron chats",
|
||||
"No conversations yet": "Aún no hay conversaciones",
|
||||
"Open full page": "Abrir página completa",
|
||||
"Previous 7 days": "Últimos 7 días",
|
||||
"Previous 30 days": "Últimos 30 días",
|
||||
"Search chats...": "Buscar chats...",
|
||||
"Start a new chat to see it here.": "Inicia un nuevo chat para verlo aquí.",
|
||||
"Summarize this page": "Resumir esta página",
|
||||
"Toggle AI Chat": "Alternar chat de IA",
|
||||
"Translate this page": "Traducir esta página",
|
||||
"Try a different search term.": "Prueba con otro término de búsqueda.",
|
||||
"Try again": "Intentar de nuevo",
|
||||
"Untitled chat": "Chat sin título",
|
||||
"What can I help you with?": "¿En qué puedo ayudarte?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Ajouter des membres",
|
||||
"Add to groups": "Ajouter aux groupes",
|
||||
"Add space members": "Ajouter des membres à l'espace",
|
||||
"Add to favorites": "Ajouter aux favoris",
|
||||
"Admin": "Admin",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Êtes-vous sûr de vouloir supprimer ce groupe ? Les membres perdront l'accès aux ressources auxquelles ce groupe a accès.",
|
||||
"Are you sure you want to delete this page?": "Êtes-vous sûr de vouloir supprimer cette page ?",
|
||||
@@ -44,24 +45,24 @@
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Êtes-vous sûr de vouloir supprimer cette page ? Cela supprimera ses enfants et l'historique de la page. Cette action est irréversible.",
|
||||
"Description": "Description",
|
||||
"Details": "Détails",
|
||||
"e.g ACME": "par ex. ACME",
|
||||
"e.g ACME Inc": "par ex. ACME Inc",
|
||||
"e.g Developers": "par ex. Développeurs",
|
||||
"e.g Group for developers": "par ex. Groupe pour développeurs",
|
||||
"e.g product": "par ex. produit",
|
||||
"e.g Product Team": "par ex. Équipe Produit",
|
||||
"e.g Sales": "par ex. Ventes",
|
||||
"e.g Space for product team": "par ex. Espace pour l'équipe produit",
|
||||
"e.g Space for sales team to collaborate": "par ex. Espace pour l'équipe de vente pour collaborer",
|
||||
"e.g ACME": "p. ex. ACME",
|
||||
"e.g ACME Inc": "p. ex. ACME Inc",
|
||||
"e.g Developers": "p. ex. Développeurs",
|
||||
"e.g Group for developers": "p. ex. Groupe pour les développeurs",
|
||||
"e.g product": "p. ex. produit",
|
||||
"e.g Product Team": "p. ex. Équipe produit",
|
||||
"e.g Sales": "p. ex. Ventes",
|
||||
"e.g Space for product team": "p. ex. Espace pour l’équipe produit",
|
||||
"e.g Space for sales team to collaborate": "p. ex. Espace pour que l’équipe commerciale collabore",
|
||||
"Edit": "Modifier",
|
||||
"Read": "Lire",
|
||||
"Edit group": "Modifier groupe",
|
||||
"Email": "Email",
|
||||
"Enter a strong password": "Entrez un mot de passe fort",
|
||||
"Enter valid email addresses separated by comma or space max_50": "Entrez des adresses email valides séparées par une virgule ou un espace [max : 50]",
|
||||
"enter valid emails addresses": "entrez des adresses email valides",
|
||||
"enter valid emails addresses": "saisissez des adresses e-mail valides",
|
||||
"Enter your current password": "Entrez votre mot de passe actuel",
|
||||
"enter your full name": "entrez votre nom complet",
|
||||
"enter your full name": "saisissez votre nom complet",
|
||||
"Enter your new password": "Entrez votre nouveau mot de passe",
|
||||
"Enter your new preferred email": "Entrez votre nouvel email préféré",
|
||||
"Enter your password": "Entrez votre mot de passe",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Échec de l'importation des pages",
|
||||
"Failed to load page. An error occurred.": "Échec du chargement de la page. Une erreur s'est produite.",
|
||||
"Failed to update data": "Échec de la mise à jour des données",
|
||||
"Favorite spaces": "Espaces favoris",
|
||||
"Favorite spaces appear here": "Les espaces favoris apparaissent ici",
|
||||
"Favorites": "Favoris",
|
||||
"Full access": "Accès complet",
|
||||
"Full page width": "Largeur de page complète",
|
||||
"Full width": "Largeur complète",
|
||||
@@ -87,11 +91,12 @@
|
||||
"Import pages": "Importer des pages",
|
||||
"Import pages & space settings": "Importer des pages et paramètres de l'espace",
|
||||
"Importing pages": "Importation des pages",
|
||||
"invalid invitation link": "lien d'invitation invalide",
|
||||
"invalid invitation link": "lien d’invitation invalide",
|
||||
"Invitation signup": "Inscription par invitation",
|
||||
"Invite by email": "Inviter par email",
|
||||
"Invite members": "Inviter des membres",
|
||||
"Invite new members": "Inviter de nouveaux membres",
|
||||
"Invite People": "Inviter des personnes",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Les membres invités qui n'ont pas encore accepté leur invitation apparaîtront ici.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Les membres invités auront accès aux espaces auxquels les groupes peuvent accéder",
|
||||
"Join the workspace": "Rejoindre l'espace de travail",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "Aucun groupe trouvé",
|
||||
"No page history saved yet.": "Aucun historique de la page enregistré pour l'instant.",
|
||||
"No pages yet": "Aucune page pour l'instant",
|
||||
"No shared pages": "Aucune page partagée",
|
||||
"No results found...": "Aucun résultat trouvé...",
|
||||
"No user found": "Aucun utilisateur trouvé",
|
||||
"Overview": "Vue d'ensemble",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "pages",
|
||||
"Password": "Mot de passe",
|
||||
"Password changed successfully": "Mot de passe changé avec succès",
|
||||
"People": "Personnes",
|
||||
"Pending": "En attente",
|
||||
"Please confirm your action": "Veuillez confirmer votre action",
|
||||
"Preferences": "Préférences",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Profil",
|
||||
"Recently updated": "Récemment mis à jour",
|
||||
"Remove": "Retirer",
|
||||
"Remove from favorites": "Retirer des favoris",
|
||||
"Remove group member": "Retirer un membre du groupe",
|
||||
"Remove space member": "Retirer un membre de l'espace",
|
||||
"Restore": "Restaurer",
|
||||
@@ -149,53 +157,54 @@
|
||||
"Search...": "Rechercher...",
|
||||
"Select language": "Sélectionner la langue",
|
||||
"Select role": "Sélectionner un rôle",
|
||||
"Select role to assign to all invited members": "Sélectionner le rôle à attribuer à tous les membres invités",
|
||||
"Select role to assign to all invited members": "Sélectionnez le rôle à attribuer à tous les membres invités",
|
||||
"Select theme": "Sélectionner le thème",
|
||||
"Send invitation": "Envoyer l'invitation",
|
||||
"Send invitation": "Envoyer l’invitation",
|
||||
"Invitation sent": "Invitation envoyée",
|
||||
"Settings": "Paramètres",
|
||||
"Setup workspace": "Configurer l'espace de travail",
|
||||
"Setup workspace": "Configurer l’espace de travail",
|
||||
"Sign In": "Se connecter",
|
||||
"Sign Up": "S'inscrire",
|
||||
"Sign Up": "S’inscrire",
|
||||
"Slug": "Slug",
|
||||
"Space": "Espace",
|
||||
"Space description": "Description de l'espace",
|
||||
"Space menu": "Menu de l'espace",
|
||||
"Space name": "Nom de l'espace",
|
||||
"Space settings": "Paramètres de l'espace",
|
||||
"Space slug": "Slug de l'espace",
|
||||
"Space description": "Description de l’espace",
|
||||
"Space menu": "Menu de l’espace",
|
||||
"Space name": "Nom de l’espace",
|
||||
"Space settings": "Paramètres de l’espace",
|
||||
"Space slug": "Slug de l’espace",
|
||||
"Spaces": "Espaces",
|
||||
"Spaces you belong to": "Espaces auxquels vous appartenez",
|
||||
"No space found": "Aucun espace trouvé",
|
||||
"Search for spaces": "Rechercher des espaces",
|
||||
"Start typing to search...": "Commencez à taper pour rechercher...",
|
||||
"Status": "Statut",
|
||||
"Successfully imported": "Importé avec succès",
|
||||
"Successfully restored": "Restauré avec succès",
|
||||
"Successfully imported": "Importation réussie",
|
||||
"Successfully restored": "Restauration réussie",
|
||||
"System settings": "Paramètres système",
|
||||
"Templates": "Modèles",
|
||||
"Theme": "Thème",
|
||||
"To change your email, you have to enter your password and new email.": "Pour changer votre email, vous devez entrer votre mot de passe et votre nouvel email.",
|
||||
"Toggle full page width": "Basculer sur la largeur complète de la page",
|
||||
"Toggle full page width": "Basculer la largeur complète de la page",
|
||||
"Unable to import pages. Please try again.": "Impossible d'importer les pages. Veuillez réessayer.",
|
||||
"untitled": "sans titre",
|
||||
"Untitled": "Sans titre",
|
||||
"Updated successfully": "Mis à jour avec succès",
|
||||
"Updated successfully": "Mise à jour réussie",
|
||||
"User": "Utilisateur",
|
||||
"Workspace": "Espace de travail",
|
||||
"Workspace Name": "Nom de l'espace de travail",
|
||||
"Workspace settings": "Paramètres de l'espace de travail",
|
||||
"Workspace Name": "Nom de l’espace de travail",
|
||||
"Workspace settings": "Paramètres de l’espace de travail",
|
||||
"You can change your password here.": "Vous pouvez changer votre mot de passe ici.",
|
||||
"Your Email": "Votre Email",
|
||||
"Your Email": "Votre e-mail",
|
||||
"Your import is complete.": "Votre importation est terminée.",
|
||||
"Your name": "Votre nom",
|
||||
"Your Name": "Votre Nom",
|
||||
"Your Name": "Votre nom",
|
||||
"Your password": "Votre mot de passe",
|
||||
"Your password must be a minimum of 8 characters.": "Votre mot de passe doit contenir au moins 8 caractères.",
|
||||
"Sidebar toggle": "Bascule de la barre latérale",
|
||||
"Sidebar toggle": "Basculer la barre latérale",
|
||||
"Comments": "Commentaires",
|
||||
"404 page not found": "404 page non trouvée",
|
||||
"Sorry, we can't find the page you are looking for.": "Désolé, nous ne pouvons pas trouver la page que vous cherchez.",
|
||||
"Take me back to homepage": "Ramenez-moi à la page d'accueil",
|
||||
"Take me back to homepage": "Retour à la page d’accueil",
|
||||
"Forgot password": "Mot de passe oublié",
|
||||
"Forgot your password?": "Mot de passe oublié?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "Un lien de réinitialisation de mot de passe a été envoyé à votre e-mail. Veuillez vérifier votre boîte de réception.",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Répondre...",
|
||||
"Error loading comments.": "Erreur lors du chargement des commentaires.",
|
||||
"No comments yet.": "Pas de commentaires pour l'instant.",
|
||||
"No open comments.": "Aucun commentaire ouvert.",
|
||||
"No resolved comments.": "Aucun commentaire résolu.",
|
||||
"Add a comment...": "Ajouter un commentaire...",
|
||||
"Edit comment": "Modifier le commentaire",
|
||||
"Delete comment": "Supprimer le commentaire",
|
||||
"Are you sure you want to delete this comment?": "Êtes-vous sûr de vouloir supprimer ce commentaire ?",
|
||||
"Delete chat": "Supprimer la conversation",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Êtes-vous sûr de vouloir supprimer '{{title}}' ? Cette action est irréversible.",
|
||||
"Comment created successfully": "Commentaire créé avec succès",
|
||||
"Error creating comment": "Erreur lors de la création du commentaire",
|
||||
"Comment updated successfully": "Commentaire mis à jour avec succès",
|
||||
@@ -218,17 +232,16 @@
|
||||
"Failed to delete comment": "Échec de la suppression du commentaire",
|
||||
"Comment resolved successfully": "Commentaire résolu avec succès",
|
||||
"Comment re-opened successfully": "Commentaire rouvert avec succès",
|
||||
"Comment unresolved successfully": "Commentaire non résolu avec succès",
|
||||
"Comment unresolved successfully": "Commentaire marqué comme non résolu avec succès",
|
||||
"Failed to resolve comment": "Échec de la résolution du commentaire",
|
||||
"Resolve comment": "Résoudre le commentaire",
|
||||
"Unresolve comment": "Désorganiser le commentaire",
|
||||
"Unresolve comment": "Marquer le commentaire comme non résolu",
|
||||
"Resolve Comment Thread": "Résoudre le fil de commentaires",
|
||||
"Unresolve Comment Thread": "Désorganiser le fil de commentaires",
|
||||
"Unresolve Comment Thread": "Marquer le fil de commentaires comme non résolu",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Êtes-vous sûr de vouloir résoudre ce fil de commentaires ? Cela le marquera comme terminé.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Êtes-vous sûr de vouloir désorganiser ce fil de commentaires ?",
|
||||
"Resolved": "Résolu",
|
||||
"No active comments.": "Aucun commentaire actif.",
|
||||
"No resolved comments.": "Aucun commentaire résolu.",
|
||||
"Revoke invitation": "Révoquer l'invitation",
|
||||
"Revoke": "Révoquer",
|
||||
"Don't": "Ne pas",
|
||||
@@ -237,7 +250,7 @@
|
||||
"Anyone with this link can join this workspace.": "Toute personne ayant ce lien peut rejoindre cet espace de travail.",
|
||||
"Invite link": "Lien d'invitation",
|
||||
"Copy": "Copier",
|
||||
"Copy to space": "Copier dans l'espace",
|
||||
"Copy to space": "Copier vers l’espace",
|
||||
"Copied": "Copié",
|
||||
"Duplicate": "Dupliquer",
|
||||
"Select a user": "Sélectionner un utilisateur",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "Êtes-vous sûr de vouloir supprimer cet espace ?",
|
||||
"Delete this space with all its pages and data.": "Supprimer cet espace avec toutes ses pages et données.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "Toutes les pages, commentaires, pièces jointes et autorisations dans cet espace seront supprimés irréversiblement.",
|
||||
"Confirm space name": "Confirmer le nom de l'espace",
|
||||
"Confirm space name": "Confirmer le nom de l’espace",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Tapez le nom de l'espace <b>{{spaceName}}</b> pour confirmer votre action.",
|
||||
"Format": "Format",
|
||||
"Include subpages": "Inclure les sous-pages",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Ajouter une ligne en dessous",
|
||||
"Delete table": "Supprimer le tableau",
|
||||
"Info": "Info",
|
||||
"Note": "Remarque",
|
||||
"Success": "Succès",
|
||||
"Warning": "Avertissement",
|
||||
"Danger": "Danger",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Enregistrer & Quitter",
|
||||
"Double-click to edit Excalidraw diagram": "Double-cliquez pour modifier le diagramme Excalidraw",
|
||||
"Paste link": "Coller le lien",
|
||||
"Paste link or search pages": "Coller le lien ou rechercher des pages",
|
||||
"Link to web page": "Lien vers une page web",
|
||||
"Recents": "Récents",
|
||||
"Page or URL": "Page ou URL",
|
||||
"Link title": "Titre du lien",
|
||||
"Edit link": "Modifier le lien",
|
||||
"Remove link": "Supprimer le lien",
|
||||
"Add link": "Ajouter un lien",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Rose",
|
||||
"Gray": "Gris",
|
||||
"Embed link": "Intégrer un lien",
|
||||
"Invalid {{provider}} embed link": "Lien d'intégration {{provider}} non valide",
|
||||
"Invalid {{provider}} embed link": "Lien d’intégration {{provider}} invalide",
|
||||
"Embed {{provider}}": "Intégrer {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Entrez le lien {{provider}} à intégrer",
|
||||
"Bold": "Gras",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Insérer un séparateur de règle horizontale",
|
||||
"Upload any image from your device.": "Téléchargez n'importe quelle image depuis votre appareil.",
|
||||
"Upload any video from your device.": "Téléchargez n'importe quelle vidéo depuis votre appareil.",
|
||||
"Upload any audio from your device.": "Téléchargez n'importe quel fichier audio depuis votre appareil.",
|
||||
"Upload any file from your device.": "Téléchargez n'importe quel fichier depuis votre appareil.",
|
||||
"Uploading {{name}}": "Téléchargement de {{name}}",
|
||||
"Uploading file": "Téléchargement du fichier",
|
||||
@@ -338,16 +358,22 @@
|
||||
"Insert a table.": "Insérez un tableau.",
|
||||
"Insert collapsible block.": "Insérer un bloc repliable.",
|
||||
"Video": "Vidéo",
|
||||
"Divider": "Diviseur",
|
||||
"Divider": "Séparateur",
|
||||
"Quote": "Citation",
|
||||
"Image": "Image",
|
||||
"File attachment": "Pièce jointe",
|
||||
"Toggle block": "Basculer le bloc",
|
||||
"Callout": "Appel",
|
||||
"Audio": "Audio",
|
||||
"Embed PDF": "Intégrer un PDF",
|
||||
"Upload and embed a PDF file.": "Téléchargez et intégrez un fichier PDF.",
|
||||
"Embed as PDF": "Intégrer comme PDF",
|
||||
"Failed to load PDF": "Échec du chargement du PDF",
|
||||
"Convert to attachment": "Convertir en pièce jointe",
|
||||
"File attachment": "Fichier joint",
|
||||
"Toggle block": "Bloc basculable",
|
||||
"Callout": "Encadré",
|
||||
"Insert callout notice.": "Insérer un avis d'appel.",
|
||||
"Math inline": "Mathématiques en ligne",
|
||||
"Math inline": "Maths en ligne",
|
||||
"Insert inline math equation.": "Insérez une équation mathématique en ligne.",
|
||||
"Math block": "Bloc mathématiques",
|
||||
"Math block": "Bloc mathématique",
|
||||
"Insert math equation": "Insérer une équation mathématique",
|
||||
"Mermaid diagram": "Diagramme Mermaid",
|
||||
"Insert mermaid diagram": "Insérer un diagramme Mermaid",
|
||||
@@ -355,11 +381,25 @@
|
||||
"Insert current date": "Insérer la date actuelle",
|
||||
"Draw and sketch excalidraw diagrams": "Dessiner et esquisser des diagrammes Excalidraw",
|
||||
"Multiple": "Multiple",
|
||||
"Turn into": "Transformer en",
|
||||
"Text align": "Alignement du texte",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Cette page a peut-être été supprimée, déplacée ou vous n'y avez peut-être pas accès.",
|
||||
"Go to homepage": "Aller à l'accueil",
|
||||
"Pages you create will show up here.": "Les pages que vous créez apparaîtront ici.",
|
||||
"Heading {{level}}": "Titre {{level}}",
|
||||
"Toggle title": "Basculer le titre",
|
||||
"Write anything. Enter \"/\" for commands": "Écrivez n'importe quoi. Entrez \"/\" pour les commandes",
|
||||
"Toggle title": "Titre du bloc basculable",
|
||||
"Write anything. Enter \"/\" for commands": "Écrivez n’importe quoi. Saisissez \"/\" pour les commandes",
|
||||
"Write...": "Écrire...",
|
||||
"Column count": "Nombre de colonnes",
|
||||
"{{count}} Columns": "{count, plural, one {# colonne} other {# colonnes}}",
|
||||
"Equal columns": "Colonnes égales",
|
||||
"Left sidebar": "Barre latérale gauche",
|
||||
"Right sidebar": "Barre latérale droite",
|
||||
"Wide center": "Large au centre",
|
||||
"Left wide": "Large à gauche",
|
||||
"Right wide": "Large à droite",
|
||||
"Names do not match": "Les noms ne correspondent pas",
|
||||
"Today, {{time}}": "Aujourd'hui, {{time}}",
|
||||
"Today, {{time}}": "Aujourd’hui, {{time}}",
|
||||
"Yesterday, {{time}}": "Hier, {{time}}",
|
||||
"Space created successfully": "Espace créé avec succès",
|
||||
"Space updated successfully": "Espace mis à jour avec succès",
|
||||
@@ -368,35 +408,42 @@
|
||||
"Member removed successfully": "Membre supprimé avec succès",
|
||||
"Member role updated successfully": "Rôle du membre mis à jour avec succès",
|
||||
"Created by: <b>{{creatorName}}</b>": "Créé par : <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Créé à : {{time}}",
|
||||
"Created at: {{time}}": "Créé le : {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Modifié par {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Nombre de mots : {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Nombre de caractères : {{characterCount}}",
|
||||
"New update": "Nouvelle mise à jour",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} est disponible",
|
||||
"Default page edit mode": "Mode d'édition de page par défaut",
|
||||
"Default page edit mode": "Mode d’édition par défaut de la page",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Choisissez votre mode d'édition de page préféré. Évitez les modifications accidentelles.",
|
||||
"Reading": "Lecture",
|
||||
"Delete member": "Supprimer le membre",
|
||||
"Member deleted successfully": "Membre supprimé avec succès",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Êtes-vous sûr de vouloir supprimer ce membre de l'espace de travail? Cette action est irréversible.",
|
||||
"Deactivate member": "Désactiver le membre",
|
||||
"Activate member": "Activer le membre",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Êtes-vous sûr de vouloir désactiver ce membre de l'espace de travail ? Cette personne ne pourra plus accéder à cet espace de travail.",
|
||||
"Are you sure you want to activate this workspace member?": "Êtes-vous sûr de vouloir activer ce membre de l'espace de travail ?",
|
||||
"Deactivate": "Désactiver",
|
||||
"Activate": "Activer",
|
||||
"Deactivated": "Désactivé",
|
||||
"Move": "Déplacer",
|
||||
"Move page": "Déplacer la page",
|
||||
"Move page to a different space.": "Déplacer la page vers un autre espace.",
|
||||
"Real-time editor connection lost. Retrying...": "Connexion avec l'éditeur en temps réel perdue. Nouvelle tentative...",
|
||||
"Table of contents": "",
|
||||
"Table of contents": "Table des matières",
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Ajoutez des titres (H1, H2, H3) pour générer une table des matières.",
|
||||
"Share": "Partager",
|
||||
"Public sharing": "Partage public",
|
||||
"Shared by": "Partagé par",
|
||||
"Shared at": "Partagé à",
|
||||
"Shared at": "Partagé le",
|
||||
"Inherits public sharing from": "Hérite du partage public de",
|
||||
"Share to web": "Partager sur le web",
|
||||
"Shared to web": "Partagé sur le web",
|
||||
"Anyone with the link can view this page": "Toute personne avec le lien peut voir cette page",
|
||||
"Make this page publicly accessible": "Rendre cette page accessible au public",
|
||||
"Anyone with the link can view this page": "Toute personne disposant du lien peut voir cette page",
|
||||
"Make this page publicly accessible": "Rendre cette page accessible publiquement",
|
||||
"Include sub-pages": "Inclure les sous-pages",
|
||||
"Make sub-pages public too": "Rendre également les sous-pages publiques",
|
||||
"Make sub-pages public too": "Rendre aussi les sous-pages publiques",
|
||||
"Allow search engines to index page": "Autoriser les moteurs de recherche à indexer la page",
|
||||
"Open page": "Ouvrir la page",
|
||||
"Page": "Page",
|
||||
@@ -405,91 +452,111 @@
|
||||
"Are you sure you want to delete this shared link?": "Êtes-vous sûr de vouloir supprimer ce lien partagé ?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Les pages partagées publiquement des espaces dont vous êtes membre apparaîtront ici",
|
||||
"Share deleted successfully": "Partage supprimé avec succès",
|
||||
"Share not found": "Partage non trouvé",
|
||||
"Share not found": "Partage introuvable",
|
||||
"Failed to share page": "Échec du partage de la page",
|
||||
"Disable public sharing": "Désactiver le partage public",
|
||||
"Prevent members from sharing pages publicly.": "Empêcher les membres de partager des pages publiquement.",
|
||||
"Toggle public sharing": "Basculer le partage public",
|
||||
"Toggle space public sharing": "Basculer le partage public de l'espace",
|
||||
"Allow viewers to comment": "Autoriser les spectateurs à commenter",
|
||||
"Allow viewers to add comments on pages in this space.": "Autoriser les spectateurs à ajouter des commentaires sur les pages de cet espace.",
|
||||
"Toggle viewer comments": "Basculer les commentaires des spectateurs",
|
||||
"Public sharing is disabled at the workspace level": "Le partage public est désactivé au niveau de l'espace de travail",
|
||||
"Prevent pages in this space from being shared publicly.": "Empêcher les pages de cet espace d'être partagées publiquement.",
|
||||
"Page permissions": "Autorisations de la page",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Contrôlez qui peut consulter et modifier chaque page. Disponible avec une licence Entreprise.",
|
||||
"Enable public sharing": "Activer le partage public",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Êtes-vous sûr de vouloir activer le partage public ? Les membres pourront partager des pages publiquement.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Êtes-vous sûr de vouloir désactiver le partage public ? Tous les liens partagés existants dans cet espace de travail seront supprimés.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Êtes-vous sûr de vouloir activer le partage public pour cet espace ?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Êtes-vous sûr de vouloir désactiver le partage public ? Tous les liens partagés existants dans cet espace seront supprimés.",
|
||||
"Public sharing is disabled": "Le partage public est désactivé",
|
||||
"Public sharing has been disabled at the workspace level.": "Le partage public a été désactivé au niveau de l'espace de travail.",
|
||||
"Public sharing has been disabled for this space.": "Le partage public a été désactivé pour cet espace.",
|
||||
"Copy page": "Copier la page",
|
||||
"Copy page to a different space.": "Copier la page dans un autre espace.",
|
||||
"Page copied successfully": "Page copiée avec succès",
|
||||
"Page duplicated successfully": "Page dupliquée avec succès",
|
||||
"Find": "Trouver",
|
||||
"Not found": "Non trouvé",
|
||||
"Previous Match (Shift+Enter)": "Correspondance précédente (Shift+Entrée)",
|
||||
"Next match (Enter)": "Correspondance suivante (Entrée)",
|
||||
"Find": "Rechercher",
|
||||
"Not found": "Introuvable",
|
||||
"Previous Match (Shift+Enter)": "Occurrence précédente (Maj+Entrée)",
|
||||
"Next match (Enter)": "Occurrence suivante (Entrée)",
|
||||
"Match case (Alt+C)": "Respecter la casse (Alt+C)",
|
||||
"Replace": "Remplacer",
|
||||
"Close (Escape)": "Fermer (Échapper)",
|
||||
"Close (Escape)": "Fermer (Échap)",
|
||||
"Replace (Enter)": "Remplacer (Entrée)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Tout remplacer (Ctrl+Alt+Entrée)",
|
||||
"Replace all": "Tout remplacer",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Remplacer tout (Ctrl+Alt+Entrée)",
|
||||
"Replace all": "Remplacer tout",
|
||||
"View all": "Voir tout",
|
||||
"View all spaces": "Voir tous les espaces",
|
||||
"Error": "Erreur",
|
||||
"Failed to disable MFA": "Impossible de désactiver l'A2F",
|
||||
"Disable two-factor authentication": "Désactiver l'authentification à deux facteurs",
|
||||
"Failed to disable MFA": "Échec de la désactivation de l’authentification multifacteur",
|
||||
"Disable two-factor authentication": "Désactiver l’authentification à deux facteurs",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "La désactivation de l'authentification à deux facteurs rendra votre compte moins sécurisé. Vous n'aurez besoin que de votre mot de passe pour vous connecter.",
|
||||
"Please enter your password to disable two-factor authentication:": "Veuillez entrer votre mot de passe pour désactiver l'authentification à deux facteurs :",
|
||||
"Two-factor authentication has been enabled": "L'authentification à deux facteurs a été activée",
|
||||
"Two-factor authentication has been disabled": "L'authentification à deux facteurs a été désactivée",
|
||||
"Two-factor authentication has been enabled": "L’authentification à deux facteurs a été activée",
|
||||
"Two-factor authentication has been disabled": "L’authentification à deux facteurs a été désactivée",
|
||||
"2-step verification": "Vérification en 2 étapes",
|
||||
"Protect your account with an additional verification layer when signing in.": "Protégez votre compte avec une couche de vérification supplémentaire lors de la connexion.",
|
||||
"Two-factor authentication is active on your account.": "L'authentification à deux facteurs est active sur votre compte.",
|
||||
"Add 2FA method": "Ajouter une méthode A2F",
|
||||
"Backup codes": "Codes de sauvegarde",
|
||||
"Add 2FA method": "Ajouter une méthode d’A2F",
|
||||
"Backup codes": "Codes de secours",
|
||||
"Disable": "Désactiver",
|
||||
"Invalid verification code": "Code de vérification invalide",
|
||||
"New backup codes have been generated": "De nouveaux codes de sauvegarde ont été générés",
|
||||
"Failed to regenerate backup codes": "Échec de la régénération des codes de sauvegarde",
|
||||
"About backup codes": "À propos des codes de sauvegarde",
|
||||
"New backup codes have been generated": "De nouveaux codes de secours ont été générés",
|
||||
"Failed to regenerate backup codes": "Échec de la régénération des codes de secours",
|
||||
"About backup codes": "À propos des codes de secours",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Les codes de sauvegarde peuvent être utilisés pour accéder à votre compte si vous perdez l'accès à votre application d'authentification. Chaque code ne peut être utilisé qu'une seule fois.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Vous pouvez régénérer de nouveaux codes de sauvegarde à tout moment. Cela invalidera tous les codes existants.",
|
||||
"Confirm password": "Confirmer le mot de passe",
|
||||
"Generate new backup codes": "Générer de nouveaux codes de sauvegarde",
|
||||
"Save your new backup codes": "Enregistrez vos nouveaux codes de sauvegarde",
|
||||
"Generate new backup codes": "Générer de nouveaux codes de secours",
|
||||
"Save your new backup codes": "Enregistrez vos nouveaux codes de secours",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Assurez-vous d'enregistrer ces codes dans un endroit sécurisé. Vos anciens codes de sauvegarde ne sont plus valides.",
|
||||
"Your new backup codes": "Vos nouveaux codes de sauvegarde",
|
||||
"I've saved my backup codes": "J'ai enregistré mes codes de sauvegarde",
|
||||
"Failed to setup MFA": "Échec de la configuration de l'A2F",
|
||||
"Your new backup codes": "Vos nouveaux codes de secours",
|
||||
"I've saved my backup codes": "J’ai enregistré mes codes de secours",
|
||||
"Failed to setup MFA": "Échec de la configuration de l’authentification multifacteur",
|
||||
"Setup & Verify": "Configurer et vérifier",
|
||||
"Add to authenticator": "Ajouter à l'authentification",
|
||||
"1. Scan this QR code with your authenticator app": "1. Scannez ce code QR avec votre application d'authentification",
|
||||
"Add to authenticator": "Ajouter à l’application d’authentification",
|
||||
"1. Scan this QR code with your authenticator app": "1. Scannez ce code QR avec votre application d’authentification",
|
||||
"Can't scan the code?": "Impossible de scanner le code ?",
|
||||
"Enter this code manually in your authenticator app:": "Entrez ce code manuellement dans votre application d'authentification :",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Entrez le code à 6 chiffres de votre authentificateur",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Saisissez le code à 6 chiffres de votre application d’authentification",
|
||||
"Verify and enable": "Vérifier et activer",
|
||||
"Failed to generate QR code. Please try again.": "Échec de la génération du code QR. Veuillez réessayer.",
|
||||
"Backup": "Sauvegarde",
|
||||
"Backup": "Secours",
|
||||
"Save codes": "Enregistrer les codes",
|
||||
"Save your backup codes": "Enregistrez vos codes de sauvegarde",
|
||||
"Save your backup codes": "Enregistrez vos codes de secours",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Ces codes peuvent être utilisés pour accéder à votre compte si vous perdez l'accès à votre application d'authentification. Chaque code ne peut être utilisé qu'une seule fois.",
|
||||
"Print": "Imprimer",
|
||||
"Two-factor authentication has been set up. Please log in again.": "L'authentification à deux facteurs a été configurée. Veuillez vous reconnecter.",
|
||||
"Two-Factor authentication required": "Authentification à deux facteurs requise",
|
||||
"Your workspace requires two-factor authentication for all users": "Votre espace de travail nécessite l'authentification à deux facteurs pour tous les utilisateurs",
|
||||
"Your workspace requires two-factor authentication for all users": "Votre espace de travail exige l’authentification à deux facteurs pour tous les utilisateurs",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Pour continuer à accéder à votre espace de travail, vous devez configurer l'authentification à deux facteurs. Cela ajoute une couche de sécurité supplémentaire à votre compte.",
|
||||
"Set up two-factor authentication": "Configurer l'authentification à deux facteurs",
|
||||
"Set up two-factor authentication": "Configurer l’authentification à deux facteurs",
|
||||
"Cancel and logout": "Annuler et se déconnecter",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Votre espace de travail nécessite l'authentification à deux facteurs. Veuillez le configurer pour continuer.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Cela ajoute une couche de sécurité supplémentaire à votre compte en exigeant un code de vérification provenant de votre application d'authentification.",
|
||||
"Password is required": "Mot de passe requis",
|
||||
"Password is required": "Le mot de passe est requis",
|
||||
"Password must be at least 8 characters": "Le mot de passe doit comporter au moins 8 caractères",
|
||||
"Please enter a 6-digit code": "Veuillez entrer un code à 6 chiffres",
|
||||
"Code must be exactly 6 digits": "Le code doit être exactement de 6 chiffres",
|
||||
"Enter the 6-digit code found in your authenticator app": "Entrez le code à 6 chiffres trouvé dans votre application d'authentification",
|
||||
"Please enter a 6-digit code": "Veuillez saisir un code à 6 chiffres",
|
||||
"Code must be exactly 6 digits": "Le code doit comporter exactement 6 chiffres",
|
||||
"Enter the 6-digit code found in your authenticator app": "Saisissez le code à 6 chiffres indiqué dans votre application d’authentification",
|
||||
"Need help authenticating?": "Besoin d'aide pour l'authentification ?",
|
||||
"MFA QR Code": "Code QR de l'A2F",
|
||||
"MFA QR Code": "Code QR d’authentification multifacteur",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Compte créé avec succès. Veuillez vous connecter pour configurer l'authentification à deux facteurs.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "Réinitialisation du mot de passe réussie. Veuillez vous connecter avec votre nouveau mot de passe et compléter l'authentification à deux facteurs.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Réinitialisation du mot de passe réussie. Veuillez vous connecter avec votre nouveau mot de passe pour configurer l'authentification à deux facteurs.",
|
||||
"Password reset was successful. Please log in with your new password.": "La réinitialisation du mot de passe a réussi. Veuillez vous connecter avec votre nouveau mot de passe.",
|
||||
"Two-factor authentication": "Authentification à deux facteurs",
|
||||
"Use authenticator app instead": "Utilisez l'application d'authentification à la place",
|
||||
"Verify backup code": "Vérifier le code de sauvegarde",
|
||||
"Use backup code": "Utiliser le code de sauvegarde",
|
||||
"Enter one of your backup codes": "Entrez un de vos codes de sauvegarde",
|
||||
"Backup code": "Code de sauvegarde",
|
||||
"Use authenticator app instead": "Utiliser l’application d’authentification à la place",
|
||||
"Verify backup code": "Vérifier le code de secours",
|
||||
"Use backup code": "Utiliser un code de secours",
|
||||
"Enter one of your backup codes": "Saisissez l’un de vos codes de secours",
|
||||
"Backup code": "Code de secours",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Entrez un de vos codes de sauvegarde. Chaque code de sauvegarde ne peut être utilisé qu'une seule fois.",
|
||||
"Verify": "Vérifier",
|
||||
"Trash": "Corbeille",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Les pages dans la corbeille seront définitivement supprimées après 30 jours.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "Les pages dans la corbeille seront définitivement supprimées après {{count}} jours.",
|
||||
"Deleted": "Supprimé",
|
||||
"No pages in trash": "Aucune page dans la corbeille",
|
||||
"Permanently delete page?": "Supprimer définitivement la page ?",
|
||||
@@ -501,12 +568,12 @@
|
||||
"Page moved to trash": "Page déplacée vers la corbeille",
|
||||
"Page restored successfully": "Page restaurée avec succès",
|
||||
"Deleted by": "Supprimé par",
|
||||
"Deleted at": "Supprimé à",
|
||||
"Deleted at": "Supprimé le",
|
||||
"Preview": "Aperçu",
|
||||
"Subpages": "Sous-pages",
|
||||
"Failed to load subpages": "Échec du chargement des sous-pages",
|
||||
"No subpages": "Pas de sous-pages",
|
||||
"Subpages (Child pages)": "Sous-pages (Pages enfants)",
|
||||
"No subpages": "Aucune sous-page",
|
||||
"Subpages (Child pages)": "Sous-pages (pages enfants)",
|
||||
"List all subpages of the current page": "Lister toutes les sous-pages de la page actuelle",
|
||||
"Attachments": "Pièces jointes",
|
||||
"All spaces": "Tous les espaces",
|
||||
@@ -516,24 +583,24 @@
|
||||
"Type": "Type",
|
||||
"Enterprise": "Entreprise",
|
||||
"Download attachment": "Télécharger la pièce jointe",
|
||||
"Allowed email domains": "Domaines de messagerie autorisés",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Seuls les utilisateurs possédant des adresses e-mail provenant de ces domaines peuvent s'inscrire via SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Entrez des noms de domaine valides séparés par une virgule ou un espace",
|
||||
"Enforce two-factor authentication": "Imposer l'authentification à deux facteurs",
|
||||
"Allowed email domains": "Domaines e-mail autorisés",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Seuls les utilisateurs disposant d’adresses e-mail provenant de ces domaines peuvent s’inscrire via l’authentification unique (SSO).",
|
||||
"Enter valid domain names separated by comma or space": "Saisissez des noms de domaine valides séparés par une virgule ou un espace",
|
||||
"Enforce two-factor authentication": "Imposer l’authentification à deux facteurs",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Une fois appliquée, tous les membres doivent activer l'authentification à deux facteurs pour accéder à l'espace de travail.",
|
||||
"Toggle MFA enforcement": "Basculer l'application de l'AMF",
|
||||
"Display name": "Nom d'affichage",
|
||||
"Allow signup": "Autoriser l'inscription",
|
||||
"Toggle MFA enforcement": "Basculer l’obligation d’authentification multifacteur",
|
||||
"Display name": "Nom d’affichage",
|
||||
"Allow signup": "Autoriser l’inscription",
|
||||
"Enabled": "Activé",
|
||||
"Advanced Settings": "Paramètres avancés",
|
||||
"Enable TLS/SSL": "Activer TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Utiliser une connexion sécurisée au serveur LDAP",
|
||||
"Group sync": "Synchronisation de groupe",
|
||||
"Group sync": "Synchronisation des groupes",
|
||||
"No SSO providers found.": "Aucun fournisseur SSO trouvé.",
|
||||
"Delete SSO provider": "Supprimer le fournisseur SSO",
|
||||
"Are you sure you want to delete this SSO provider?": "Êtes-vous sûr de vouloir supprimer ce fournisseur SSO ?",
|
||||
"Action": "Action",
|
||||
"{{ssoProviderType}} configuration": "Configuration {{ssoProviderType}}",
|
||||
"{{ssoProviderType}} configuration": "Configuration de {{ssoProviderType}}",
|
||||
"Icon": "Icône",
|
||||
"Upload image": "Téléverser une image",
|
||||
"Remove image": "Supprimer l'image",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Cette action ne peut pas être annulée. Toutes les applications utilisant cette clé API cesseront de fonctionner.",
|
||||
"Update API key": "Mettre à jour la clé API",
|
||||
"Manage API keys for all users in the workspace": "Gérer les clés API pour tous les utilisateurs dans l'espace de travail",
|
||||
"Restrict API key creation to admins": "Restreindre la création de clés API aux administrateurs",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Seuls les administrateurs et les propriétaires peuvent créer de nouvelles clés API. Les clés des membres existants continueront de fonctionner.",
|
||||
"Toggle restrict API keys to admins": "Activer ou désactiver la restriction des clés API aux administrateurs",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "La création de clés API est restreinte aux administrateurs par l’administrateur de votre espace de travail.",
|
||||
"AI settings": "Paramètres de l'IA",
|
||||
"AI search": "Recherche IA",
|
||||
"AI Answer": "Réponse IA",
|
||||
"Ask AI": "Demander à l'IA",
|
||||
"AI is thinking...": "L'IA réfléchit...",
|
||||
"Thinking": "Réflexion en cours",
|
||||
"Ask a question...": "Posez une question...",
|
||||
"AI-powered search (Ask AI)": "Recherche assistée par l'IA (Demander à l'IA)",
|
||||
"AI Answers": "Réponses IA",
|
||||
"AI-powered search (AI Answers)": "Recherche propulsée par IA (Réponses IA)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "La recherche IA utilise des incorporations vectorielles pour fournir des capacités de recherche sémantique à travers le contenu de votre espace de travail.",
|
||||
"Toggle AI search": "Basculer la recherche IA",
|
||||
"Generative AI (Ask AI)": "IA générative (Demandez à l'IA)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Activer la génération de contenu assistée par IA dans l'éditeur. Permet aux utilisateurs de générer, améliorer, traduire et transformer du texte.",
|
||||
"Toggle generative AI": "Activer/désactiver l'IA générative",
|
||||
"Upgrade your plan": "Mettez à niveau votre forfait",
|
||||
"Available with a paid license": "Disponible avec une licence payante",
|
||||
"Upgrade your license tier.": "Mettez à niveau votre niveau de licence.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "L'IA n'est disponible que dans l'édition Entreprise de Docmost. Contactez sales@docmost.com.",
|
||||
"AI & MCP": "IA & MCP",
|
||||
"AI": "IA",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Protocole de contexte de modèle (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Activez le serveur MCP pour permettre aux assistants et outils IA d'interagir avec le contenu de votre espace de travail.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP n'est disponible que dans l'édition Entreprise de Docmost. Contactez sales@docmost.com.",
|
||||
"MCP Server URL": "URL du serveur MCP",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Utilisez votre clé API pour l'authentification. Vous pouvez gérer les clés API dans les paramètres de votre compte.",
|
||||
"Supported tools": "Outils pris en charge",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Votre espace de travail a MCP activé. Utilisez votre clé API pour connecter des assistants IA.",
|
||||
"MCP server URL:": "URL du serveur MCP :",
|
||||
"Learn more": "En savoir plus",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gérez les clés API pour tous les utilisateurs de l'espace de travail. Consultez la <anchor>documentation API</anchor> pour plus de détails sur l'utilisation.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Consultez la <anchor>documentation API</anchor> pour plus de détails sur l'utilisation.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Consultez la <anchor>documentation MCP</anchor>.",
|
||||
"Sources": "Sources",
|
||||
"Ask AI not available for attachments": "Demande à l'IA non disponible pour les pièces jointes",
|
||||
"AI Answers not available for attachments": "Réponses IA non disponibles pour les pièces jointes",
|
||||
"No answer available": "Pas de réponse disponible",
|
||||
"Background color": "Couleur de fond",
|
||||
"Highlight color": "Couleur de surbrillance",
|
||||
"Remove color": "Supprimer la couleur"
|
||||
"Remove color": "Supprimer la couleur",
|
||||
"Notifications": "Notifications",
|
||||
"No notifications": "Aucune notification",
|
||||
"No unread notifications": "Aucune notification non lue",
|
||||
"All notifications": "Toutes les notifications",
|
||||
"Unread only": "Non lues uniquement",
|
||||
"Mark all as read": "Tout marquer comme lu",
|
||||
"Mark as read": "Marquer comme lu",
|
||||
"More options": "Plus d'options",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> vous a mentionné dans un commentaire",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> a commenté une page",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> a résolu un commentaire",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> vous a mentionné sur une page",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> vous a donné un accès de modification à une page",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> vous a donné un accès en lecture à une page",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> a mis à jour une page",
|
||||
"Watch page": "Suivre la page",
|
||||
"Stop watching": "Arrêter de suivre",
|
||||
"Watch space": "Suivre l’espace",
|
||||
"Stop watching space": "Arrêter de suivre l’espace",
|
||||
"Email notifications": "Notifications par e-mail",
|
||||
"Page updates": "Mises à jour de la page",
|
||||
"Get notified when pages you watch are updated.": "Recevez une notification lorsque les pages que vous surveillez sont mises à jour.",
|
||||
"Page mentions": "Mentions sur la page",
|
||||
"Get notified when someone mentions you on a page.": "Recevez une notification lorsqu'une personne vous mentionne sur une page.",
|
||||
"Comment mentions": "Mentions dans les commentaires",
|
||||
"Get notified when someone mentions you in a comment.": "Recevez une notification lorsqu'une personne vous mentionne dans un commentaire.",
|
||||
"New comments": "Nouveaux commentaires",
|
||||
"Get notified about new comments on threads you participate in.": "Recevez une notification concernant les nouveaux commentaires dans les fils auxquels vous participez.",
|
||||
"Resolved comments": "Commentaires résolus",
|
||||
"Get notified when your comment is resolved.": "Recevez une notification lorsque votre commentaire est résolu.",
|
||||
"You are now watching this page": "Vous surveillez désormais cette page",
|
||||
"You are no longer watching this page": "Vous ne surveillez plus cette page",
|
||||
"You are now watching this space": "Vous suivez maintenant cet espace",
|
||||
"You are no longer watching this space": "Vous ne suivez plus cet espace",
|
||||
"Direct": "Direct",
|
||||
"Updates": "Mises à jour",
|
||||
"Today": "Aujourd'hui",
|
||||
"Yesterday": "Hier",
|
||||
"This week": "Cette semaine",
|
||||
"Older": "Plus ancien",
|
||||
"Restricted page": "Page restreinte",
|
||||
"Restricted pages cannot be shared publicly.": "Les pages restreintes ne peuvent pas être partagées publiquement.",
|
||||
"Restricted by parent": "Restreint par la page parente",
|
||||
"Restricted": "Restreint",
|
||||
"Open": "Ouvert",
|
||||
"Inherits restrictions from ancestor page": "Hérite des restrictions d'une page ancêtre",
|
||||
"Only people listed below can access this page": "Seules les personnes listées ci-dessous peuvent accéder à cette page",
|
||||
"Everyone in this space can access": "Tout le monde dans cet espace y a accès",
|
||||
"No additional restrictions on this page": "Aucune restriction supplémentaire sur cette page",
|
||||
"Only specific people can access": "Seules certaines personnes peuvent y accéder",
|
||||
"Use only inherited restrictions": "Utiliser uniquement les restrictions héritées",
|
||||
"Add restrictions on top of inherited": "Ajouter des restrictions en plus de celles héritées",
|
||||
"Inherited restriction": "Restriction héritée",
|
||||
"Access limited by": "Accès limité par",
|
||||
"Restrict access to control who can view and edit this page": "Restreindre l'accès pour contrôler qui peut consulter et modifier cette page",
|
||||
"Add additional restrictions specific to this page": "Ajouter des restrictions supplémentaires propres à cette page",
|
||||
"Access": "Accès",
|
||||
"People with access": "Personnes ayant accès",
|
||||
"Remove all": "Tout retirer",
|
||||
"Remove access": "Retirer l'accès",
|
||||
"Remove all access": "Retirer tous les accès",
|
||||
"Are you sure you want to remove this member's access to the page?": "Êtes-vous sûr de vouloir retirer l'accès de ce membre à la page ?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Êtes-vous sûr de vouloir supprimer tous les accès spécifiques ? Cela rendra la page accessible à tous les membres de l'espace.",
|
||||
"Trash retention": "Conservation de la corbeille",
|
||||
"Pages in trash will be permanently deleted after this period.": "Les pages dans la corbeille seront définitivement supprimées après cette période.",
|
||||
"Trash retention updated": "Durée de conservation de la corbeille mise à jour",
|
||||
"Failed to update trash retention": "Échec de la mise à jour de la durée de conservation de la corbeille",
|
||||
"Removed page restriction": "Restriction de la page supprimée",
|
||||
"Added page permission": "Autorisation de la page ajoutée",
|
||||
"Removed page permission": "Autorisation de la page supprimée",
|
||||
"day": "jour",
|
||||
"days": "jours",
|
||||
"week": "semaine",
|
||||
"weeks": "semaines",
|
||||
"month": "mois",
|
||||
"months": "mois",
|
||||
"year": "an",
|
||||
"years": "ans",
|
||||
"Period": "Période",
|
||||
"Fixed date": "Date fixe",
|
||||
"Indefinitely": "Indéfiniment",
|
||||
"Days": "Jours",
|
||||
"Weeks": "Semaines",
|
||||
"Months": "Mois",
|
||||
"Years": "Ans",
|
||||
"Pick a date": "Choisir une date",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Le maximum est de {{max}} {{unit}} pour cette unité",
|
||||
"Never expires. Verifiers can re-verify at any time.": "N’expire jamais. Les vérificateurs peuvent revérifier à tout moment.",
|
||||
"Verified": "Vérifié",
|
||||
"Review needed": "Révision nécessaire",
|
||||
"Verification expired": "Vérification expirée",
|
||||
"Draft": "Brouillon",
|
||||
"In Approval": "En approbation",
|
||||
"In approval": "En approbation",
|
||||
"Approved": "Approuvé",
|
||||
"Obsolete": "Obsolète",
|
||||
"Expiring": "Expire bientôt",
|
||||
"Set up verification": "Configurer la vérification",
|
||||
"Verify page": "Vérifier la page",
|
||||
"Page verification": "Vérification de la page",
|
||||
"Add verification": "Ajouter une vérification",
|
||||
"Edit verification": "Modifier la vérification",
|
||||
"Search by title": "Rechercher par titre",
|
||||
"Choose how this page should stay accurate.": "Choisissez comment cette page doit rester exacte.",
|
||||
"Recurring verification": "Vérification récurrente",
|
||||
"Verifiers re-confirm this page on a schedule.": "Les vérificateurs reconfirment cette page selon une fréquence définie.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Revérifier selon une fréquence définie (p. ex. tous les 30 jours)",
|
||||
"Page stays editable at all times": "La page reste modifiable en permanence",
|
||||
"Best for runbooks, FAQs, living documentation": "Idéal pour les runbooks, FAQ et la documentation évolutive",
|
||||
"Approval workflow": "Flux d’approbation",
|
||||
"Formal document lifecycle with named approvers.": "Cycle de vie formel du document avec des approbateurs désignés.",
|
||||
"Draft → In approval → Approved → Obsolete": "Brouillon → En approbation → Approuvé → Obsolète",
|
||||
"Locked once approved, with full history": "Verrouillé une fois approuvé, avec historique complet",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Conçu pour l’ISO 9001, l’ISO 13485 et la FDA",
|
||||
"Best for SOPs and controlled documents": "Idéal pour les SOP et les documents contrôlés",
|
||||
"Back": "Retour",
|
||||
"Quality management": "Gestion de la qualité",
|
||||
"Recurring": "Récurrent",
|
||||
"Pages move through draft, approval, and approved stages.": "Les pages passent par les étapes brouillon, approbation et approuvé.",
|
||||
"Verifiers": "Vérificateurs",
|
||||
"Add verifier": "Ajouter un vérificateur",
|
||||
"I've reviewed this page for accuracy": "J’ai vérifié l’exactitude de cette page",
|
||||
"Set up": "Configurer",
|
||||
"Remove verification": "Supprimer la vérification",
|
||||
"Are you sure you want to remove verification from this page?": "Voulez-vous vraiment supprimer la vérification de cette page ?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Les vérificateurs assignés doivent revérifier périodiquement cette page.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Dernière vérification par {{name}} {{time}} (expirée)",
|
||||
"The fixed expiration date has passed.": "La date d’expiration fixe est passée.",
|
||||
"Verified by {{name}} {{time}}": "Vérifié par {{name}} {{time}}",
|
||||
"Expires {{date}}": "Expire le {{date}}",
|
||||
"Expired {{date}}": "Expiré le {{date}}",
|
||||
"Mark as obsolete": "Marquer comme obsolète",
|
||||
"Mark obsolete": "Marquer comme obsolète",
|
||||
"Returned by {{name}} {{time}}": "Renvoyé par {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Aucune approbation n’a encore été demandée.",
|
||||
"Submitted by {{name}} {{time}}": "Soumis par {{name}} {{time}}",
|
||||
"Someone": "Quelqu’un",
|
||||
"Approved by {{name}} {{time}}": "Approuvé par {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Ce document a été marqué comme obsolète.",
|
||||
"Rejection comment": "Commentaire de rejet",
|
||||
"Reason for returning this document...": "Raison du renvoi de ce document...",
|
||||
"Confirm rejection": "Confirmer le rejet",
|
||||
"Submit for approval": "Soumettre pour approbation",
|
||||
"Reject": "Rejeter",
|
||||
"Approve": "Approuver",
|
||||
"Re-submit for approval": "Soumettre à nouveau pour approbation",
|
||||
"Verified until": "Vérifié jusqu’au",
|
||||
"QMS": "SMQ",
|
||||
"Verified pages": "Pages vérifiées",
|
||||
"Search pages...": "Rechercher des pages...",
|
||||
"Filter by space": "Filtrer par espace",
|
||||
"Filter by type": "Filtrer par type",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> a vérifié une page",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> a soumis une page à votre approbation",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> a renvoyé une page pour révision",
|
||||
"Page verification expires soon": "La vérification de la page expire bientôt",
|
||||
"Page verification has expired": "La vérification de la page a expiré",
|
||||
"Verifying your email": "Vérification de votre e-mail",
|
||||
"Please wait...": "Veuillez patienter...",
|
||||
"Verification failed. The link may have expired.": "Échec de la vérification. Le lien a peut-être expiré.",
|
||||
"Check your email": "Vérifiez votre e-mail",
|
||||
"We sent a verification link to {{email}}.": "Nous avons envoyé un lien de vérification à {{email}}.",
|
||||
"We sent a verification link to your email.": "Nous avons envoyé un lien de vérification à votre adresse e-mail.",
|
||||
"Click the link to verify your email and access your workspace.": "Cliquez sur le lien pour vérifier votre adresse et accéder à votre espace de travail.",
|
||||
"Resend verification email": "Renvoyer l’e-mail de vérification",
|
||||
"Verification email sent. Please check your inbox.": "E-mail de vérification envoyé. Veuillez vérifier votre boîte de réception.",
|
||||
"Failed to resend verification email. Please try again.": "Échec de l'envoi du nouvel e-mail de vérification. Veuillez réessayer.",
|
||||
"We've sent you an email with your associated workspaces.": "Nous vous avons envoyé un e-mail avec vos espaces de travail associés.",
|
||||
"Load more": "Charger plus",
|
||||
"Log out of all devices": "Se déconnecter de tous les appareils",
|
||||
"Log out of all sessions except this device": "Se déconnecter de toutes les sessions sauf cet appareil",
|
||||
"This Device": "Cet appareil",
|
||||
"Unknown device": "Appareil inconnu",
|
||||
"No active sessions": "Aucune session active",
|
||||
"Session revoked": "Session révoquée",
|
||||
"All other sessions revoked": "Toutes les autres sessions ont été révoquées",
|
||||
"Last used": "Dernière utilisation",
|
||||
"Created": "Créé",
|
||||
"Rename": "Renommer",
|
||||
"Publish": "Publier",
|
||||
"Security": "Sécurité",
|
||||
"Enforce SSO": "Imposer le SSO",
|
||||
"Once enforced, members will not be able to login with email and password.": "Une fois activé, les membres ne pourront plus se connecter avec leur e-mail et leur mot de passe.",
|
||||
"AI-generated content may not be accurate.": "Le contenu généré par l’IA peut ne pas être exact.",
|
||||
"AI Chat": "Chat IA",
|
||||
"Analyze for insights": "Analyser pour obtenir des informations",
|
||||
"Ask anything...": "Posez n’importe quelle question...",
|
||||
"Chat history": "Historique des discussions",
|
||||
"Chat name": "Nom de la discussion",
|
||||
"Close": "Fermer",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Échec du chargement de la discussion. Une erreur s’est produite.",
|
||||
"Failed to render this message.": "Échec de l’affichage de ce message.",
|
||||
"How can I help you today?": "Comment puis-je vous aider aujourd’hui ?",
|
||||
"New chat": "Nouvelle discussion",
|
||||
"No chat history": "Aucun historique de discussion",
|
||||
"No chats found": "Aucune discussion trouvée",
|
||||
"No conversations yet": "Aucune conversation pour le moment",
|
||||
"Open full page": "Ouvrir la page complète",
|
||||
"Previous 7 days": "7 derniers jours",
|
||||
"Previous 30 days": "30 derniers jours",
|
||||
"Search chats...": "Rechercher des discussions...",
|
||||
"Start a new chat to see it here.": "Commencez une nouvelle discussion pour la voir ici.",
|
||||
"Summarize this page": "Résumer cette page",
|
||||
"Toggle AI Chat": "Basculer le chat IA",
|
||||
"Translate this page": "Traduire cette page",
|
||||
"Try a different search term.": "Essayez un autre terme de recherche.",
|
||||
"Try again": "Réessayer",
|
||||
"Untitled chat": "Discussion sans titre",
|
||||
"What can I help you with?": "Que puis-je faire pour vous aider ?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Aggiungi membri",
|
||||
"Add to groups": "Aggiungi ai gruppi",
|
||||
"Add space members": "Aggiungi membri allo spazio",
|
||||
"Add to favorites": "Aggiungi ai preferiti",
|
||||
"Admin": "Amministratore",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Sei sicuro di voler eliminare questo gruppo? I membri perderanno l'accesso alle risorse accessibili da questo gruppo.",
|
||||
"Are you sure you want to delete this page?": "Sei sicuro di voler eliminare questa pagina?",
|
||||
@@ -47,19 +48,19 @@
|
||||
"e.g ACME": "es. ACME",
|
||||
"e.g ACME Inc": "es. ACME Inc",
|
||||
"e.g Developers": "es. Sviluppatori",
|
||||
"e.g Group for developers": "es. Gruppo per gli sviluppatori",
|
||||
"e.g Group for developers": "es. Gruppo per sviluppatori",
|
||||
"e.g product": "es. prodotto",
|
||||
"e.g Product Team": "es. Team di Prodotto",
|
||||
"e.g Product Team": "es. Team di prodotto",
|
||||
"e.g Sales": "es. Vendite",
|
||||
"e.g Space for product team": "es. Spazio per il team di prodotto",
|
||||
"e.g Space for sales team to collaborate": "es. Spazio per la collaborazione del team di vendita",
|
||||
"e.g Space for sales team to collaborate": "es. Spazio per la collaborazione del team vendite",
|
||||
"Edit": "Modifica",
|
||||
"Read": "Leggi",
|
||||
"Edit group": "Modifica gruppo",
|
||||
"Email": "Email",
|
||||
"Enter a strong password": "Inserisci una password sicura",
|
||||
"Enter valid email addresses separated by comma or space max_50": "Inserisci degli indirizzi email validi separati da virgola o spazio [max: 50]",
|
||||
"enter valid emails addresses": "inserisci degli indirizzi email validi",
|
||||
"enter valid emails addresses": "inserisci indirizzi email validi",
|
||||
"Enter your current password": "Inserisci la tua password attuale",
|
||||
"enter your full name": "inserisci il tuo nome completo",
|
||||
"Enter your new password": "Inserisci la tua nuova password",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Impossibile importare le pagine",
|
||||
"Failed to load page. An error occurred.": "Il caricamento della pagina è fallito. Si è verificato un errore.",
|
||||
"Failed to update data": "Impossibile aggiornare i dati",
|
||||
"Favorite spaces": "Spazi preferiti",
|
||||
"Favorite spaces appear here": "Gli spazi preferiti appariranno qui",
|
||||
"Favorites": "Preferiti",
|
||||
"Full access": "Accesso completo",
|
||||
"Full page width": "Pagina a larghezza intera",
|
||||
"Full width": "Larghezza intera",
|
||||
@@ -92,6 +96,7 @@
|
||||
"Invite by email": "Invita tramite email",
|
||||
"Invite members": "Invita membri",
|
||||
"Invite new members": "Invita nuovi membri",
|
||||
"Invite People": "Invita persone",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "I membri invitati che non hanno ancora accettato il loro invito appariranno qui.",
|
||||
"Invited members will be granted access to spaces the groups can access": "I membri invitati avranno accesso agli spazi a cui i gruppi possono accedere",
|
||||
"Join the workspace": "Unisciti all'area di lavoro",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "Nessun gruppo trovato",
|
||||
"No page history saved yet.": "La pagina non ha una cronologia per ora.",
|
||||
"No pages yet": "Nessuna pagina per ora",
|
||||
"No shared pages": "Nessuna pagina condivisa.",
|
||||
"No results found...": "Nessun risultato trovato...",
|
||||
"No user found": "Nessun utente trovato",
|
||||
"Overview": "Panoramica",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "pagine",
|
||||
"Password": "Password",
|
||||
"Password changed successfully": "Password cambiata con successo",
|
||||
"People": "Persone",
|
||||
"Pending": "In sospeso",
|
||||
"Please confirm your action": "Si prega di confermare la propria azione",
|
||||
"Preferences": "Preferenze",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Profilo",
|
||||
"Recently updated": "Aggiornato di recente",
|
||||
"Remove": "Rimuovi",
|
||||
"Remove from favorites": "Rimuovi dai preferiti",
|
||||
"Remove group member": "Rimuovi membro dal gruppo",
|
||||
"Remove space member": "Rimuovi membro dallo spazio",
|
||||
"Restore": "Ripristina",
|
||||
@@ -147,55 +155,56 @@
|
||||
"Search for users": "Cerca un utente",
|
||||
"Search for users and groups": "Cerca un utente o un gruppo",
|
||||
"Search...": "Cerca...",
|
||||
"Select language": "Seleziona una lingua",
|
||||
"Select role": "Seleziona un ruolo",
|
||||
"Select language": "Seleziona lingua",
|
||||
"Select role": "Seleziona ruolo",
|
||||
"Select role to assign to all invited members": "Seleziona il ruolo da assegnare a tutti i membri invitati",
|
||||
"Select theme": "Seleziona un tema",
|
||||
"Select theme": "Seleziona tema",
|
||||
"Send invitation": "Invia invito",
|
||||
"Invitation sent": "Invito inviato",
|
||||
"Settings": "Impostazioni",
|
||||
"Setup workspace": "Configura l'area di lavoro",
|
||||
"Setup workspace": "Configura workspace",
|
||||
"Sign In": "Accedi",
|
||||
"Sign Up": "Registrati",
|
||||
"Slug": "Slug",
|
||||
"Space": "Spazio",
|
||||
"Space description": "Descrizione dello spazio",
|
||||
"Space menu": "Menu spazio",
|
||||
"Space menu": "Menu dello spazio",
|
||||
"Space name": "Nome dello spazio",
|
||||
"Space settings": "Impostazioni dello spazio",
|
||||
"Space slug": "Slug dello spazio",
|
||||
"Spaces": "Spazi",
|
||||
"Spaces you belong to": "Spazi a cui appartieni",
|
||||
"Spaces you belong to": "Spazi di cui fai parte",
|
||||
"No space found": "Nessuno spazio trovato",
|
||||
"Search for spaces": "Cerca uno spazio",
|
||||
"Search for spaces": "Cerca spazi",
|
||||
"Start typing to search...": "Inizia a digitare per cercare...",
|
||||
"Status": "Stato",
|
||||
"Successfully imported": "Importato con successo",
|
||||
"Successfully restored": "Ripristinato con successo",
|
||||
"System settings": "Impostazioni di sistema",
|
||||
"Templates": "Modelli",
|
||||
"Theme": "Tema",
|
||||
"To change your email, you have to enter your password and new email.": "Per cambiare la tua email, devi inserire la tua password e la nuova email.",
|
||||
"Toggle full page width": "Attiva/disattiva pagina a larghezza intera",
|
||||
"Toggle full page width": "Attiva/disattiva larghezza completa della pagina",
|
||||
"Unable to import pages. Please try again.": "Impossibile importare le pagine. Riprova.",
|
||||
"untitled": "senza titolo",
|
||||
"Untitled": "Senza titolo",
|
||||
"Updated successfully": "Aggiornato con successo",
|
||||
"User": "Utente",
|
||||
"Workspace": "Area di lavoro",
|
||||
"Workspace Name": "Nome dell'area di lavoro",
|
||||
"Workspace settings": "Impostazioni dell'area di lavoro",
|
||||
"Workspace": "Workspace",
|
||||
"Workspace Name": "Nome del workspace",
|
||||
"Workspace settings": "Impostazioni del workspace",
|
||||
"You can change your password here.": "Qui puoi cambiare la tua password.",
|
||||
"Your Email": "La tua email",
|
||||
"Your import is complete.": "La tua importazione è completata.",
|
||||
"Your name": "Il tuo nome",
|
||||
"Your Name": "Il Tuo Nome",
|
||||
"Your Name": "Il tuo nome",
|
||||
"Your password": "La tua password",
|
||||
"Your password must be a minimum of 8 characters.": "La tua password deve contenere almeno 8 caratteri.",
|
||||
"Sidebar toggle": "Attiva/disattiva barra laterale",
|
||||
"Comments": "Commenti",
|
||||
"404 page not found": "404 pagina non trovata",
|
||||
"Sorry, we can't find the page you are looking for.": "Siamo spiacenti, non riusciamo a trovare la pagina che stai cercando.",
|
||||
"Take me back to homepage": "Torna all'homepage",
|
||||
"Take me back to homepage": "Torna alla homepage",
|
||||
"Forgot password": "Password dimenticata",
|
||||
"Forgot your password?": "Hai dimenticato la password?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "Un link per il reset della password è stato inviato al tuo indirizzo email. Per favore, controlla la tua casella di posta.",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Rispondi...",
|
||||
"Error loading comments.": "Si è verificato un errore durante il caricamento dei commenti.",
|
||||
"No comments yet.": "Nessun commento per ora.",
|
||||
"No open comments.": "Nessun commento aperto.",
|
||||
"No resolved comments.": "Nessun commento risolto.",
|
||||
"Add a comment...": "Aggiungi un commento...",
|
||||
"Edit comment": "Modifica commento",
|
||||
"Delete comment": "Elimina commento",
|
||||
"Are you sure you want to delete this comment?": "Sei sicuro di voler eliminare questo commento?",
|
||||
"Delete chat": "Elimina chat",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Sei sicuro di voler eliminare '{{title}}'? Questa azione non può essere annullata.",
|
||||
"Comment created successfully": "Commento creato con successo",
|
||||
"Error creating comment": "Si è verificato un errore durante la creazione del commento",
|
||||
"Comment updated successfully": "Commento aggiornato con successo",
|
||||
@@ -218,17 +232,16 @@
|
||||
"Failed to delete comment": "Impossibile eliminare il commento",
|
||||
"Comment resolved successfully": "Commento risolto con successo",
|
||||
"Comment re-opened successfully": "Commento riaperto con successo",
|
||||
"Comment unresolved successfully": "Commento non risolto con successo",
|
||||
"Comment unresolved successfully": "Commento contrassegnato come non risolto con successo",
|
||||
"Failed to resolve comment": "Impossibile risolvere il commento",
|
||||
"Resolve comment": "Risolvi commento",
|
||||
"Unresolve comment": "Annulla risoluzione commento",
|
||||
"Resolve Comment Thread": "Risolvi discussione commenti",
|
||||
"Unresolve Comment Thread": "Annulla risoluzione discussione commenti",
|
||||
"Unresolve comment": "Segna commento come non risolto",
|
||||
"Resolve Comment Thread": "Risolvi discussione del commento",
|
||||
"Unresolve Comment Thread": "Segna discussione del commento come non risolta",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Sei sicuro di voler risolvere questa discussione di commenti? Questo la contrassegnerà come completata.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Sei sicuro di voler annullare la risoluzione di questa discussione di commenti?",
|
||||
"Resolved": "Risolto",
|
||||
"No active comments.": "Nessun commento attivo.",
|
||||
"No resolved comments.": "Nessun commento risolto.",
|
||||
"Revoke invitation": "Revoca invito",
|
||||
"Revoke": "Revoca",
|
||||
"Don't": "Non",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "Sei sicuro di voler eliminare questo spazio?",
|
||||
"Delete this space with all its pages and data.": "Elimina questo spazio con tutte le sue pagine e i suoi dati.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "Tutte le pagine, i commenti, gli allegati e i permessi di questo spazio verranno eliminati irreversibilmente.",
|
||||
"Confirm space name": "Conferma nome spazio",
|
||||
"Confirm space name": "Conferma nome dello spazio",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Digita il nome dello spazio <b>{{spaceName}}</b> per confermare la tua azione.",
|
||||
"Format": "Formato",
|
||||
"Include subpages": "Includi sottopagine",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Aggiungi riga sotto",
|
||||
"Delete table": "Elimina tabella",
|
||||
"Info": "Informazioni",
|
||||
"Note": "Nota",
|
||||
"Success": "Successo",
|
||||
"Warning": "Avviso",
|
||||
"Danger": "Pericolo",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Salva ed esci",
|
||||
"Double-click to edit Excalidraw diagram": "Fai doppio clic per modificare il diagramma di Excalidraw",
|
||||
"Paste link": "Incolla link",
|
||||
"Paste link or search pages": "Incolla il link o cerca le pagine",
|
||||
"Link to web page": "Collega a una pagina web",
|
||||
"Recents": "Recenti",
|
||||
"Page or URL": "Pagina o URL",
|
||||
"Link title": "Titolo del link",
|
||||
"Edit link": "Modifica link",
|
||||
"Remove link": "Rimuovi link",
|
||||
"Add link": "Aggiungi link",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Rosa",
|
||||
"Gray": "Grigio",
|
||||
"Embed link": "Incorpora collegamento",
|
||||
"Invalid {{provider}} embed link": "Link di incorporamento {{provider}} non valido",
|
||||
"Invalid {{provider}} embed link": "Link incorporato {{provider}} non valido",
|
||||
"Embed {{provider}}": "Incorpora {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Inserisci il link {{provider}} per incorporare",
|
||||
"Bold": "Grassetto",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Inserisci divisore di regola orizzontale",
|
||||
"Upload any image from your device.": "Carica un'immagine dal tuo dispositivo.",
|
||||
"Upload any video from your device.": "Carica qualsiasi video dal tuo dispositivo.",
|
||||
"Upload any audio from your device.": "Carica qualsiasi audio dal tuo dispositivo.",
|
||||
"Upload any file from your device.": "Carica qualsiasi file dal tuo dispositivo.",
|
||||
"Uploading {{name}}": "Caricamento di {{name}}",
|
||||
"Uploading file": "Caricamento file",
|
||||
@@ -338,26 +358,46 @@
|
||||
"Insert a table.": "Inserisci una tabella.",
|
||||
"Insert collapsible block.": "Inserisci blocco comprimibile.",
|
||||
"Video": "Video",
|
||||
"Divider": "Divisore",
|
||||
"Quote": "Preventivo",
|
||||
"Divider": "Separatore",
|
||||
"Quote": "Citazione",
|
||||
"Image": "Immagine",
|
||||
"Audio": "Audio",
|
||||
"Embed PDF": "Incorpora PDF",
|
||||
"Upload and embed a PDF file.": "Carica e incorpora un file PDF.",
|
||||
"Embed as PDF": "Incorpora come PDF",
|
||||
"Failed to load PDF": "Caricamento del PDF non riuscito",
|
||||
"Convert to attachment": "Converti in allegato",
|
||||
"File attachment": "Allegato file",
|
||||
"Toggle block": "Attiva blocco",
|
||||
"Callout": "Avviso",
|
||||
"Toggle block": "Blocco a comparsa",
|
||||
"Callout": "Riquadro evidenziato",
|
||||
"Insert callout notice.": "Inserisci avviso di richiamo.",
|
||||
"Math inline": "Matematica in linea",
|
||||
"Math inline": "Formula matematica in linea",
|
||||
"Insert inline math equation.": "Inserisci equazione matematica in linea.",
|
||||
"Math block": "Blocco matematico",
|
||||
"Insert math equation": "Inserisci equazione matematica",
|
||||
"Mermaid diagram": "Diagramma di Mermaid",
|
||||
"Insert mermaid diagram": "Inserisci un diagramma di Mermaid",
|
||||
"Mermaid diagram": "Diagramma Mermaid",
|
||||
"Insert mermaid diagram": "Inserisci diagramma Mermaid",
|
||||
"Insert and design Drawio diagrams": "Inserisci e progetta diagrammi Drawio",
|
||||
"Insert current date": "Inserisci la data corrente",
|
||||
"Draw and sketch excalidraw diagrams": "Disegna e schizza diagrammi excalidraw",
|
||||
"Insert current date": "Inserisci data corrente",
|
||||
"Draw and sketch excalidraw diagrams": "Disegna e abbozza diagrammi Excalidraw",
|
||||
"Multiple": "Multiplo",
|
||||
"Turn into": "Trasforma in",
|
||||
"Text align": "Allinea testo",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Questa pagina potrebbe essere stata eliminata o spostata, oppure potresti non avere accesso.",
|
||||
"Go to homepage": "Vai alla pagina principale",
|
||||
"Pages you create will show up here.": "Le pagine che crei appariranno qui.",
|
||||
"Heading {{level}}": "Intestazione {{level}}",
|
||||
"Toggle title": "Attiva/disattiva titolo",
|
||||
"Write anything. Enter \"/\" for commands": "Scrivi qualcosa. Digita \"/\" per i comandi",
|
||||
"Write anything. Enter \"/\" for commands": "Scrivi qualsiasi cosa. Digita \"/\" per i comandi",
|
||||
"Write...": "Scrivi...",
|
||||
"Column count": "Numero di colonne",
|
||||
"{{count}} Columns": "{{count}} colonne",
|
||||
"Equal columns": "Colonne uguali",
|
||||
"Left sidebar": "Barra laterale sinistra",
|
||||
"Right sidebar": "Barra laterale destra",
|
||||
"Wide center": "Centro ampio",
|
||||
"Left wide": "Ampia a sinistra",
|
||||
"Right wide": "Ampia a destra",
|
||||
"Names do not match": "I nomi non corrispondono",
|
||||
"Today, {{time}}": "Oggi, {{time}}",
|
||||
"Yesterday, {{time}}": "Ieri, {{time}}",
|
||||
@@ -369,75 +409,102 @@
|
||||
"Member role updated successfully": "Ruolo del membro aggiornato con successo",
|
||||
"Created by: <b>{{creatorName}}</b>": "Creato da: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Creato il: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Modificato da {{name}} il {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Modificato da {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Conteggio parole: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Conteggio caratteri: {{characterCount}}",
|
||||
"New update": "Nuovo aggiornamento",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} è disponibile",
|
||||
"Default page edit mode": "Modalità di modifica pagina predefinita",
|
||||
"Default page edit mode": "Modalità di modifica predefinita della pagina",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Scegli la tua modalità di modifica della pagina preferita. Evita modifiche accidentali.",
|
||||
"Reading": "Lettura",
|
||||
"Delete member": "Elimina membro",
|
||||
"Member deleted successfully": "Membro eliminato con successo",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Sei sicuro di voler eliminare questo membro del workspace? Questa azione è irreversibile.",
|
||||
"Deactivate member": "Disattiva membro",
|
||||
"Activate member": "Attiva membro",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Sei sicuro di voler disattivare questo membro dello spazio di lavoro? Non potrà più accedere a questo spazio di lavoro.",
|
||||
"Are you sure you want to activate this workspace member?": "Sei sicuro di voler attivare questo membro dello spazio di lavoro?",
|
||||
"Deactivate": "Disattiva",
|
||||
"Activate": "Attiva",
|
||||
"Deactivated": "Disattivato",
|
||||
"Move": "Sposta",
|
||||
"Move page": "Sposta pagina",
|
||||
"Move page to a different space.": "Sposta la pagina in un altro spazio.",
|
||||
"Real-time editor connection lost. Retrying...": "Connessione all'editor in tempo reale persa. Riprovo...",
|
||||
"Table of contents": "Indice dei contenuti",
|
||||
"Table of contents": "Indice",
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Aggiungi intestazioni (H1, H2, H3) per generare un sommario.",
|
||||
"Share": "Condividi",
|
||||
"Public sharing": "Condivisione pubblica",
|
||||
"Shared by": "Condiviso da",
|
||||
"Shared at": "Condiviso il",
|
||||
"Inherits public sharing from": "Eredita la condivisione pubblica da",
|
||||
"Share to web": "Condividi su web",
|
||||
"Shared to web": "Condiviso su web",
|
||||
"Share to web": "Condividi sul web",
|
||||
"Shared to web": "Condiviso sul web",
|
||||
"Anyone with the link can view this page": "Chiunque abbia il link può visualizzare questa pagina",
|
||||
"Make this page publicly accessible": "Rendi questa pagina accessibile pubblicamente",
|
||||
"Include sub-pages": "Includi sotto-pagine",
|
||||
"Make sub-pages public too": "Rendi pubbliche anche le sotto-pagine",
|
||||
"Allow search engines to index page": "Permetti ai motori di ricerca di indicizzare la pagina",
|
||||
"Include sub-pages": "Includi sottopagine",
|
||||
"Make sub-pages public too": "Rendi pubbliche anche le sottopagine",
|
||||
"Allow search engines to index page": "Consenti ai motori di ricerca di indicizzare la pagina",
|
||||
"Open page": "Apri pagina",
|
||||
"Page": "Pagina",
|
||||
"Delete public share link": "Elimina il link di condivisione pubblica",
|
||||
"Delete public share link": "Elimina link di condivisione pubblica",
|
||||
"Delete share": "Elimina condivisione",
|
||||
"Are you sure you want to delete this shared link?": "Sei sicuro di voler eliminare questo link condiviso?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Le pagine condivise pubblicamente dagli spazi di cui sei membro appariranno qui",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Le pagine condivise pubblicamente degli spazi di cui fai parte appariranno qui",
|
||||
"Share deleted successfully": "Condivisione eliminata con successo",
|
||||
"Share not found": "Condivisione non trovata",
|
||||
"Failed to share page": "Condivisione della pagina fallita",
|
||||
"Failed to share page": "Condivisione della pagina non riuscita",
|
||||
"Disable public sharing": "Disabilita la condivisione pubblica",
|
||||
"Prevent members from sharing pages publicly.": "Impedisci ai membri di condividere pubblicamente le pagine.",
|
||||
"Toggle public sharing": "Attiva/disattiva la condivisione pubblica",
|
||||
"Toggle space public sharing": "Attiva/disattiva la condivisione pubblica nello spazio",
|
||||
"Allow viewers to comment": "Consenti agli utenti di commentare",
|
||||
"Allow viewers to add comments on pages in this space.": "Consenti agli utenti di aggiungere commenti alle pagine in questo spazio.",
|
||||
"Toggle viewer comments": "Attiva/disattiva i commenti degli utenti",
|
||||
"Public sharing is disabled at the workspace level": "La condivisione pubblica è disabilitata a livello di area di lavoro",
|
||||
"Prevent pages in this space from being shared publicly.": "Impedisci che le pagine in questo spazio vengano condivise pubblicamente.",
|
||||
"Page permissions": "Autorizzazioni della pagina.",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Controlla chi può visualizzare e modificare le singole pagine. Disponibile con una licenza Enterprise.",
|
||||
"Enable public sharing": "Abilita la condivisione pubblica",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Sei sicuro di voler abilitare la condivisione pubblica? I membri potranno condividere le pagine pubblicamente.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Sei sicuro di voler disabilitare la condivisione pubblica? Tutti i link condivisi esistenti in questa area di lavoro verranno eliminati.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Sei sicuro di voler abilitare la condivisione pubblica per questo spazio?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Sei sicuro di voler disabilitare la condivisione pubblica? Tutti i link condivisi esistenti in questo spazio verranno eliminati.",
|
||||
"Public sharing is disabled": "La condivisione pubblica è disabilitata",
|
||||
"Public sharing has been disabled at the workspace level.": "La condivisione pubblica è stata disabilitata a livello di area di lavoro.",
|
||||
"Public sharing has been disabled for this space.": "La condivisione pubblica è stata disabilitata per questo spazio.",
|
||||
"Copy page": "Copia pagina",
|
||||
"Copy page to a different space.": "Copia pagina in un altro spazio.",
|
||||
"Page copied successfully": "Pagina copiata con successo",
|
||||
"Page duplicated successfully": "Pagina duplicata con successo",
|
||||
"Find": "Trova",
|
||||
"Not found": "Non trovato",
|
||||
"Previous Match (Shift+Enter)": "Corrispondenza precedente (Shift+Invio)",
|
||||
"Previous Match (Shift+Enter)": "Corrispondenza precedente (Maiusc+Invio)",
|
||||
"Next match (Enter)": "Corrispondenza successiva (Invio)",
|
||||
"Match case (Alt+C)": "Maiuscole/minuscole (Alt+C)",
|
||||
"Match case (Alt+C)": "Distingui maiuscole/minuscole (Alt+C)",
|
||||
"Replace": "Sostituisci",
|
||||
"Close (Escape)": "Chiudi (Esc)",
|
||||
"Replace (Enter)": "Sostituisci (Invio)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Sostituisci tutto (Ctrl+Alt+Invio)",
|
||||
"Replace all": "Sostituisci tutto",
|
||||
"View all": "Visualizza tutto",
|
||||
"View all spaces": "Visualizza tutti gli spazi",
|
||||
"Error": "Errore",
|
||||
"Failed to disable MFA": "Disabilitazione MFA non riuscita",
|
||||
"Disable two-factor authentication": "Disabilita autenticazione a due fattori",
|
||||
"Failed to disable MFA": "Disattivazione MFA non riuscita",
|
||||
"Disable two-factor authentication": "Disattiva autenticazione a due fattori",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Disabilitare l'autenticazione a due fattori renderà il tuo account meno sicuro. Avrai bisogno solo della tua password per accedere.",
|
||||
"Please enter your password to disable two-factor authentication:": "Inserisci la tua password per disabilitare l'autenticazione a due fattori:",
|
||||
"Two-factor authentication has been enabled": "Autenticazione a due fattori abilitata",
|
||||
"Two-factor authentication has been disabled": "Autenticazione a due fattori disabilitata",
|
||||
"Two-factor authentication has been enabled": "L'autenticazione a due fattori è stata abilitata",
|
||||
"Two-factor authentication has been disabled": "L'autenticazione a due fattori è stata disabilitata",
|
||||
"2-step verification": "Verifica in 2 passaggi",
|
||||
"Protect your account with an additional verification layer when signing in.": "Proteggi il tuo account con un ulteriore livello di verifica durante l'accesso.",
|
||||
"Two-factor authentication is active on your account.": "L'autenticazione a due fattori è attiva sul tuo account.",
|
||||
"Add 2FA method": "Aggiungi metodo 2FA",
|
||||
"Backup codes": "Codici di backup",
|
||||
"Disable": "Disabilita",
|
||||
"Disable": "Disattiva",
|
||||
"Invalid verification code": "Codice di verifica non valido",
|
||||
"New backup codes have been generated": "Nuovi codici di backup generati",
|
||||
"Failed to regenerate backup codes": "Rigenerazione codici di backup non riuscita",
|
||||
"New backup codes have been generated": "Sono stati generati nuovi codici di backup",
|
||||
"Failed to regenerate backup codes": "Rigenerazione dei codici di backup non riuscita",
|
||||
"About backup codes": "Informazioni sui codici di backup",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "I codici di backup possono essere utilizzati per accedere al tuo account se perdi l'accesso alla tua app di autenticazione. Ogni codice può essere usato solo una volta.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Puoi rigenerare nuovi codici di backup in qualsiasi momento. Questo invaliderà tutti i codici esistenti.",
|
||||
@@ -447,9 +514,9 @@
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Assicurati di salvare questi codici in un luogo sicuro. I tuoi vecchi codici di backup non sono più validi.",
|
||||
"Your new backup codes": "I tuoi nuovi codici di backup",
|
||||
"I've saved my backup codes": "Ho salvato i miei codici di backup",
|
||||
"Failed to setup MFA": "Impostazione MFA non riuscita",
|
||||
"Setup & Verify": "Imposta e Verifica",
|
||||
"Add to authenticator": "Aggiungi ad authenticator",
|
||||
"Failed to setup MFA": "Configurazione MFA non riuscita",
|
||||
"Setup & Verify": "Configura e verifica",
|
||||
"Add to authenticator": "Aggiungi all'autenticatore",
|
||||
"1. Scan this QR code with your authenticator app": "1. Scansiona questo codice QR con la tua app di autenticazione",
|
||||
"Can't scan the code?": "Non riesci a scansionare il codice?",
|
||||
"Enter this code manually in your authenticator app:": "Inserisci questo codice manualmente nella tua app di autenticazione:",
|
||||
@@ -463,17 +530,17 @@
|
||||
"Print": "Stampa",
|
||||
"Two-factor authentication has been set up. Please log in again.": "L'autenticazione a due fattori è stata impostata. Effettua nuovamente l'accesso, per favore.",
|
||||
"Two-Factor authentication required": "Autenticazione a due fattori richiesta",
|
||||
"Your workspace requires two-factor authentication for all users": "Il tuo spazio di lavoro richiede l'autenticazione a due fattori per tutti gli utenti",
|
||||
"Your workspace requires two-factor authentication for all users": "Il tuo workspace richiede l'autenticazione a due fattori per tutti gli utenti",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Per continuare ad accedere al tuo spazio di lavoro, devi impostare l'autenticazione a due fattori. Questo aggiunge un ulteriore livello di sicurezza al tuo account.",
|
||||
"Set up two-factor authentication": "Imposta l'autenticazione a due fattori",
|
||||
"Cancel and logout": "Annulla e disconnetti",
|
||||
"Set up two-factor authentication": "Configura l'autenticazione a due fattori",
|
||||
"Cancel and logout": "Annulla e disconnettiti",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Il tuo spazio di lavoro richiede l'autenticazione a due fattori. Impostala per continuare.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Questo aggiunge un ulteriore livello di sicurezza al tuo account richiedendo un codice di verifica dalla tua app di autenticazione.",
|
||||
"Password is required": "La password è richiesta",
|
||||
"Password must be at least 8 characters": "La password deve essere di almeno 8 caratteri",
|
||||
"Please enter a 6-digit code": "Inserisci un codice a 6 cifre",
|
||||
"Password is required": "La password è obbligatoria",
|
||||
"Password must be at least 8 characters": "La password deve contenere almeno 8 caratteri",
|
||||
"Please enter a 6-digit code": "Inserisci un codice di 6 cifre",
|
||||
"Code must be exactly 6 digits": "Il codice deve essere esattamente di 6 cifre",
|
||||
"Enter the 6-digit code found in your authenticator app": "Inserisci il codice a 6 cifre trovato nella tua app di autenticazione",
|
||||
"Enter the 6-digit code found in your authenticator app": "Inserisci il codice di 6 cifre presente nella tua app di autenticazione",
|
||||
"Need help authenticating?": "Hai bisogno di aiuto per autenticarti?",
|
||||
"MFA QR Code": "Codice QR MFA",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Account creato con successo. Effettua l'accesso per impostare l'autenticazione a due fattori.",
|
||||
@@ -481,7 +548,7 @@
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Reimpostazione della password riuscita. Accedi con la tua nuova password per impostare l'autenticazione a due fattori.",
|
||||
"Password reset was successful. Please log in with your new password.": "Reimpostazione della password riuscita. Accedi con la tua nuova password.",
|
||||
"Two-factor authentication": "Autenticazione a due fattori",
|
||||
"Use authenticator app instead": "Usa l'app di autenticazione invece",
|
||||
"Use authenticator app instead": "Usa invece l'app di autenticazione",
|
||||
"Verify backup code": "Verifica codice di backup",
|
||||
"Use backup code": "Usa codice di backup",
|
||||
"Enter one of your backup codes": "Inserisci uno dei tuoi codici di backup",
|
||||
@@ -489,7 +556,7 @@
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Inserisci uno dei tuoi codici di backup. Ogni codice di backup può essere utilizzato solo una volta.",
|
||||
"Verify": "Verifica",
|
||||
"Trash": "Cestino",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Le pagine nel cestino verranno eliminate definitivamente dopo 30 giorni.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "Le pagine nel cestino verranno eliminate definitivamente dopo {{count}} giorni.",
|
||||
"Deleted": "Eliminato",
|
||||
"No pages in trash": "Nessuna pagina nel cestino",
|
||||
"Permanently delete page?": "Eliminare definitivamente la pagina?",
|
||||
@@ -514,20 +581,20 @@
|
||||
"Find a space": "Trova uno spazio",
|
||||
"Search in all your spaces": "Cerca in tutti i tuoi spazi",
|
||||
"Type": "Tipo",
|
||||
"Enterprise": "Impresa",
|
||||
"Enterprise": "Enterprise",
|
||||
"Download attachment": "Scarica allegato",
|
||||
"Allowed email domains": "Domini email consentiti",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Solo gli utenti con indirizzi email provenienti da questi domini possono registrarsi tramite SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Inserisci nomi di dominio validi separati da virgole o spazi",
|
||||
"Enforce two-factor authentication": "Imponi l'autenticazione a due fattori",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Solo gli utenti con indirizzi email di questi domini possono registrarsi tramite SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Inserisci nomi di dominio validi separati da virgola o spazio",
|
||||
"Enforce two-factor authentication": "Rendi obbligatoria l'autenticazione a due fattori",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Una volta impostata, tutti i membri devono abilitare l'autenticazione a due fattori per accedere all'area di lavoro.",
|
||||
"Toggle MFA enforcement": "Attiva disattiva l'applicazione MFA",
|
||||
"Toggle MFA enforcement": "Attiva/disattiva obbligatorietà MFA",
|
||||
"Display name": "Nome visualizzato",
|
||||
"Allow signup": "Consenti iscrizione",
|
||||
"Allow signup": "Consenti registrazione",
|
||||
"Enabled": "Abilitato",
|
||||
"Advanced Settings": "Impostazioni avanzate",
|
||||
"Enable TLS/SSL": "Abilita TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Usa connessione sicura al server LDAP",
|
||||
"Use secure connection to LDAP server": "Usa una connessione sicura al server LDAP",
|
||||
"Group sync": "Sincronizzazione gruppi",
|
||||
"No SSO providers found.": "Nessun provider SSO trovato.",
|
||||
"Delete SSO provider": "Elimina provider SSO",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Questa azione non può essere annullata. Qualsiasi applicazione che utilizza questa chiave API smetterà di funzionare.",
|
||||
"Update API key": "Aggiorna chiave API",
|
||||
"Manage API keys for all users in the workspace": "Gestisci le chiavi API per tutti gli utenti nell'area di lavoro",
|
||||
"Restrict API key creation to admins": "Limita la creazione delle chiavi API agli amministratori",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Solo gli amministratori e i proprietari possono creare nuove chiavi API. Le chiavi dei membri esistenti continueranno a funzionare.",
|
||||
"Toggle restrict API keys to admins": "Attiva/disattiva la limitazione delle chiavi API agli amministratori",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "La creazione delle chiavi API è limitata agli amministratori dal tuo amministratore dello spazio di lavoro.",
|
||||
"AI settings": "Impostazioni AI",
|
||||
"AI search": "Ricerca AI",
|
||||
"AI Answer": "Risposta AI",
|
||||
"Ask AI": "Chiedi all'AI",
|
||||
"AI is thinking...": "L'AI sta pensando...",
|
||||
"Thinking": "Sto pensando",
|
||||
"Ask a question...": "Fai una domanda...",
|
||||
"AI-powered search (Ask AI)": "Ricerca potenziata dall'AI (Chiedi all'AI)",
|
||||
"AI Answers": "Risposte AI",
|
||||
"AI-powered search (AI Answers)": "Ricerca con AI (Risposte AI)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "La ricerca AI utilizza embeddings vettoriali per fornire capacità di ricerca semantica nel contenuto della tua area di lavoro.",
|
||||
"Toggle AI search": "Attiva/disattiva ricerca AI",
|
||||
"Generative AI (Ask AI)": "AI generativa (Chiedi AI)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Abilita la generazione di contenuti con AI nell'editor. Consente agli utenti di generare, migliorare, tradurre e trasformare il testo.",
|
||||
"Toggle generative AI": "Attiva/Disattiva AI generativa",
|
||||
"Upgrade your plan": "Aggiorna il tuo piano",
|
||||
"Available with a paid license": "Disponibile con una licenza a pagamento",
|
||||
"Upgrade your license tier.": "Aggiorna il livello della tua licenza.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "L'IA è disponibile solo nell'edizione Enterprise di Docmost. Contatta sales@docmost.com.",
|
||||
"AI & MCP": "IA e MCP",
|
||||
"AI": "IA",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Model Context Protocol (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Abilita il server MCP per consentire ad assistenti e strumenti IA di interagire con i contenuti del tuo spazio di lavoro.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP è disponibile solo nell'edizione Enterprise di Docmost. Contatta sales@docmost.com.",
|
||||
"MCP Server URL": "URL del server MCP",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Usa la tua chiave API per l'autenticazione. Puoi gestire le chiavi API nelle impostazioni del tuo account.",
|
||||
"Supported tools": "Strumenti supportati",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Il tuo spazio di lavoro ha MCP abilitato. Usa la tua chiave API per collegare gli assistenti IA.",
|
||||
"MCP server URL:": "URL del server MCP:",
|
||||
"Learn more": "Scopri di più",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gestisci le API key per tutti gli utenti nello spazio di lavoro. Consulta la <anchor>documentazione API</anchor> per i dettagli sull'utilizzo.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Consulta la <anchor>documentazione API</anchor> per i dettagli sull'utilizzo.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Consulta la <anchor>documentazione MCP</anchor>.",
|
||||
"Sources": "Fonti",
|
||||
"Ask AI not available for attachments": "Chiedere all'AI non è disponibile per gli allegati",
|
||||
"AI Answers not available for attachments": "Risposte AI non disponibili per gli allegati",
|
||||
"No answer available": "Nessuna risposta disponibile",
|
||||
"Background color": "Colore di sfondo",
|
||||
"Highlight color": "Colore evidenziato",
|
||||
"Remove color": "Rimuovi colore"
|
||||
"Remove color": "Rimuovi colore",
|
||||
"Notifications": "Notifiche",
|
||||
"No notifications": "Nessuna notifica",
|
||||
"No unread notifications": "Nessuna notifica non letta",
|
||||
"All notifications": "Tutte le notifiche",
|
||||
"Unread only": "Solo non lette",
|
||||
"Mark all as read": "Segna tutto come letto",
|
||||
"Mark as read": "Segna come letto",
|
||||
"More options": "Altre opzioni",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> ti ha menzionato in un commento",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> ha commentato una pagina",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> ha risolto un commento",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> ti ha menzionato in una pagina",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> ti ha dato accesso in modifica a una pagina",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> ti ha dato accesso in visualizzazione a una pagina",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> ha aggiornato una pagina",
|
||||
"Watch page": "Segui pagina",
|
||||
"Stop watching": "Smetti di seguire",
|
||||
"Watch space": "Segui spazio",
|
||||
"Stop watching space": "Smetti di seguire lo spazio",
|
||||
"Email notifications": "Notifiche email",
|
||||
"Page updates": "Aggiornamenti pagina",
|
||||
"Get notified when pages you watch are updated.": "Ricevi una notifica quando le pagine che segui vengono aggiornate.",
|
||||
"Page mentions": "Menzioni nella pagina",
|
||||
"Get notified when someone mentions you on a page.": "Ricevi una notifica quando qualcuno ti menziona su una pagina.",
|
||||
"Comment mentions": "Menzioni nei commenti",
|
||||
"Get notified when someone mentions you in a comment.": "Ricevi una notifica quando qualcuno ti menziona in un commento.",
|
||||
"New comments": "Nuovi commenti",
|
||||
"Get notified about new comments on threads you participate in.": "Ricevi una notifica sui nuovi commenti nelle discussioni a cui partecipi.",
|
||||
"Resolved comments": "Commenti risolti",
|
||||
"Get notified when your comment is resolved.": "Ricevi una notifica quando il tuo commento viene risolto.",
|
||||
"You are now watching this page": "Ora stai seguendo questa pagina",
|
||||
"You are no longer watching this page": "Non stai più seguendo questa pagina",
|
||||
"You are now watching this space": "Ora stai seguendo questo spazio",
|
||||
"You are no longer watching this space": "Non stai più seguendo questo spazio",
|
||||
"Direct": "Diretto",
|
||||
"Updates": "Aggiornamenti",
|
||||
"Today": "Oggi",
|
||||
"Yesterday": "Ieri",
|
||||
"This week": "Questa settimana",
|
||||
"Older": "Più vecchie",
|
||||
"Restricted page": "Pagina con accesso ristretto",
|
||||
"Restricted pages cannot be shared publicly.": "Le pagine con accesso ristretto non possono essere condivise pubblicamente.",
|
||||
"Restricted by parent": "Limitata dalla pagina genitore",
|
||||
"Restricted": "Limitata",
|
||||
"Open": "Aperta",
|
||||
"Inherits restrictions from ancestor page": "Eredita le restrizioni dalla pagina genitore",
|
||||
"Only people listed below can access this page": "Solo le persone elencate di seguito possono accedere a questa pagina",
|
||||
"Everyone in this space can access": "Chiunque in questo spazio può accedere",
|
||||
"No additional restrictions on this page": "Nessuna restrizione aggiuntiva su questa pagina",
|
||||
"Only specific people can access": "Solo persone specifiche possono accedere",
|
||||
"Use only inherited restrictions": "Usa solo le restrizioni ereditate",
|
||||
"Add restrictions on top of inherited": "Aggiungi restrizioni oltre a quelle ereditate",
|
||||
"Inherited restriction": "Restrizione ereditata",
|
||||
"Access limited by": "Accesso limitato da",
|
||||
"Restrict access to control who can view and edit this page": "Limita l'accesso per controllare chi può visualizzare e modificare questa pagina",
|
||||
"Add additional restrictions specific to this page": "Aggiungi restrizioni aggiuntive specifiche per questa pagina",
|
||||
"Access": "Accesso",
|
||||
"People with access": "Persone con accesso",
|
||||
"Remove all": "Rimuovi tutto",
|
||||
"Remove access": "Rimuovi accesso",
|
||||
"Remove all access": "Rimuovi tutti gli accessi",
|
||||
"Are you sure you want to remove this member's access to the page?": "Sei sicuro di voler rimuovere l'accesso di questo membro alla pagina?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Sei sicuro di voler rimuovere tutti gli accessi specifici? Questo renderà la pagina accessibile a tutti nello spazio.",
|
||||
"Trash retention": "Conservazione del cestino",
|
||||
"Pages in trash will be permanently deleted after this period.": "Le pagine nel cestino verranno eliminate definitivamente dopo questo periodo.",
|
||||
"Trash retention updated": "Conservazione del cestino aggiornata",
|
||||
"Failed to update trash retention": "Impossibile aggiornare la conservazione del cestino",
|
||||
"Removed page restriction": "Restrizione della pagina rimossa",
|
||||
"Added page permission": "Permesso sulla pagina aggiunto",
|
||||
"Removed page permission": "Permesso sulla pagina rimosso",
|
||||
"day": "giorno",
|
||||
"days": "giorni",
|
||||
"week": "settimana",
|
||||
"weeks": "settimane",
|
||||
"month": "mese",
|
||||
"months": "mesi",
|
||||
"year": "anno",
|
||||
"years": "anni",
|
||||
"Period": "Periodo",
|
||||
"Fixed date": "Data fissa",
|
||||
"Indefinitely": "A tempo indeterminato",
|
||||
"Days": "Giorni",
|
||||
"Weeks": "Settimane",
|
||||
"Months": "Mesi",
|
||||
"Years": "Anni",
|
||||
"Pick a date": "Scegli una data",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Il massimo consentito è {{max}} {{unit}} per questa unità",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Non scade mai. I verificatori possono verificare nuovamente in qualsiasi momento.",
|
||||
"Verified": "Verificato",
|
||||
"Review needed": "Revisione necessaria",
|
||||
"Verification expired": "Verifica scaduta",
|
||||
"Draft": "Bozza",
|
||||
"In Approval": "In approvazione",
|
||||
"In approval": "In approvazione",
|
||||
"Approved": "Approvato",
|
||||
"Obsolete": "Obsoleto",
|
||||
"Expiring": "In scadenza",
|
||||
"Set up verification": "Configura la verifica",
|
||||
"Verify page": "Verifica la pagina",
|
||||
"Page verification": "Verifica della pagina",
|
||||
"Add verification": "Aggiungi verifica",
|
||||
"Edit verification": "Modifica verifica",
|
||||
"Search by title": "Cerca per titolo",
|
||||
"Choose how this page should stay accurate.": "Scegli come mantenere accurata questa pagina.",
|
||||
"Recurring verification": "Verifica ricorrente",
|
||||
"Verifiers re-confirm this page on a schedule.": "I verificatori riconfermano questa pagina secondo una pianificazione.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Verifica nuovamente secondo una pianificazione (ad es. ogni 30 giorni)",
|
||||
"Page stays editable at all times": "La pagina resta sempre modificabile",
|
||||
"Best for runbooks, FAQs, living documentation": "Ideale per runbook, FAQ e documentazione dinamica",
|
||||
"Approval workflow": "Flusso di approvazione",
|
||||
"Formal document lifecycle with named approvers.": "Ciclo di vita formale del documento con approvatori nominati.",
|
||||
"Draft → In approval → Approved → Obsolete": "Bozza → In approvazione → Approvato → Obsoleto",
|
||||
"Locked once approved, with full history": "Bloccato una volta approvato, con cronologia completa",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Progettato per ISO 9001, ISO 13485 e FDA",
|
||||
"Best for SOPs and controlled documents": "Ideale per SOP e documenti controllati",
|
||||
"Back": "Indietro",
|
||||
"Quality management": "Gestione della qualità",
|
||||
"Recurring": "Ricorrente",
|
||||
"Pages move through draft, approval, and approved stages.": "Le pagine passano attraverso le fasi di bozza, approvazione e approvato.",
|
||||
"Verifiers": "Verificatori",
|
||||
"Add verifier": "Aggiungi verificatore",
|
||||
"I've reviewed this page for accuracy": "Ho controllato l'accuratezza di questa pagina",
|
||||
"Set up": "Configura",
|
||||
"Remove verification": "Rimuovi verifica",
|
||||
"Are you sure you want to remove verification from this page?": "Sei sicuro di voler rimuovere la verifica da questa pagina?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "I verificatori assegnati devono verificare nuovamente questa pagina periodicamente.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Ultima verifica effettuata da {{name}} {{time}} (scaduta)",
|
||||
"The fixed expiration date has passed.": "La data di scadenza fissa è trascorsa.",
|
||||
"Verified by {{name}} {{time}}": "Verificato da {{name}} {{time}}",
|
||||
"Expires {{date}}": "Scade il {{date}}",
|
||||
"Expired {{date}}": "Scaduto il {{date}}",
|
||||
"Mark as obsolete": "Contrassegna come obsoleto",
|
||||
"Mark obsolete": "Contrassegna come obsoleto",
|
||||
"Returned by {{name}} {{time}}": "Restituito da {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Non è stata ancora richiesta alcuna approvazione.",
|
||||
"Submitted by {{name}} {{time}}": "Inviato da {{name}} {{time}}",
|
||||
"Someone": "Qualcuno",
|
||||
"Approved by {{name}} {{time}}": "Approvato da {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Questo documento è stato contrassegnato come obsoleto.",
|
||||
"Rejection comment": "Commento di rifiuto",
|
||||
"Reason for returning this document...": "Motivo della restituzione di questo documento...",
|
||||
"Confirm rejection": "Conferma rifiuto",
|
||||
"Submit for approval": "Invia per approvazione",
|
||||
"Reject": "Rifiuta",
|
||||
"Approve": "Approva",
|
||||
"Re-submit for approval": "Invia nuovamente per approvazione",
|
||||
"Verified until": "Verificato fino al",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "Pagine verificate",
|
||||
"Search pages...": "Cerca pagine...",
|
||||
"Filter by space": "Filtra per spazio",
|
||||
"Filter by type": "Filtra per tipo",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> ha verificato una pagina",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> ha inviato una pagina per la tua approvazione",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> ha restituito una pagina per la revisione",
|
||||
"Page verification expires soon": "La verifica della pagina scadrà presto",
|
||||
"Page verification has expired": "La verifica della pagina è scaduta",
|
||||
"Verifying your email": "Verifica della tua email in corso",
|
||||
"Please wait...": "Attendere...",
|
||||
"Verification failed. The link may have expired.": "Verifica non riuscita. Il link potrebbe essere scaduto.",
|
||||
"Check your email": "Controlla la tua email",
|
||||
"We sent a verification link to {{email}}.": "Abbiamo inviato un link di verifica a {{email}}.",
|
||||
"We sent a verification link to your email.": "Abbiamo inviato un link di verifica alla tua email.",
|
||||
"Click the link to verify your email and access your workspace.": "Clicca sul link per verificare la tua email e accedere al tuo workspace.",
|
||||
"Resend verification email": "Invia nuovamente email di verifica",
|
||||
"Verification email sent. Please check your inbox.": "Email di verifica inviata. Controlla la tua casella di posta.",
|
||||
"Failed to resend verification email. Please try again.": "Invio dell'email di verifica non riuscito. Si prega di riprovare.",
|
||||
"We've sent you an email with your associated workspaces.": "Ti abbiamo inviato un'email con i workspace associati.",
|
||||
"Load more": "Carica altro",
|
||||
"Log out of all devices": "Disconnetti da tutti i dispositivi",
|
||||
"Log out of all sessions except this device": "Disconnetti da tutte le sessioni tranne questo dispositivo",
|
||||
"This Device": "Questo dispositivo",
|
||||
"Unknown device": "Dispositivo sconosciuto",
|
||||
"No active sessions": "Nessuna sessione attiva",
|
||||
"Session revoked": "Sessione revocata",
|
||||
"All other sessions revoked": "Tutte le altre sessioni sono state revocate",
|
||||
"Last used": "Ultimo utilizzo",
|
||||
"Created": "Creato",
|
||||
"Rename": "Rinomina",
|
||||
"Publish": "Pubblica",
|
||||
"Security": "Sicurezza",
|
||||
"Enforce SSO": "Rendi obbligatorio SSO",
|
||||
"Once enforced, members will not be able to login with email and password.": "Una volta reso obbligatorio, i membri non potranno accedere con email e password.",
|
||||
"AI-generated content may not be accurate.": "I contenuti generati dall'IA potrebbero non essere accurati.",
|
||||
"AI Chat": "Chat IA",
|
||||
"Analyze for insights": "Analizza per ottenere approfondimenti",
|
||||
"Ask anything...": "Chiedi qualsiasi cosa...",
|
||||
"Chat history": "Cronologia chat",
|
||||
"Chat name": "Nome chat",
|
||||
"Close": "Chiudi",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Caricamento della chat non riuscito. Si è verificato un errore.",
|
||||
"Failed to render this message.": "Impossibile visualizzare questo messaggio.",
|
||||
"How can I help you today?": "Come posso aiutarti oggi?",
|
||||
"New chat": "Nuova chat",
|
||||
"No chat history": "Nessuna cronologia chat",
|
||||
"No chats found": "Nessuna chat trovata",
|
||||
"No conversations yet": "Nessuna conversazione al momento",
|
||||
"Open full page": "Apri pagina completa",
|
||||
"Previous 7 days": "Ultimi 7 giorni",
|
||||
"Previous 30 days": "Ultimi 30 giorni",
|
||||
"Search chats...": "Cerca nelle chat...",
|
||||
"Start a new chat to see it here.": "Avvia una nuova chat per vederla qui.",
|
||||
"Summarize this page": "Riassumi questa pagina",
|
||||
"Toggle AI Chat": "Attiva/disattiva Chat IA",
|
||||
"Translate this page": "Traduci questa pagina",
|
||||
"Try a different search term.": "Prova un termine di ricerca diverso.",
|
||||
"Try again": "Riprova",
|
||||
"Untitled chat": "Chat senza titolo",
|
||||
"What can I help you with?": "Con cosa posso aiutarti?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "メンバーを追加",
|
||||
"Add to groups": "グループに追加",
|
||||
"Add space members": "スペースメンバーを追加",
|
||||
"Add to favorites": "お気に入りに追加",
|
||||
"Admin": "管理者",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "このグループを削除してもよろしいですか? メンバーはこのグループがアクセス権を持つリソースにアクセスできなくなります。",
|
||||
"Are you sure you want to delete this page?": "このページを削除してもよろしいですか?",
|
||||
@@ -44,15 +45,15 @@
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "このページを削除してもよろしいですか?子ページとページ履歴も削除されます。この操作は取り消せません。",
|
||||
"Description": "説明",
|
||||
"Details": "詳細",
|
||||
"e.g ACME": "例: 山田太郎",
|
||||
"e.g ACME Inc": "例: 株式会社サンプル",
|
||||
"e.g Developers": "例: エンジニア",
|
||||
"e.g Group for developers": "例: 開発チーム",
|
||||
"e.g ACME": "例: ACME",
|
||||
"e.g ACME Inc": "例: ACME Inc",
|
||||
"e.g Developers": "例: 開発者",
|
||||
"e.g Group for developers": "例: 開発者向けグループ",
|
||||
"e.g product": "例: product",
|
||||
"e.g Product Team": "例: プロダクトチーム",
|
||||
"e.g Sales": "例: 営業部",
|
||||
"e.g Sales": "例: 営業",
|
||||
"e.g Space for product team": "例: プロダクトチーム用スペース",
|
||||
"e.g Space for sales team to collaborate": "例: 営業チーム用スペース",
|
||||
"e.g Space for sales team to collaborate": "例: 営業チームがコラボレーションするためのスペース",
|
||||
"Edit": "編集",
|
||||
"Read": "閲覧",
|
||||
"Edit group": "グループを編集",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "ページのインポートに失敗しました",
|
||||
"Failed to load page. An error occurred.": "ページの読み込みに失敗しました。エラーが発生しました。",
|
||||
"Failed to update data": "データの更新に失敗しました",
|
||||
"Favorite spaces": "お気に入りのスペース",
|
||||
"Favorite spaces appear here": "お気に入りのスペースがここに表示されます",
|
||||
"Favorites": "お気に入り",
|
||||
"Full access": "フルアクセス",
|
||||
"Full page width": "フルページ幅で表示",
|
||||
"Full width": "左右の余白を縮小",
|
||||
@@ -87,11 +91,12 @@
|
||||
"Import pages": "ページをインポート",
|
||||
"Import pages & space settings": "ページとスペース設定をインポート",
|
||||
"Importing pages": "ページをインポートしています",
|
||||
"invalid invitation link": "無効な招待リンクです",
|
||||
"invalid invitation link": "招待リンクが無効です",
|
||||
"Invitation signup": "招待登録",
|
||||
"Invite by email": "メールアドレスで招待する",
|
||||
"Invite members": "メンバーを招待する",
|
||||
"Invite new members": "新しいメンバーを招待する",
|
||||
"Invite People": "ユーザーを招待",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "招待を承諾していないメンバーがここに表示されます",
|
||||
"Invited members will be granted access to spaces the groups can access": "招待されたメンバーはグループがアクセスできるスペースにアクセスできます",
|
||||
"Join the workspace": "ワークスペースに参加",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "グループが見つかりません",
|
||||
"No page history saved yet.": "ページ履歴がありません",
|
||||
"No pages yet": "ページがありません",
|
||||
"No shared pages": "共有ページはありません。",
|
||||
"No results found...": "結果が見つかりません",
|
||||
"No user found": "ユーザーが見つかりません",
|
||||
"Overview": "概要",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "ページ",
|
||||
"Password": "パスワード",
|
||||
"Password changed successfully": "パスワードを変更しました",
|
||||
"People": "メンバー",
|
||||
"Pending": "保留中",
|
||||
"Please confirm your action": "アクションを確認してください",
|
||||
"Preferences": "設定",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "プロフィール",
|
||||
"Recently updated": "最近の更新",
|
||||
"Remove": "削除",
|
||||
"Remove from favorites": "お気に入りから削除",
|
||||
"Remove group member": "グループメンバーを削除",
|
||||
"Remove space member": "スペースメンバーを削除",
|
||||
"Restore": "復元",
|
||||
@@ -149,37 +157,38 @@
|
||||
"Search...": "検索",
|
||||
"Select language": "言語を選択",
|
||||
"Select role": "ロールを選択",
|
||||
"Select role to assign to all invited members": "招待するメンバーに割り当てるロールを選択",
|
||||
"Select role to assign to all invited members": "招待したすべてのメンバーに割り当てるロールを選択",
|
||||
"Select theme": "テーマを選択",
|
||||
"Send invitation": "招待を送る",
|
||||
"Send invitation": "招待を送信",
|
||||
"Invitation sent": "招待を送信しました",
|
||||
"Settings": "設定",
|
||||
"Setup workspace": "ワークスペースを設定する",
|
||||
"Setup workspace": "ワークスペースを設定",
|
||||
"Sign In": "サインイン",
|
||||
"Sign Up": "新規登録",
|
||||
"Slug": "スラッグ(URL識別子)",
|
||||
"Sign Up": "サインアップ",
|
||||
"Slug": "スラッグ",
|
||||
"Space": "スペース",
|
||||
"Space description": "スペース説明",
|
||||
"Space description": "スペースの説明",
|
||||
"Space menu": "スペースメニュー",
|
||||
"Space name": "スペース名",
|
||||
"Space settings": "スペース設定",
|
||||
"Space slug": "スペースのスラッグ(URL識別子)",
|
||||
"Space slug": "スペーススラッグ",
|
||||
"Spaces": "スペース",
|
||||
"Spaces you belong to": "所属しているスペース",
|
||||
"No space found": "スペースが見つかりません",
|
||||
"Search for spaces": "スペースを検索",
|
||||
"Start typing to search...": "入力して検索",
|
||||
"Status": "ステータス",
|
||||
"Successfully imported": "インポートしました",
|
||||
"Successfully restored": "復元しました",
|
||||
"Successfully imported": "正常にインポートされました",
|
||||
"Successfully restored": "正常に復元されました",
|
||||
"System settings": "システム設定",
|
||||
"Templates": "テンプレート",
|
||||
"Theme": "テーマ",
|
||||
"To change your email, you have to enter your password and new email.": "メールアドレスを変更するには、パスワードと新しいメールアドレスを入力してください",
|
||||
"Toggle full page width": "ページ幅を切り替え",
|
||||
"Toggle full page width": "ページ全幅表示を切り替え",
|
||||
"Unable to import pages. Please try again.": "ページをインポートできませんでした。もう一度お試しください",
|
||||
"untitled": "無題",
|
||||
"Untitled": "無題",
|
||||
"Updated successfully": "更新しました",
|
||||
"Updated successfully": "正常に更新されました",
|
||||
"User": "ユーザー",
|
||||
"Workspace": "ワークスペース",
|
||||
"Workspace Name": "ワークスペース名",
|
||||
@@ -187,16 +196,16 @@
|
||||
"You can change your password here.": "パスワードを変更できます",
|
||||
"Your Email": "メールアドレス",
|
||||
"Your import is complete.": "インポートが完了しました",
|
||||
"Your name": "名前",
|
||||
"Your Name": "名前",
|
||||
"Your name": "あなたの名前",
|
||||
"Your Name": "あなたの名前",
|
||||
"Your password": "パスワード",
|
||||
"Your password must be a minimum of 8 characters.": "パスワードは8文字以上にしてください",
|
||||
"Sidebar toggle": "サイドバー切り替え",
|
||||
"Comments": "コメント",
|
||||
"404 page not found": "404 ページが見つかりません",
|
||||
"Sorry, we can't find the page you are looking for.": "お探しのページが見つかりません",
|
||||
"Take me back to homepage": "ホームに戻る",
|
||||
"Forgot password": "パスワードを忘れた",
|
||||
"Take me back to homepage": "ホームページに戻る",
|
||||
"Forgot password": "パスワードを忘れた場合",
|
||||
"Forgot your password?": "パスワードを忘れましたか?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "パスワードリセット用のリンクをメールに送信しました。受信トレイを確認してください",
|
||||
"Send reset link": "リセットリンクを送信",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "返信...",
|
||||
"Error loading comments.": "コメントの読み込みに失敗しました",
|
||||
"No comments yet.": "コメントがありません",
|
||||
"No open comments.": "未解決のコメントはありません。",
|
||||
"No resolved comments.": "解決済みのコメントはありません",
|
||||
"Add a comment...": "コメントを追加...",
|
||||
"Edit comment": "コメントを編集する",
|
||||
"Delete comment": "コメントを削除する",
|
||||
"Are you sure you want to delete this comment?": "このコメントを削除してもよろしいですか?",
|
||||
"Delete chat": "チャットを削除",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "「{{title}}」を削除してもよろしいですか?この操作は元に戻せません。",
|
||||
"Comment created successfully": "コメントを作成しました",
|
||||
"Error creating comment": "コメントの作成に失敗しました",
|
||||
"Comment updated successfully": "コメントを更新しました",
|
||||
@@ -217,18 +231,17 @@
|
||||
"Comment deleted successfully": "コメントを削除しました",
|
||||
"Failed to delete comment": "コメントの削除に失敗しました",
|
||||
"Comment resolved successfully": "コメントを解決しました",
|
||||
"Comment re-opened successfully": "コメントを再開しました",
|
||||
"Comment unresolved successfully": "コメントを未解決に戻しました",
|
||||
"Comment re-opened successfully": "コメントを正常に再オープンしました",
|
||||
"Comment unresolved successfully": "コメントの解決を正常に取り消しました",
|
||||
"Failed to resolve comment": "コメントの解決に失敗しました",
|
||||
"Resolve comment": "コメントを解決",
|
||||
"Unresolve comment": "コメントを未解決に戻す",
|
||||
"Unresolve comment": "コメントの解決を取り消す",
|
||||
"Resolve Comment Thread": "コメントスレッドを解決",
|
||||
"Unresolve Comment Thread": "コメントスレッドを未解決に戻す",
|
||||
"Unresolve Comment Thread": "コメントスレッドの解決を取り消す",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "このコメントスレッドを解決しますか?完了としてマークされます",
|
||||
"Are you sure you want to unresolve this comment thread?": "このコメントスレッドを未解決に戻しますか?",
|
||||
"Resolved": "解決済",
|
||||
"Resolved": "解決済み",
|
||||
"No active comments.": "アクティブなコメントはありません",
|
||||
"No resolved comments.": "解決済みのコメントはありません",
|
||||
"Revoke invitation": "招待を取り消す",
|
||||
"Revoke": "取り消す",
|
||||
"Don't": "取り消さない",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "このスペースを削除してもよろしいですか?",
|
||||
"Delete this space with all its pages and data.": "このスペースとすべてのページ、データを削除します",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "スペース内のすべてのページ、コメント、添付ファイル、権限が完全に削除されます",
|
||||
"Confirm space name": "スペース名を確認する",
|
||||
"Confirm space name": "スペース名を確認",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "確認のためスペース名 <b>{{spaceName}}</b> を入力してください",
|
||||
"Format": "フォーマット",
|
||||
"Include subpages": "サブページを含める",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "下に行を追加",
|
||||
"Delete table": "テーブルを削除",
|
||||
"Info": "情報",
|
||||
"Note": "ノート",
|
||||
"Success": "成功",
|
||||
"Warning": "警告",
|
||||
"Danger": "危険",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "保存して終了",
|
||||
"Double-click to edit Excalidraw diagram": "ダブルクリックして Excalidraw 図を編集",
|
||||
"Paste link": "リンクを貼り付け",
|
||||
"Paste link or search pages": "リンクを貼り付けるかページを検索してください。 ",
|
||||
"Link to web page": "ウェブページへのリンク",
|
||||
"Recents": "最近使用したもの",
|
||||
"Page or URL": "ページまたはURL",
|
||||
"Link title": "リンクタイトル",
|
||||
"Edit link": "リンクを編集",
|
||||
"Remove link": "リンクを削除",
|
||||
"Add link": "リンクを追加",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "ピンク色",
|
||||
"Gray": "灰色",
|
||||
"Embed link": "リンクを埋め込む",
|
||||
"Invalid {{provider}} embed link": "埋め込まれた {{provider}} のリンクは無効です",
|
||||
"Invalid {{provider}} embed link": "{{provider}} の埋め込みリンクが無効です",
|
||||
"Embed {{provider}}": "埋め込まれた {{provider}}",
|
||||
"Enter {{provider}} link to embed": "埋め込みたい {{provider}} のリンクを入力してください",
|
||||
"Bold": "太字",
|
||||
@@ -331,55 +350,83 @@
|
||||
"Insert horizontal rule divider": "区切り線を挿入します",
|
||||
"Upload any image from your device.": "デバイスから画像をアップロードします",
|
||||
"Upload any video from your device.": "デバイスから動画をアップロードします",
|
||||
"Upload any audio from your device.": "デバイスから音声ファイルをアップロードします。",
|
||||
"Upload any file from your device.": "デバイスからファイルをアップロードします",
|
||||
"Uploading {{name}}": "{{name}} をアップロード中",
|
||||
"Uploading file": "ファイルをアップロード中",
|
||||
"Table": "テーブル",
|
||||
"Table": "表",
|
||||
"Insert a table.": "テーブルを挿入します",
|
||||
"Insert collapsible block.": "折りたたみブロックを挿入します",
|
||||
"Video": "動画",
|
||||
"Divider": "区切り線",
|
||||
"Quote": "引用",
|
||||
"Image": "画像",
|
||||
"Audio": "音声",
|
||||
"Embed PDF": "PDFを埋め込む",
|
||||
"Upload and embed a PDF file.": "PDFファイルをアップロードして埋め込みます。",
|
||||
"Embed as PDF": "PDFとして埋め込む",
|
||||
"Failed to load PDF": "PDFの読み込みに失敗しました",
|
||||
"Convert to attachment": "添付ファイルに変換",
|
||||
"File attachment": "ファイル添付",
|
||||
"Toggle block": "ブロックを切り替える",
|
||||
"Toggle block": "トグルブロック",
|
||||
"Callout": "コールアウト",
|
||||
"Insert callout notice.": "コールアウトを挿入します",
|
||||
"Math inline": "インライン数式",
|
||||
"Insert inline math equation.": "インライン数式を挿入します",
|
||||
"Math block": "数式ブロック",
|
||||
"Insert math equation": "数式を挿入します",
|
||||
"Insert math equation": "数式を挿入",
|
||||
"Mermaid diagram": "Mermaid ダイアグラム",
|
||||
"Insert mermaid diagram": "Mermaid ダイアグラムを挿入します",
|
||||
"Insert and design Drawio diagrams": "Draw.io 図を挿入・編集します",
|
||||
"Insert current date": "現在の日付を挿入します",
|
||||
"Draw and sketch excalidraw diagrams": "Excalidraw 図を挿入します",
|
||||
"Insert mermaid diagram": "Mermaid ダイアグラムを挿入",
|
||||
"Insert and design Drawio diagrams": "Drawio ダイアグラムを挿入して作成",
|
||||
"Insert current date": "現在の日付を挿入",
|
||||
"Draw and sketch excalidraw diagrams": "Excalidraw ダイアグラムを描画・スケッチ",
|
||||
"Multiple": "複数",
|
||||
"Turn into": "変換する",
|
||||
"Text align": "テキストの配置",
|
||||
"This page may have been deleted, moved, or you may not have access.": "このページは削除されたか移動されたか、またはアクセス権がない可能性があります。},{",
|
||||
"Go to homepage": "ホームページへ移動",
|
||||
"Pages you create will show up here.": "ここに作成したページが表示されます。",
|
||||
"Heading {{level}}": "見出し {{level}}",
|
||||
"Toggle title": "タイトルの表示/非表示を切り替える",
|
||||
"Write anything. Enter \"/\" for commands": "文字を入力するか、「/」でコマンドを呼び出します",
|
||||
"Toggle title": "トグルタイトル",
|
||||
"Write anything. Enter \"/\" for commands": "何でも入力してください。コマンドを使うには「/」を入力",
|
||||
"Write...": "ここに入力...",
|
||||
"Column count": "列数",
|
||||
"{{count}} Columns": "{{count}}列",
|
||||
"Equal columns": "均等な列",
|
||||
"Left sidebar": "左サイドバー",
|
||||
"Right sidebar": "右サイドバー",
|
||||
"Wide center": "中央ワイド",
|
||||
"Left wide": "左ワイド",
|
||||
"Right wide": "右ワイド",
|
||||
"Names do not match": "名前が一致しません",
|
||||
"Today, {{time}}": "今日、{{time}}",
|
||||
"Yesterday, {{time}}": "昨日、{{time}}",
|
||||
"Space created successfully": "スペースを作成しました",
|
||||
"Space updated successfully": "スペースを更新しました",
|
||||
"Space deleted successfully": "スペースを削除しました",
|
||||
"Members added successfully": "メンバーを追加しました",
|
||||
"Member removed successfully": "メンバーを削除しました",
|
||||
"Member role updated successfully": "メンバーのロールを更新しました",
|
||||
"Today, {{time}}": "今日 {{time}}",
|
||||
"Yesterday, {{time}}": "昨日 {{time}}",
|
||||
"Space created successfully": "スペースが正常に作成されました",
|
||||
"Space updated successfully": "スペースが正常に更新されました",
|
||||
"Space deleted successfully": "スペースが正常に削除されました",
|
||||
"Members added successfully": "メンバーが正常に追加されました",
|
||||
"Member removed successfully": "メンバーが正常に削除されました",
|
||||
"Member role updated successfully": "メンバーのロールが正常に更新されました",
|
||||
"Created by: <b>{{creatorName}}</b>": "作成者: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "作成日: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "最終編集: {{name}} {{time}}",
|
||||
"Created at: {{time}}": "作成日時: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "{{name}} が {{time}} に編集",
|
||||
"Word count: {{wordCount}}": "単語数: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "文字数: {{characterCount}}",
|
||||
"New update": "新規更新",
|
||||
"New update": "新しいアップデート",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} が利用可能です",
|
||||
"Default page edit mode": "デフォルトのページ編集モード",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "お好みのページ編集モードを選択してください(誤編集を防止します)",
|
||||
"Reading": "読み取り",
|
||||
"Delete member": "メンバーを削除する",
|
||||
"Member deleted successfully": "メンバーを削除しました",
|
||||
"Reading": "閲覧",
|
||||
"Delete member": "メンバーを削除",
|
||||
"Member deleted successfully": "メンバーが正常に削除されました",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "このメンバーを削除してもよろしいですか?この操作は取り消せません",
|
||||
"Deactivate member": "メンバーを無効化",
|
||||
"Activate member": "メンバーを有効化",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "本当にこのワークスペースのメンバーを無効化しますか?無効化すると、このワークスペースにアクセスできなくなります。",
|
||||
"Are you sure you want to activate this workspace member?": "本当にこのワークスペースのメンバーを有効化しますか?",
|
||||
"Deactivate": "無効化",
|
||||
"Activate": "有効化",
|
||||
"Deactivated": "無効化済み",
|
||||
"Move": "移動",
|
||||
"Move page": "ページを移動",
|
||||
"Move page to a different space.": "ページを別のスペースに移動します",
|
||||
@@ -390,75 +437,95 @@
|
||||
"Public sharing": "公開共有",
|
||||
"Shared by": "共有者",
|
||||
"Shared at": "共有日時",
|
||||
"Inherits public sharing from": "から公開共有を継承する",
|
||||
"Share to web": "ウェブで共有",
|
||||
"Shared to web": "ウェブに共有済み",
|
||||
"Anyone with the link can view this page": "リンクを持っている人はこのページを閲覧できます",
|
||||
"Make this page publicly accessible": "このページを公開します",
|
||||
"Include sub-pages": "サブページを含む",
|
||||
"Inherits public sharing from": "公開共有を次から継承",
|
||||
"Share to web": "Web に公開",
|
||||
"Shared to web": "Web に公開済み",
|
||||
"Anyone with the link can view this page": "リンクを知っている人は誰でもこのページを閲覧できます",
|
||||
"Make this page publicly accessible": "このページを公開アクセス可能にする",
|
||||
"Include sub-pages": "サブページを含める",
|
||||
"Make sub-pages public too": "サブページも公開する",
|
||||
"Allow search engines to index page": "検索エンジンにページのインデックス作成を許可する",
|
||||
"Allow search engines to index page": "検索エンジンによるページのインデックスを許可",
|
||||
"Open page": "ページを開く",
|
||||
"Page": "ページ",
|
||||
"Delete public share link": "公開リンクを削除",
|
||||
"Delete public share link": "公開共有リンクを削除",
|
||||
"Delete share": "共有を削除",
|
||||
"Are you sure you want to delete this shared link?": "この共有リンクを削除してもよろしいですか?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "メンバーであるスペースからの公開ページがここに表示されます",
|
||||
"Share deleted successfully": "共有を削除しました",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "あなたがメンバーであるスペースの公開共有ページがここに表示されます",
|
||||
"Share deleted successfully": "共有が正常に削除されました",
|
||||
"Share not found": "共有が見つかりません",
|
||||
"Failed to share page": "ページの共有に失敗しました",
|
||||
"Disable public sharing": "公開共有を無効にする",
|
||||
"Prevent members from sharing pages publicly.": "メンバーがページを公開で共有するのを防ぐ。",
|
||||
"Toggle public sharing": "公開共有を切り替える",
|
||||
"Toggle space public sharing": "スペースの公開共有を切り替える",
|
||||
"Allow viewers to comment": "閲覧者によるコメントを許可",
|
||||
"Allow viewers to add comments on pages in this space.": "このスペース内のページに閲覧者がコメントを追加できるようにします。",
|
||||
"Toggle viewer comments": "閲覧者コメントの切り替え",
|
||||
"Public sharing is disabled at the workspace level": "ワークスペースレベルで公開共有が無効になっています",
|
||||
"Prevent pages in this space from being shared publicly.": "このスペース内のページが公開で共有されるのを防ぐ。",
|
||||
"Page permissions": "ページのアクセス権",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "個々のページを誰が表示・編集できるかを制御します。エンタープライズライセンスで利用可能です。",
|
||||
"Enable public sharing": "公開共有を有効にする",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "本当に公開共有を有効にしますか?メンバーはページを公開で共有できるようになります。",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "本当に公開共有を無効にしますか?このワークスペース内のすべての既存の共有リンクが削除されます。",
|
||||
"Are you sure you want to enable public sharing for this space?": "本当にこのスペースの公開共有を有効にしますか?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "本当に公開共有を無効にしますか?このスペースのすべての既存の共有リンクが削除されます。",
|
||||
"Public sharing is disabled": "公開共有が無効になっています",
|
||||
"Public sharing has been disabled at the workspace level.": "ワークスペースレベルで公開共有が無効になりました。",
|
||||
"Public sharing has been disabled for this space.": "このスペースで公開共有が無効になりました。",
|
||||
"Copy page": "ページをコピー",
|
||||
"Copy page to a different space.": "ページを別のスペースにコピーします",
|
||||
"Page copied successfully": "ページをコピーしました",
|
||||
"Page duplicated successfully": "ページを複製しました",
|
||||
"Page copied successfully": "ページが正常にコピーされました",
|
||||
"Page duplicated successfully": "ページが正常に複製されました",
|
||||
"Find": "検索",
|
||||
"Not found": "見つかりません",
|
||||
"Previous Match (Shift+Enter)": "前の一致 (Shift+Enter)",
|
||||
"Next match (Enter)": "次の一致 (Enter)",
|
||||
"Match case (Alt+C)": "大文字小文字を区別 (Alt+C)",
|
||||
"Match case (Alt+C)": "大文字と小文字を区別 (Alt+C)",
|
||||
"Replace": "置換",
|
||||
"Close (Escape)": "閉じる (Escape)",
|
||||
"Replace (Enter)": "置換 (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "すべて置換 (Ctrl+Alt+Enter)",
|
||||
"Replace all": "すべて置換",
|
||||
"View all": "すべて表示",
|
||||
"View all spaces": "すべてのスペースを表示",
|
||||
"Error": "エラー",
|
||||
"Failed to disable MFA": "MFAの無効化に失敗しました",
|
||||
"Failed to disable MFA": "MFA を無効化できませんでした",
|
||||
"Disable two-factor authentication": "二要素認証を無効化",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "二要素認証を無効にすると、アカウントのセキュリティが低下します。サインインにはパスワードのみが必要になります",
|
||||
"Please enter your password to disable two-factor authentication:": "二要素認証を無効にするにはパスワードを入力してください",
|
||||
"Two-factor authentication has been enabled": "二要素認証を有効にしました",
|
||||
"Two-factor authentication has been disabled": "二要素認証を無効にしました",
|
||||
"2-step verification": "2段階認証",
|
||||
"Two-factor authentication has been enabled": "二要素認証が有効になりました",
|
||||
"Two-factor authentication has been disabled": "二要素認証が無効になりました",
|
||||
"2-step verification": "2 段階認証",
|
||||
"Protect your account with an additional verification layer when signing in.": "サインイン時に追加の認証でアカウントを保護します",
|
||||
"Two-factor authentication is active on your account.": "二要素認証が有効です",
|
||||
"Add 2FA method": "2FAメソッドを追加",
|
||||
"Add 2FA method": "2FA 方法を追加",
|
||||
"Backup codes": "バックアップコード",
|
||||
"Disable": "無効にする",
|
||||
"Invalid verification code": "無効な認証コード",
|
||||
"New backup codes have been generated": "新しいバックアップコードを生成しました",
|
||||
"Disable": "無効化",
|
||||
"Invalid verification code": "認証コードが無効です",
|
||||
"New backup codes have been generated": "新しいバックアップコードが生成されました",
|
||||
"Failed to regenerate backup codes": "バックアップコードの再生成に失敗しました",
|
||||
"About backup codes": "バックアップコードについて",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "認証アプリにアクセスできない場合、バックアップコードでアカウントにアクセスできます。各コードは1回のみ使用可能です",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "新しいバックアップコードはいつでも再生成できます。既存のコードはすべて無効になります",
|
||||
"Confirm password": "パスワードを確認",
|
||||
"Generate new backup codes": "新しいバックアップコードを生成",
|
||||
"Save your new backup codes": "新しいバックアップコードを保存",
|
||||
"Save your new backup codes": "新しいバックアップコードを保存してください",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "これらのコードを安全な場所に保存してください。古いバックアップコードは無効になりました",
|
||||
"Your new backup codes": "新しいバックアップコード",
|
||||
"I've saved my backup codes": "バックアップコードを保存しました",
|
||||
"Failed to setup MFA": "MFAの設定に失敗しました",
|
||||
"Setup & Verify": "設定と確認",
|
||||
"Failed to setup MFA": "MFA の設定に失敗しました",
|
||||
"Setup & Verify": "設定して認証",
|
||||
"Add to authenticator": "認証アプリに追加",
|
||||
"1. Scan this QR code with your authenticator app": "1. このQRコードを認証アプリでスキャンしてください",
|
||||
"1. Scan this QR code with your authenticator app": "1. 認証アプリでこの QR コードをスキャンしてください",
|
||||
"Can't scan the code?": "コードをスキャンできませんか?",
|
||||
"Enter this code manually in your authenticator app:": "このコードを認証アプリに手動で入力してください:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. 認証アプリからの6桁のコードを入力してください",
|
||||
"Verify and enable": "確認と有効化",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. 認証アプリに表示された 6 桁のコードを入力してください",
|
||||
"Verify and enable": "認証して有効化",
|
||||
"Failed to generate QR code. Please try again.": "QRコードの生成に失敗しました。もう一度お試しください",
|
||||
"Backup": "バックアップ",
|
||||
"Save codes": "コードを保存",
|
||||
"Save your backup codes": "バックアップコードを保存",
|
||||
"Save your backup codes": "バックアップコードを保存してください",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "認証アプリにアクセスできない場合、これらのコードでアカウントにアクセスできます。各コードは1回のみ使用可能です",
|
||||
"Print": "印刷",
|
||||
"Two-factor authentication has been set up. Please log in again.": "二要素認証を設定しました。再度ログインしてください",
|
||||
@@ -469,71 +536,71 @@
|
||||
"Cancel and logout": "キャンセルしてログアウト",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "このワークスペースでは二要素認証が必要です。続行するには設定してください",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "認証アプリからの確認コードでアカウントのセキュリティが強化されます",
|
||||
"Password is required": "パスワードが必要です",
|
||||
"Password must be at least 8 characters": "パスワードは8文字以上必要です",
|
||||
"Please enter a 6-digit code": "6桁のコードを入力してください",
|
||||
"Code must be exactly 6 digits": "コードは6桁で入力してください",
|
||||
"Enter the 6-digit code found in your authenticator app": "認証アプリに表示された6桁のコードを入力してください",
|
||||
"Password is required": "パスワードは必須です",
|
||||
"Password must be at least 8 characters": "パスワードは 8 文字以上である必要があります",
|
||||
"Please enter a 6-digit code": "6 桁のコードを入力してください",
|
||||
"Code must be exactly 6 digits": "コードはちょうど 6 桁である必要があります",
|
||||
"Enter the 6-digit code found in your authenticator app": "認証アプリに表示された 6 桁のコードを入力してください",
|
||||
"Need help authenticating?": "認証に関するヘルプが必要ですか?",
|
||||
"MFA QR Code": "MFA QRコード",
|
||||
"MFA QR Code": "MFA QR コード",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "アカウントを作成しました。二要素認証を設定するためにログインしてください",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "パスワードをリセットしました。新しいパスワードでログインして二要素認証を完了してください",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "パスワードをリセットしました。新しいパスワードでログインして二要素認証を設定してください",
|
||||
"Password reset was successful. Please log in with your new password.": "パスワードをリセットしました。新しいパスワードでログインしてください",
|
||||
"Two-factor authentication": "二要素認証",
|
||||
"Use authenticator app instead": "代わりに認証アプリを使用",
|
||||
"Verify backup code": "バックアップコードを確認",
|
||||
"Verify backup code": "バックアップコードを認証",
|
||||
"Use backup code": "バックアップコードを使用",
|
||||
"Enter one of your backup codes": "バックアップコードのいずれかを入力してください",
|
||||
"Enter one of your backup codes": "バックアップコードのいずれか 1 つを入力してください",
|
||||
"Backup code": "バックアップコード",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "バックアップコードを入力してください。各コードは1回のみ使用可能です",
|
||||
"Verify": "確認",
|
||||
"Trash": "ごみ箱",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "ごみ箱内のページは30日後に完全に削除されます",
|
||||
"Deleted": "削除",
|
||||
"No pages in trash": "ごみ箱にページがありません",
|
||||
"Verify": "認証",
|
||||
"Trash": "ゴミ箱",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "{count, plural, other {ゴミ箱内のページは#日後に完全に削除されます。}}",
|
||||
"Deleted": "削除済み",
|
||||
"No pages in trash": "ゴミ箱にページはありません",
|
||||
"Permanently delete page?": "ページを完全に削除しますか?",
|
||||
"Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "「{{title}}」を完全に削除しますか?この操作は取り消せません",
|
||||
"Restore '{{title}}' and its sub-pages?": "「{{title}}」とそのサブページを復元しますか?",
|
||||
"Move to trash": "ごみ箱に移動",
|
||||
"Move to trash": "ゴミ箱に移動",
|
||||
"Move this page to trash?": "このページをごみ箱に移動しますか?",
|
||||
"Restore page": "ページを復元",
|
||||
"Page moved to trash": "ページをごみ箱に移動しました",
|
||||
"Page restored successfully": "ページを復元しました",
|
||||
"Page moved to trash": "ページをゴミ箱に移動しました",
|
||||
"Page restored successfully": "ページが正常に復元されました",
|
||||
"Deleted by": "削除者",
|
||||
"Deleted at": "削除日時",
|
||||
"Preview": "プレビュー",
|
||||
"Subpages": "サブページ",
|
||||
"Failed to load subpages": "サブページの読み込みに失敗しました",
|
||||
"No subpages": "サブページがありません",
|
||||
"No subpages": "サブページはありません",
|
||||
"Subpages (Child pages)": "サブページ(子ページ)",
|
||||
"List all subpages of the current page": "現在のページのすべてのサブページをリスト",
|
||||
"List all subpages of the current page": "現在のページのすべてのサブページを一覧表示",
|
||||
"Attachments": "添付ファイル",
|
||||
"All spaces": "すべてのスペース",
|
||||
"Unknown": "不明",
|
||||
"Find a space": "スペースを探す",
|
||||
"Search in all your spaces": "あなたのすべてのスペースで検索",
|
||||
"Search in all your spaces": "すべてのスペースを検索",
|
||||
"Type": "タイプ",
|
||||
"Enterprise": "エンタープライズ",
|
||||
"Download attachment": "添付ファイルをダウンロード",
|
||||
"Allowed email domains": "許可されたメールドメイン",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "これらのドメインのメールアドレスを持つユーザーのみSSO経由で登録できます",
|
||||
"Enter valid domain names separated by comma or space": "コンマまたはスペースで区切って有効なドメイン名を入力してください",
|
||||
"Enforce two-factor authentication": "二要素認証を強制する",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "これらのドメインのメールアドレスを持つユーザーのみが SSO 経由でサインアップできます。",
|
||||
"Enter valid domain names separated by comma or space": "有効なドメイン名をカンマまたはスペース区切りで入力してください",
|
||||
"Enforce two-factor authentication": "二要素認証を必須化",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "有効にすると、すべてのメンバーが二要素認証を設定しないとワークスペースにアクセスできなくなります",
|
||||
"Toggle MFA enforcement": "MFAの強制を切り替える",
|
||||
"Toggle MFA enforcement": "MFA 必須化を切り替え",
|
||||
"Display name": "表示名",
|
||||
"Allow signup": "登録を許可する",
|
||||
"Allow signup": "サインアップを許可",
|
||||
"Enabled": "有効",
|
||||
"Advanced Settings": "詳細設定",
|
||||
"Enable TLS/SSL": "TLS/SSLを有効にする",
|
||||
"Use secure connection to LDAP server": "LDAPサーバーへの安全な接続を使用する",
|
||||
"Enable TLS/SSL": "TLS/SSL を有効化",
|
||||
"Use secure connection to LDAP server": "LDAP サーバーへの安全な接続を使用",
|
||||
"Group sync": "グループ同期",
|
||||
"No SSO providers found.": "SSOプロバイダーが見つかりませんでした。",
|
||||
"Delete SSO provider": "SSOプロバイダーを削除する",
|
||||
"Delete SSO provider": "SSO プロバイダーを削除",
|
||||
"Are you sure you want to delete this SSO provider?": "このSSOプロバイダーを削除してもよろしいですか?",
|
||||
"Action": "アクション",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}}の構成",
|
||||
"Action": "操作",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} の設定",
|
||||
"Icon": "アイコン",
|
||||
"Upload image": "画像をアップロード",
|
||||
"Remove image": "画像を削除",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "この操作は取り消せません。このAPIキーを使用しているアプリケーションは動作しなくなります",
|
||||
"Update API key": "APIキーを更新",
|
||||
"Manage API keys for all users in the workspace": "ワークスペース内のすべてのユーザーのAPIキーを管理",
|
||||
"Restrict API key creation to admins": "APIキーの作成を管理者のみに制限する",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "新しいAPIキーを作成できるのは管理者とオーナーのみです。既存のメンバーキーは引き続き有効です。",
|
||||
"Toggle restrict API keys to admins": "APIキーの作成制限(管理者のみ)を切り替える",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "ワークスペース管理者によってAPIキーの作成が管理者のみに制限されています。",
|
||||
"AI settings": "AI設定",
|
||||
"AI search": "AI検索",
|
||||
"AI Answer": "AI回答",
|
||||
"Ask AI": "AIに質問する",
|
||||
"AI is thinking...": "AIが考え中...",
|
||||
"Thinking": "考えています",
|
||||
"Ask a question...": "質問を入力...",
|
||||
"AI-powered search (Ask AI)": "AIによる検索(AIに質問)",
|
||||
"AI Answers": "AI回答",
|
||||
"AI-powered search (AI Answers)": "AI搭載検索 (AI回答)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI検索はベクター埋め込みを使用してワークスペース全体の意味検索を実現します",
|
||||
"Toggle AI search": "AI検索を切り替え",
|
||||
"Generative AI (Ask AI)": "生成AI (Ask AI)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "エディターでAIを活用したコンテンツ生成を有効にします。ユーザーがテキストの生成、改善、翻訳、および変換を行うことができます。",
|
||||
"Toggle generative AI": "生成AIを切り替える",
|
||||
"Upgrade your plan": "プランをアップグレードする",
|
||||
"Available with a paid license": "有料ライセンスで利用可能",
|
||||
"Upgrade your license tier.": "ライセンスタイアをアップグレードしてください。",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI は Docmost のエンタープライズ版でのみ利用可能です。sales@docmost.com までお問い合わせください。",
|
||||
"AI & MCP": "AI と MCP",
|
||||
"AI": "AI",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "モデルコンテキストプロトコル(MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "MCP サーバーを有効にして、AI アシスタントやツールがワークスペースのコンテンツとやり取りできるようにします。",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP は Docmost のエンタープライズ版でのみ利用可能です。sales@docmost.com までお問い合わせください。",
|
||||
"MCP Server URL": "MCP サーバーの URL",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "認証には API キーを使用してください。API キーはアカウント設定で管理できます。",
|
||||
"Supported tools": "サポートされているツール",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "このワークスペースでは MCP が有効になっています。AI アシスタントを接続するには API キーを使用してください。",
|
||||
"MCP server URL:": "MCP サーバーの URL:",
|
||||
"Learn more": "詳細を見る",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "ワークスペース内のすべてのユーザーのAPIキーを管理します。利用方法の詳細は<anchor>APIドキュメント</anchor>をご覧ください。",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "利用方法の詳細は<anchor>APIドキュメント</anchor>をご覧ください。",
|
||||
"View the <anchor>MCP documentation</anchor>.": "<anchor>MCPドキュメント</anchor>をご覧ください。",
|
||||
"Sources": "ソース",
|
||||
"Ask AI not available for attachments": "添付ファイルにはAI質問は利用できません",
|
||||
"AI Answers not available for attachments": "添付ファイルにはAI回答を利用できません",
|
||||
"No answer available": "回答がありません",
|
||||
"Background color": "背景色",
|
||||
"Highlight color": "ハイライト色",
|
||||
"Remove color": "色を削除"
|
||||
"Remove color": "色を削除",
|
||||
"Notifications": "通知",
|
||||
"No notifications": "通知なし",
|
||||
"No unread notifications": "未読の通知はありません",
|
||||
"All notifications": "すべての通知",
|
||||
"Unread only": "未読のみ",
|
||||
"Mark all as read": "すべてを既読にする",
|
||||
"Mark as read": "既読にする",
|
||||
"More options": "その他のオプション",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold>さんがコメントであなたに言及しました",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold>さんがページにコメントしました",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> さんがコメントを解決しました",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> さんがページであなたにメンションしました",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> さんがあなたにページの編集権限を付与しました",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> さんがあなたにページの閲覧権限を付与しました",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> さんがページを更新しました",
|
||||
"Watch page": "ページをウォッチ",
|
||||
"Stop watching": "ウォッチを解除",
|
||||
"Watch space": "スペースをウォッチ",
|
||||
"Stop watching space": "スペースのウォッチを解除",
|
||||
"Email notifications": "メール通知",
|
||||
"Page updates": "ページの更新",
|
||||
"Get notified when pages you watch are updated.": "ウォッチしているページが更新されたときに通知を受け取ります。",
|
||||
"Page mentions": "ページでの言及",
|
||||
"Get notified when someone mentions you on a page.": "誰かがページであなたに言及したとき通知を受け取ります。",
|
||||
"Comment mentions": "コメントでの言及",
|
||||
"Get notified when someone mentions you in a comment.": "誰かがコメントであなたに言及したとき通知を受け取ります。",
|
||||
"New comments": "新しいコメント",
|
||||
"Get notified about new comments on threads you participate in.": "参加しているスレッドに新しいコメントがあると通知されます。",
|
||||
"Resolved comments": "解決済みコメント",
|
||||
"Get notified when your comment is resolved.": "あなたのコメントが解決されたとき通知を受け取ります。",
|
||||
"You are now watching this page": "このページをウォッチしています",
|
||||
"You are no longer watching this page": "このページのウォッチを解除しました",
|
||||
"You are now watching this space": "このスペースのウォッチを開始しました",
|
||||
"You are no longer watching this space": "このスペースのウォッチを解除しました",
|
||||
"Direct": "直接",
|
||||
"Updates": "アップデート",
|
||||
"Today": "今日",
|
||||
"Yesterday": "昨日",
|
||||
"This week": "今週",
|
||||
"Older": "以前のもの",
|
||||
"Restricted page": "アクセス制限されたページ",
|
||||
"Restricted pages cannot be shared publicly.": "アクセス制限されたページは公開共有できません。",
|
||||
"Restricted by parent": "親ページによって制限されています",
|
||||
"Restricted": "制限あり",
|
||||
"Open": "制限なし",
|
||||
"Inherits restrictions from ancestor page": "上位ページから制限を継承しています",
|
||||
"Only people listed below can access this page": "以下に記載されている人のみがこのページにアクセスできます",
|
||||
"Everyone in this space can access": "このスペース内の全員がアクセスできます",
|
||||
"No additional restrictions on this page": "このページに追加の制限はありません",
|
||||
"Only specific people can access": "特定の人のみがアクセスできます",
|
||||
"Use only inherited restrictions": "継承された制限のみを適用する",
|
||||
"Add restrictions on top of inherited": "継承された制限に追加の制限を加える",
|
||||
"Inherited restriction": "継承された制限",
|
||||
"Access limited by": "アクセス制限元",
|
||||
"Restrict access to control who can view and edit this page": "このページを誰が表示・編集できるかを制御するためにアクセスを制限します",
|
||||
"Add additional restrictions specific to this page": "このページ固有の追加制限を設定する",
|
||||
"Access": "アクセス",
|
||||
"People with access": "アクセスできる人",
|
||||
"Remove all": "すべてを削除",
|
||||
"Remove access": "アクセス権を削除",
|
||||
"Remove all access": "すべてのアクセス権を削除",
|
||||
"Are you sure you want to remove this member's access to the page?": "このメンバーのページへのアクセス権を削除してもよろしいですか?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "すべての特定のアクセスを削除してもよろしいですか?これによりページはスペース内の全員に公開されます。",
|
||||
"Trash retention": "ゴミ箱の保持期間",
|
||||
"Pages in trash will be permanently deleted after this period.": "この期間を過ぎるとゴミ箱内のページは完全に削除されます。",
|
||||
"Trash retention updated": "ゴミ箱保持期間が更新されました",
|
||||
"Failed to update trash retention": "ゴミ箱保持期間の更新に失敗しました",
|
||||
"Removed page restriction": "ページの制限を解除しました",
|
||||
"Added page permission": "ページの権限を追加しました",
|
||||
"Removed page permission": "ページの権限を削除しました",
|
||||
"day": "日",
|
||||
"days": "日",
|
||||
"week": "週",
|
||||
"weeks": "週",
|
||||
"month": "か月",
|
||||
"months": "か月",
|
||||
"year": "年",
|
||||
"years": "年",
|
||||
"Period": "期間",
|
||||
"Fixed date": "指定日",
|
||||
"Indefinitely": "無期限",
|
||||
"Days": "日",
|
||||
"Weeks": "週",
|
||||
"Months": "か月",
|
||||
"Years": "年",
|
||||
"Pick a date": "日付を選択",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "この単位の最大値は{{max}}{{unit}}です",
|
||||
"Never expires. Verifiers can re-verify at any time.": "有効期限はありません。検証者はいつでも再検証できます。",
|
||||
"Verified": "検証済み",
|
||||
"Review needed": "確認が必要",
|
||||
"Verification expired": "検証期限切れ",
|
||||
"Draft": "下書き",
|
||||
"In Approval": "承認中",
|
||||
"In approval": "承認中",
|
||||
"Approved": "承認済み",
|
||||
"Obsolete": "廃止",
|
||||
"Expiring": "期限間近",
|
||||
"Set up verification": "検証を設定",
|
||||
"Verify page": "ページを検証",
|
||||
"Page verification": "ページ検証",
|
||||
"Add verification": "検証を追加",
|
||||
"Edit verification": "検証を編集",
|
||||
"Search by title": "タイトルで検索",
|
||||
"Choose how this page should stay accurate.": "このページの正確性をどのように維持するか選択してください。",
|
||||
"Recurring verification": "定期検証",
|
||||
"Verifiers re-confirm this page on a schedule.": "検証者がこのページを定期的に再確認します。",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "スケジュールに従って再検証(例:30日ごと)",
|
||||
"Page stays editable at all times": "ページは常に編集可能です",
|
||||
"Best for runbooks, FAQs, living documentation": "運用手順書、FAQ、継続的に更新されるドキュメントに最適",
|
||||
"Approval workflow": "承認ワークフロー",
|
||||
"Formal document lifecycle with named approvers.": "指定された承認者による正式な文書ライフサイクルです。",
|
||||
"Draft → In approval → Approved → Obsolete": "下書き → 承認中 → 承認済み → 廃止",
|
||||
"Locked once approved, with full history": "承認後はロックされ、完全な履歴が残ります",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "ISO 9001、ISO 13485、FDA向けに設計",
|
||||
"Best for SOPs and controlled documents": "SOPや管理文書に最適",
|
||||
"Back": "戻る",
|
||||
"Quality management": "品質管理",
|
||||
"Recurring": "定期",
|
||||
"Pages move through draft, approval, and approved stages.": "ページは下書き、承認中、承認済みの各段階を進みます。",
|
||||
"Verifiers": "検証者",
|
||||
"Add verifier": "検証者を追加",
|
||||
"I've reviewed this page for accuracy": "このページの正確性を確認しました",
|
||||
"Set up": "設定",
|
||||
"Remove verification": "検証を削除",
|
||||
"Are you sure you want to remove verification from this page?": "このページから検証を削除してもよろしいですか?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "割り当てられた検証者はこのページを定期的に再検証する必要があります。",
|
||||
"Last verified by {{name}} {{time}} (expired)": "最終検証者:{{name}} {{time}}(期限切れ)",
|
||||
"The fixed expiration date has passed.": "指定された有効期限を過ぎています。",
|
||||
"Verified by {{name}} {{time}}": "{{name}}が{{time}}に検証",
|
||||
"Expires {{date}}": "有効期限:{{date}}",
|
||||
"Expired {{date}}": "{{date}}に期限切れ",
|
||||
"Mark as obsolete": "廃止としてマーク",
|
||||
"Mark obsolete": "廃止にする",
|
||||
"Returned by {{name}} {{time}}": "{{name}}が{{time}}に差し戻し",
|
||||
"No approval has been requested yet.": "まだ承認は依頼されていません。",
|
||||
"Submitted by {{name}} {{time}}": "{{name}}が{{time}}に提出",
|
||||
"Someone": "誰か",
|
||||
"Approved by {{name}} {{time}}": "{{name}}が{{time}}に承認",
|
||||
"This document has been marked as obsolete.": "この文書は廃止としてマークされています。",
|
||||
"Rejection comment": "差し戻しコメント",
|
||||
"Reason for returning this document...": "この文書を差し戻す理由...",
|
||||
"Confirm rejection": "差し戻しを確定",
|
||||
"Submit for approval": "承認を申請",
|
||||
"Reject": "差し戻す",
|
||||
"Approve": "承認",
|
||||
"Re-submit for approval": "再度承認を申請",
|
||||
"Verified until": "検証有効期限",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "検証済みページ",
|
||||
"Search pages...": "ページを検索...",
|
||||
"Filter by space": "スペースで絞り込み",
|
||||
"Filter by type": "タイプで絞り込み",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold>がページを検証しました",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold>があなたの承認のためにページを提出しました",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold>がページを修正のため差し戻しました",
|
||||
"Page verification expires soon": "ページ検証の期限が間もなく切れます",
|
||||
"Page verification has expired": "ページ検証の期限が切れています",
|
||||
"Verifying your email": "メールアドレスを確認しています",
|
||||
"Please wait...": "お待ちください…",
|
||||
"Verification failed. The link may have expired.": "認証に失敗しました。リンクの有効期限が切れている可能性があります。",
|
||||
"Check your email": "メールを確認してください",
|
||||
"We sent a verification link to {{email}}.": "確認用リンクを{{email}}に送信しました。",
|
||||
"We sent a verification link to your email.": "確認用リンクをあなたのメールアドレスに送信しました。",
|
||||
"Click the link to verify your email and access your workspace.": "リンクをクリックしてメールを認証し、ワークスペースにアクセスしてください。",
|
||||
"Resend verification email": "確認メールを再送信",
|
||||
"Verification email sent. Please check your inbox.": "確認メールを送信しました。受信箱をご確認ください。",
|
||||
"Failed to resend verification email. Please try again.": "確認メールの再送信に失敗しました。もう一度お試しください。",
|
||||
"We've sent you an email with your associated workspaces.": "紐づいているワークスペース情報をメールでお送りしました。",
|
||||
"Load more": "もっと読み込む",
|
||||
"Log out of all devices": "すべてのデバイスからログアウト",
|
||||
"Log out of all sessions except this device": "このデバイス以外のすべてのセッションからログアウト",
|
||||
"This Device": "このデバイス",
|
||||
"Unknown device": "不明なデバイス",
|
||||
"No active sessions": "アクティブなセッションはありません",
|
||||
"Session revoked": "セッションを無効化しました",
|
||||
"All other sessions revoked": "他のすべてのセッションを無効化しました",
|
||||
"Last used": "最終使用",
|
||||
"Created": "作成日",
|
||||
"Rename": "名前を変更",
|
||||
"Publish": "公開",
|
||||
"Security": "セキュリティ",
|
||||
"Enforce SSO": "SSO を必須化",
|
||||
"Once enforced, members will not be able to login with email and password.": "必須化すると、メンバーはメールアドレスとパスワードでログインできなくなります。",
|
||||
"AI-generated content may not be accurate.": "AI が生成したコンテンツは正確でない場合があります。",
|
||||
"AI Chat": "AI チャット",
|
||||
"Analyze for insights": "分析してインサイトを得る",
|
||||
"Ask anything...": "何でも聞いてください...",
|
||||
"Chat history": "チャット履歴",
|
||||
"Chat name": "チャット名",
|
||||
"Close": "閉じる",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "チャットの読み込みに失敗しました。エラーが発生しました。",
|
||||
"Failed to render this message.": "このメッセージの表示に失敗しました。",
|
||||
"How can I help you today?": "本日はどのようにお手伝いできますか?",
|
||||
"New chat": "新しいチャット",
|
||||
"No chat history": "チャット履歴はありません",
|
||||
"No chats found": "チャットが見つかりません",
|
||||
"No conversations yet": "会話はまだありません",
|
||||
"Open full page": "全ページで開く",
|
||||
"Previous 7 days": "過去 7 日間",
|
||||
"Previous 30 days": "過去 30 日間",
|
||||
"Search chats...": "チャットを検索...",
|
||||
"Start a new chat to see it here.": "ここに表示するには新しいチャットを開始してください。",
|
||||
"Summarize this page": "このページを要約",
|
||||
"Toggle AI Chat": "AI チャットを切り替え",
|
||||
"Translate this page": "このページを翻訳",
|
||||
"Try a different search term.": "別の検索語を試してください。",
|
||||
"Try again": "再試行",
|
||||
"Untitled chat": "無題のチャット",
|
||||
"What can I help you with?": "何をお手伝いしましょうか?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "사용자 추가",
|
||||
"Add to groups": "팀에 추가",
|
||||
"Add space members": "Space에 사용자 추가",
|
||||
"Add to favorites": "즐겨찾기에 추가",
|
||||
"Admin": "관리자",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "이 팀을 삭제하시겠습니까? 해당 팀에 속한 사용자들은 이 팀이 가진 모든 권한을 잃게 됩니다.",
|
||||
"Are you sure you want to delete this page?": "이 페이지를 삭제하시겠습니까?",
|
||||
@@ -47,12 +48,12 @@
|
||||
"e.g ACME": "예: ACME",
|
||||
"e.g ACME Inc": "예: ACME Inc",
|
||||
"e.g Developers": "예: 개발자",
|
||||
"e.g Group for developers": "예: 개발자를 위한 팀",
|
||||
"e.g Group for developers": "예: 개발자를 위한 그룹",
|
||||
"e.g product": "예: 제품",
|
||||
"e.g Product Team": "예: 제품 팀",
|
||||
"e.g Sales": "예: 영업",
|
||||
"e.g Space for product team": "예: 제품 팀을 위한 Space",
|
||||
"e.g Space for sales team to collaborate": "예: 영업 팀의 Space",
|
||||
"e.g Space for product team": "예: 제품 팀용 스페이스",
|
||||
"e.g Space for sales team to collaborate": "예: 영업 팀이 협업하는 스페이스",
|
||||
"Edit": "편집",
|
||||
"Read": "읽기",
|
||||
"Edit group": "팀 편집",
|
||||
@@ -61,7 +62,7 @@
|
||||
"Enter valid email addresses separated by comma or space max_50": "유효한 이메일 주소를 쉼표나 공백으로 구분하여 입력하세요 [최대: 50]",
|
||||
"enter valid emails addresses": "유효한 이메일 주소를 입력하세요",
|
||||
"Enter your current password": "기존 비밀번호를 입력하세요",
|
||||
"enter your full name": "전체 이름을 입력하세요",
|
||||
"enter your full name": "성명을 입력하세요",
|
||||
"Enter your new password": "새 비밀번호를 입력하세요",
|
||||
"Enter your new preferred email": "새로운 이메일을 입력하세요",
|
||||
"Enter your password": "비밀번호를 입력하세요",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "페이지 가져오기 실패",
|
||||
"Failed to load page. An error occurred.": "페이지 불러오기 실패. 오류가 발생했습니다.",
|
||||
"Failed to update data": "데이터 갱신 실패",
|
||||
"Favorite spaces": "즐겨찾는 스페이스",
|
||||
"Favorite spaces appear here": "즐겨찾는 스페이스가 여기에 표시됩니다",
|
||||
"Favorites": "즐겨찾기",
|
||||
"Full access": "전체 권한",
|
||||
"Full page width": "전체 페이지 너비",
|
||||
"Full width": "전체 너비",
|
||||
@@ -87,11 +91,12 @@
|
||||
"Import pages": "페이지 가져오기",
|
||||
"Import pages & space settings": "페이지 및 Space 설정 가져오기",
|
||||
"Importing pages": "페이지 가져오는 중",
|
||||
"invalid invitation link": "유효하지 않은 초대 링크",
|
||||
"invalid invitation link": "유효하지 않은 초대 링크입니다",
|
||||
"Invitation signup": "초대 가입",
|
||||
"Invite by email": "이메일로 초대",
|
||||
"Invite members": "사용자 초대",
|
||||
"Invite new members": "새 사용자 초대",
|
||||
"Invite People": "사용자 초대",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "초대를 아직 수락하지 않은 초대된 사용자가 여기에 표시됩니다.",
|
||||
"Invited members will be granted access to spaces the groups can access": "초대된 사용자는 팀이 접근할 수 있는 Space에 대한 접근 권한을 받게 됩니다",
|
||||
"Join the workspace": "Workspace 참여",
|
||||
@@ -113,9 +118,10 @@
|
||||
"New email": "새 이메일",
|
||||
"New page": "새 페이지",
|
||||
"New password": "새 비밀번호",
|
||||
"No group found": "팀을 찾을 수 없음",
|
||||
"No group found": "그룹을 찾을 수 없습니다",
|
||||
"No page history saved yet.": "아직 저장된 페이지 기록이 없습니다.",
|
||||
"No pages yet": "아직 페이지가 없습니다",
|
||||
"No shared pages": "공유된 페이지가 없습니다.",
|
||||
"No results found...": "결과를 찾을 수 없습니다...",
|
||||
"No user found": "사용자를 찾을 수 없음",
|
||||
"Overview": "개요",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "페이지",
|
||||
"Password": "비밀번호",
|
||||
"Password changed successfully": "비밀번호 변경 완료",
|
||||
"People": "사용자",
|
||||
"Pending": "대기 중",
|
||||
"Please confirm your action": "작업을 확인해 주세요",
|
||||
"Preferences": "설정",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "프로필",
|
||||
"Recently updated": "최근 업데이트",
|
||||
"Remove": "제거",
|
||||
"Remove from favorites": "즐겨찾기에서 제거",
|
||||
"Remove group member": "팀에서 사용자 제거",
|
||||
"Remove space member": "Space에서 사용자 제거",
|
||||
"Restore": "복원",
|
||||
@@ -149,41 +157,42 @@
|
||||
"Search...": "검색...",
|
||||
"Select language": "언어 선택",
|
||||
"Select role": "역할 선택",
|
||||
"Select role to assign to all invited members": "초대된 모든 사용자에게 할당할 역할 선택",
|
||||
"Select theme": "배경 선택",
|
||||
"Select role to assign to all invited members": "초대된 모든 멤버에게 할당할 역할을 선택하세요",
|
||||
"Select theme": "테마 선택",
|
||||
"Send invitation": "초대 보내기",
|
||||
"Invitation sent": "초대 발송 완료",
|
||||
"Invitation sent": "초대를 보냈습니다",
|
||||
"Settings": "설정",
|
||||
"Setup workspace": "Workspace 설정",
|
||||
"Setup workspace": "워크스페이스 설정",
|
||||
"Sign In": "로그인",
|
||||
"Sign Up": "회원 가입",
|
||||
"Slug": "고유 경로",
|
||||
"Space": "Space",
|
||||
"Space description": "Space 설명",
|
||||
"Space menu": "Space 메뉴",
|
||||
"Space name": "Space 이름",
|
||||
"Space settings": "Space 설정",
|
||||
"Space slug": "Space의 고유 경로",
|
||||
"Spaces": "Space",
|
||||
"Spaces you belong to": "소속된 Space",
|
||||
"No space found": "Space을 찾을 수 없음",
|
||||
"Search for spaces": "Space 검색",
|
||||
"Sign Up": "회원가입",
|
||||
"Slug": "슬러그",
|
||||
"Space": "스페이스",
|
||||
"Space description": "스페이스 설명",
|
||||
"Space menu": "스페이스 메뉴",
|
||||
"Space name": "스페이스 이름",
|
||||
"Space settings": "스페이스 설정",
|
||||
"Space slug": "스페이스 슬러그",
|
||||
"Spaces": "스페이스",
|
||||
"Spaces you belong to": "내가 속한 스페이스",
|
||||
"No space found": "스페이스를 찾을 수 없습니다",
|
||||
"Search for spaces": "스페이스 검색",
|
||||
"Start typing to search...": "검색하려면 입력을 시작하세요...",
|
||||
"Status": "상태",
|
||||
"Successfully imported": "가져오기 완료",
|
||||
"Successfully restored": "복원 완료",
|
||||
"Successfully imported": "가져오기에 성공했습니다",
|
||||
"Successfully restored": "복원에 성공했습니다",
|
||||
"System settings": "시스템 설정",
|
||||
"Theme": "배경",
|
||||
"Templates": "템플릿",
|
||||
"Theme": "테마",
|
||||
"To change your email, you have to enter your password and new email.": "이메일을 변경하려면 기존 비밀번호와 새 이메일을 입력해야 합니다.",
|
||||
"Toggle full page width": "전체 페이지 너비 전환",
|
||||
"Unable to import pages. Please try again.": "페이지를 가져올 수 없습니다. 다시 시도해주세요.",
|
||||
"untitled": "제목 없음",
|
||||
"Untitled": "제목 없음",
|
||||
"Updated successfully": "업데이트 완료",
|
||||
"Updated successfully": "성공적으로 업데이트되었습니다",
|
||||
"User": "사용자",
|
||||
"Workspace": "Workspace",
|
||||
"Workspace Name": "Workspce 이름",
|
||||
"Workspace settings": "Workspace 설정",
|
||||
"Workspace": "워크스페이스",
|
||||
"Workspace Name": "워크스페이스 이름",
|
||||
"Workspace settings": "워크스페이스 설정",
|
||||
"You can change your password here.": "여기서 비밀번호를 변경할 수 있습니다.",
|
||||
"Your Email": "이메일",
|
||||
"Your import is complete.": "가져오기가 완료되었습니다.",
|
||||
@@ -193,10 +202,10 @@
|
||||
"Your password must be a minimum of 8 characters.": "비밀번호는 최소 8자 이상이어야 합니다.",
|
||||
"Sidebar toggle": "사이드바 전환",
|
||||
"Comments": "댓글",
|
||||
"404 page not found": "404 페이지를 찾을 수 없음",
|
||||
"404 page not found": "404 페이지를 찾을 수 없습니다",
|
||||
"Sorry, we can't find the page you are looking for.": "죄송합니다. 페이지를 찾을 수 없습니다.",
|
||||
"Take me back to homepage": "홈페이지로 돌아가기",
|
||||
"Forgot password": "비밀번호 찾기",
|
||||
"Forgot password": "비밀번호를 잊으셨나요",
|
||||
"Forgot your password?": "비밀번호를 잊으셨나요?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "비밀번호 재설정 링크가 이메일로 전송되었습니다. 받은 편지함을 확인해주세요.",
|
||||
"Send reset link": "재설정 링크 보내기",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "답글...",
|
||||
"Error loading comments.": "댓글 불러오기 오류.",
|
||||
"No comments yet.": "아직 댓글이 없습니다.",
|
||||
"No open comments.": "열린 댓글이 없습니다.",
|
||||
"No resolved comments.": "해결된 댓글이 없습니다.",
|
||||
"Add a comment...": "댓글 추가...",
|
||||
"Edit comment": "댓글 수정",
|
||||
"Delete comment": "댓글 삭제",
|
||||
"Are you sure you want to delete this comment?": "이 댓글을 삭제하시겠습니까?",
|
||||
"Delete chat": "채팅 삭제",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "'{{title}}'을(를) 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
|
||||
"Comment created successfully": "댓글 생성 완료",
|
||||
"Error creating comment": "댓글 생성 오류",
|
||||
"Comment updated successfully": "댓글 업데이트 완료",
|
||||
@@ -218,17 +232,16 @@
|
||||
"Failed to delete comment": "댓글 삭제 실패",
|
||||
"Comment resolved successfully": "댓글 처리 완료",
|
||||
"Comment re-opened successfully": "댓글이 성공적으로 다시 열렸습니다",
|
||||
"Comment unresolved successfully": "댓글 미해결로 변경 완료",
|
||||
"Comment unresolved successfully": "댓글 해결이 성공적으로 취소되었습니다",
|
||||
"Failed to resolve comment": "댓글 처리 실패",
|
||||
"Resolve comment": "댓글 해결하기",
|
||||
"Unresolve comment": "댓글 미해결로 변경하기",
|
||||
"Resolve Comment Thread": "댓글 스레드 해결하기",
|
||||
"Unresolve Comment Thread": "댓글 스레드 미해결로 변경하기",
|
||||
"Resolve comment": "댓글 해결",
|
||||
"Unresolve comment": "댓글 해결 취소",
|
||||
"Resolve Comment Thread": "댓글 스레드 해결",
|
||||
"Unresolve Comment Thread": "댓글 스레드 해결 취소",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "이 댓글 스레드를 해결하시겠습니까? 완료로 표시됩니다.",
|
||||
"Are you sure you want to unresolve this comment thread?": "이 댓글 스레드를 미해결로 변경하시겠습니까?",
|
||||
"Resolved": "해결됨",
|
||||
"No active comments.": "활성 댓글이 없습니다.",
|
||||
"No resolved comments.": "해결된 댓글이 없습니다.",
|
||||
"Revoke invitation": "초대 취소",
|
||||
"Revoke": "취소",
|
||||
"Don't": "하지 않음",
|
||||
@@ -237,9 +250,9 @@
|
||||
"Anyone with this link can join this workspace.": "이 링크를 가진 모든 사용자가 이 Workspace에 참여할 수 있습니다.",
|
||||
"Invite link": "초대 링크",
|
||||
"Copy": "복사",
|
||||
"Copy to space": "공간에 복사하기",
|
||||
"Copy to space": "스페이스로 복사",
|
||||
"Copied": "복사됨",
|
||||
"Duplicate": "중복",
|
||||
"Duplicate": "복제",
|
||||
"Select a user": "사용자 선택",
|
||||
"Select a group": "팀 선택",
|
||||
"Export all pages and attachments in this space.": "이 Space의 모든 페이지와 첨부파일을 내보냅니다.",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "이 Space을 삭제하시겠습니까?",
|
||||
"Delete this space with all its pages and data.": "이 Space의 모든 페이지와 데이터를 삭제합니다.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "이 Space의 모든 페이지, 댓글, 첨부파일 및 권한이 영구적으로 삭제됩니다.",
|
||||
"Confirm space name": "Space 이름 확인",
|
||||
"Confirm space name": "스페이스 이름 확인",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "작업을 진행하려면 Space 이름 <b>{{spaceName}}</b>을 입력하세요.",
|
||||
"Format": "형식",
|
||||
"Include subpages": "하위 페이지 포함",
|
||||
@@ -263,7 +276,7 @@
|
||||
"Align left": "왼쪽 정렬",
|
||||
"Align right": "오른쪽 정렬",
|
||||
"Align center": "가운데 정렬",
|
||||
"Justify": "정렬",
|
||||
"Justify": "양쪽 정렬",
|
||||
"Merge cells": "셀 병합",
|
||||
"Split cell": "셀 분할",
|
||||
"Delete column": "열 삭제",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "아래에 행 추가",
|
||||
"Delete table": "테이블 삭제",
|
||||
"Info": "정보",
|
||||
"Note": "참고",
|
||||
"Success": "완료",
|
||||
"Warning": "주의",
|
||||
"Danger": "위험",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "저장 후 나가기",
|
||||
"Double-click to edit Excalidraw diagram": "Excalidraw diagram을 편집하려면 더블 클릭하세요",
|
||||
"Paste link": "링크 붙여넣기",
|
||||
"Paste link or search pages": "링크를 붙여넣거나 페이지를 검색",
|
||||
"Link to web page": "웹페이지에 링크하기",
|
||||
"Recents": "최근 항목",
|
||||
"Page or URL": "페이지 또는 URL",
|
||||
"Link title": "링크 제목",
|
||||
"Edit link": "링크 수정",
|
||||
"Remove link": "링크 제거",
|
||||
"Add link": "링크 추가",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "분홍색",
|
||||
"Gray": "회색",
|
||||
"Embed link": "임베드 링크",
|
||||
"Invalid {{provider}} embed link": "잘못된 {{provider}} 임베드 링크",
|
||||
"Invalid {{provider}} embed link": "유효하지 않은 {{provider}} 임베드 링크입니다",
|
||||
"Embed {{provider}}": "{{provider}} 임베드",
|
||||
"Enter {{provider}} link to embed": "임베드를 할 {{provider}} 링크 입력",
|
||||
"Bold": "굵게",
|
||||
@@ -331,55 +350,83 @@
|
||||
"Insert horizontal rule divider": "가로 구분선 삽입",
|
||||
"Upload any image from your device.": "기기에서 이미지를 업로드하세요.",
|
||||
"Upload any video from your device.": "기기에서 비디오를 업로드하세요.",
|
||||
"Upload any audio from your device.": "기기에서 오디오를 업로드하세요.",
|
||||
"Upload any file from your device.": "기기에서 파일을 업로드하세요.",
|
||||
"Uploading {{name}}": "{{name}} 업로드 중",
|
||||
"Uploading file": "파일 업로드 중",
|
||||
"Table": "테이블",
|
||||
"Table": "표",
|
||||
"Insert a table.": "테이블 삽입.",
|
||||
"Insert collapsible block.": "접을 수 있는 블록 삽입.",
|
||||
"Video": "비디오",
|
||||
"Video": "동영상",
|
||||
"Divider": "구분선",
|
||||
"Quote": "인용",
|
||||
"Quote": "인용문",
|
||||
"Image": "이미지",
|
||||
"Audio": "오디오",
|
||||
"Embed PDF": "PDF 임베드",
|
||||
"Upload and embed a PDF file.": "PDF 파일을 업로드하고 임베드하세요.",
|
||||
"Embed as PDF": "PDF로 임베드",
|
||||
"Failed to load PDF": "PDF 로드 실패",
|
||||
"Convert to attachment": "첨부 파일로 변환",
|
||||
"File attachment": "파일 첨부",
|
||||
"Toggle block": "블록 토글",
|
||||
"Callout": "경고 상자",
|
||||
"Toggle block": "토글 블록",
|
||||
"Callout": "콜아웃",
|
||||
"Insert callout notice.": "돋보이는 글을 작성하기.",
|
||||
"Math inline": "수식",
|
||||
"Math inline": "인라인 수식",
|
||||
"Insert inline math equation.": "수식 삽입.",
|
||||
"Math block": "수식 블록",
|
||||
"Insert math equation": "수식 삽입",
|
||||
"Mermaid diagram": "Mermaid diagram",
|
||||
"Insert mermaid diagram": "Mermaid diagram 삽입",
|
||||
"Insert and design Drawio diagrams": "Drawio diagram 삽입 및 디자인",
|
||||
"Mermaid diagram": "Mermaid 다이어그램",
|
||||
"Insert mermaid diagram": "Mermaid 다이어그램 삽입",
|
||||
"Insert and design Drawio diagrams": "Drawio 다이어그램 삽입 및 편집",
|
||||
"Insert current date": "현재 날짜 삽입",
|
||||
"Draw and sketch excalidraw diagrams": "Excalidraw diagram 그리기 및 스케치",
|
||||
"Multiple": "복제",
|
||||
"Draw and sketch excalidraw diagrams": "Excalidraw 다이어그램 그리기 및 스케치",
|
||||
"Multiple": "다중",
|
||||
"Turn into": "변경하기",
|
||||
"Text align": "텍스트 정렬",
|
||||
"This page may have been deleted, moved, or you may not have access.": "이 페이지는 삭제되었거나 이동되었거나 접근 권한이 없을 수 있습니다.",
|
||||
"Go to homepage": "홈으로 이동",
|
||||
"Pages you create will show up here.": "여기에 생성한 페이지가 표시됩니다.",
|
||||
"Heading {{level}}": "제목 {{level}}",
|
||||
"Toggle title": "제목 토글",
|
||||
"Write anything. Enter \"/\" for commands": "아무거나 입력하세요. 명령어를 사용하려면 \"/\"를 입력하세요",
|
||||
"Toggle title": "토글 제목",
|
||||
"Write anything. Enter \"/\" for commands": "무엇이든 입력하세요. 명령어를 사용하려면 \"/\"를 입력하세요",
|
||||
"Write...": "작성...",
|
||||
"Column count": "열 개수",
|
||||
"{{count}} Columns": "{{count}}열",
|
||||
"Equal columns": "열 너비 균등",
|
||||
"Left sidebar": "왼쪽 사이드바",
|
||||
"Right sidebar": "오른쪽 사이드바",
|
||||
"Wide center": "가운데 넓게",
|
||||
"Left wide": "왼쪽 넓게",
|
||||
"Right wide": "오른쪽 넓게",
|
||||
"Names do not match": "이름이 일치하지 않습니다",
|
||||
"Today, {{time}}": "오늘, {{time}}",
|
||||
"Yesterday, {{time}}": "어제, {{time}}",
|
||||
"Space created successfully": "공간 생성 완료",
|
||||
"Space updated successfully": "공간이 성공적으로 업데이트되었습니다",
|
||||
"Space deleted successfully": "스페이스 삭제 완료",
|
||||
"Members added successfully": "회원 추가 완료",
|
||||
"Space created successfully": "스페이스가 성공적으로 생성되었습니다",
|
||||
"Space updated successfully": "스페이스가 성공적으로 업데이트되었습니다",
|
||||
"Space deleted successfully": "스페이스가 성공적으로 삭제되었습니다",
|
||||
"Members added successfully": "멤버가 성공적으로 추가되었습니다",
|
||||
"Member removed successfully": "멤버가 성공적으로 제거되었습니다",
|
||||
"Member role updated successfully": "회원 역할이 성공적으로 업데이트되었습니다",
|
||||
"Member role updated successfully": "멤버 역할이 성공적으로 업데이트되었습니다",
|
||||
"Created by: <b>{{creatorName}}</b>": "작성자: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "생성 날짜: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "{{name}}님이 편집함 {{time}}",
|
||||
"Created at: {{time}}": "작성 시간: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "{{name}}님이 {{time}}에 편집함",
|
||||
"Word count: {{wordCount}}": "단어 수: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "문자 수: {{characterCount}}",
|
||||
"New update": "새로운 업데이트",
|
||||
"{{latestVersion}} is available": "{{latestVersion}}이 사용 가능합니다",
|
||||
"New update": "새 업데이트",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} 버전을 사용할 수 있습니다",
|
||||
"Default page edit mode": "기본 페이지 편집 모드",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "선호하는 페이지 편집 모드를 선택하세요. 실수로 인한 편집을 방지하세요.",
|
||||
"Reading": "읽기",
|
||||
"Delete member": "회원 삭제",
|
||||
"Member deleted successfully": "멤버가 성공적으로 제거되었습니다",
|
||||
"Delete member": "멤버 삭제",
|
||||
"Member deleted successfully": "멤버가 성공적으로 삭제되었습니다",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "이 워크스페이스 멤버를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
|
||||
"Deactivate member": "멤버 비활성화",
|
||||
"Activate member": "멤버 활성화",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "이 워크스페이스 멤버를 비활성화하시겠습니까? 해당 사용자는 더 이상 이 워크스페이스에 접근할 수 없습니다.",
|
||||
"Are you sure you want to activate this workspace member?": "이 워크스페이스 멤버를 활성화하시겠습니까?",
|
||||
"Deactivate": "비활성화",
|
||||
"Activate": "활성화",
|
||||
"Deactivated": "비활성화됨",
|
||||
"Move": "이동",
|
||||
"Move page": "페이지 이동",
|
||||
"Move page to a different space.": "페이지를 다른 공간으로 이동합니다.",
|
||||
@@ -388,26 +435,45 @@
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "목차를 생성하려면 제목 (H1, H2, H3)을 추가하세요.",
|
||||
"Share": "공유",
|
||||
"Public sharing": "공개 공유",
|
||||
"Shared by": "공유자",
|
||||
"Shared by": "공유한 사람",
|
||||
"Shared at": "공유 시간",
|
||||
"Inherits public sharing from": "로부터 공개 공유를 상속함",
|
||||
"Inherits public sharing from": "다음으로부터 공개 공유를 상속받음",
|
||||
"Share to web": "웹에 공유",
|
||||
"Shared to web": "웹에 공유됨",
|
||||
"Anyone with the link can view this page": "링크가 있는 사람은 이 페이지를 볼 수 있습니다",
|
||||
"Make this page publicly accessible": "이 페이지를 공개적으로 접근 가능하게 만들기",
|
||||
"Anyone with the link can view this page": "링크가 있는 누구나 이 페이지를 볼 수 있습니다",
|
||||
"Make this page publicly accessible": "이 페이지를 공개적으로 접근 가능하게 설정",
|
||||
"Include sub-pages": "하위 페이지 포함",
|
||||
"Make sub-pages public too": "하위 페이지도 공개로 설정",
|
||||
"Allow search engines to index page": "검색 엔진이 페이지를 색인할 수 있도록 허용",
|
||||
"Allow search engines to index page": "검색 엔진이 페이지를 색인하도록 허용",
|
||||
"Open page": "페이지 열기",
|
||||
"Page": "페이지",
|
||||
"Delete public share link": "공유 링크 삭제",
|
||||
"Delete public share link": "공개 공유 링크 삭제",
|
||||
"Delete share": "공유 삭제",
|
||||
"Are you sure you want to delete this shared link?": "이 공유 링크를 삭제하시겠습니까?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "회원인 공간의 공개 공유된 페이지가 여기에 표시됩니다",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "회원으로 속한 스페이스의 공개 공유 페이지가 여기에 표시됩니다",
|
||||
"Share deleted successfully": "공유가 성공적으로 삭제되었습니다",
|
||||
"Share not found": "공유를 찾을 수 없습니다",
|
||||
"Failed to share page": "페이지 공유에 실패했습니다",
|
||||
"Copy page": "페이지 복사하기",
|
||||
"Disable public sharing": "공유 비활성화",
|
||||
"Prevent members from sharing pages publicly.": "멤버들이 페이지를 공개적으로 공유하지 못하도록 방지하십시오.",
|
||||
"Toggle public sharing": "공유 전환",
|
||||
"Toggle space public sharing": "공간 공유 전환",
|
||||
"Allow viewers to comment": "뷰어가 댓글을 달 수 있도록 허용",
|
||||
"Allow viewers to add comments on pages in this space.": "이 공간 내 페이지에 뷰어가 댓글을 추가할 수 있도록 허용합니다.",
|
||||
"Toggle viewer comments": "뷰어 댓글 전환",
|
||||
"Public sharing is disabled at the workspace level": "워크스페이스 수준에서 공유가 비활성화되었습니다.",
|
||||
"Prevent pages in this space from being shared publicly.": "이 공간의 페이지가 공개적으로 공유되지 않도록 방지하십시오.",
|
||||
"Page permissions": "페이지 권한},{",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "개별 페이지의 조회 및 편집 권한을 제어합니다. 엔터프라이즈 라이선스에서 이용 가능합니다.",
|
||||
"Enable public sharing": "공유 활성화",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "공유를 활성화하시겠습니까? 멤버들이 페이지를 공개적으로 공유할 수 있게 됩니다.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "정말로 공유를 비활성화하시겠습니까? 이 워크스페이스의 모든 기존 공유 링크가 삭제됩니다.",
|
||||
"Are you sure you want to enable public sharing for this space?": "이 공간의 공유를 활성화하시겠습니까?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "정말로 공유를 비활성화하시겠습니까? 이 공간의 모든 기존 공유 링크가 삭제됩니다.",
|
||||
"Public sharing is disabled": "공유가 비활성화되었습니다.",
|
||||
"Public sharing has been disabled at the workspace level.": "워크스페이스 수준에서 공유가 비활성화되었습니다.",
|
||||
"Public sharing has been disabled for this space.": "이 공간의 공유가 비활성화되었습니다.",
|
||||
"Copy page": "페이지 복사",
|
||||
"Copy page to a different space.": "다른 공간으로 페이지 복사하기.",
|
||||
"Page copied successfully": "페이지가 성공적으로 복사되었습니다",
|
||||
"Page duplicated successfully": "페이지가 성공적으로 복제되었습니다",
|
||||
@@ -416,71 +482,72 @@
|
||||
"Previous Match (Shift+Enter)": "이전 일치 항목 (Shift+Enter)",
|
||||
"Next match (Enter)": "다음 일치 항목 (Enter)",
|
||||
"Match case (Alt+C)": "대소문자 구분 (Alt+C)",
|
||||
"Replace": "교체",
|
||||
"Replace": "바꾸기",
|
||||
"Close (Escape)": "닫기 (Escape)",
|
||||
"Replace (Enter)": "교체 (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "모두 교체하기 (Ctrl+Alt+Enter)",
|
||||
"Replace all": "모두 교체하기",
|
||||
"View all spaces": "모든 공간 보기",
|
||||
"Replace (Enter)": "바꾸기 (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "모두 바꾸기 (Ctrl+Alt+Enter)",
|
||||
"Replace all": "모두 바꾸기",
|
||||
"View all": "모두 보기",
|
||||
"View all spaces": "모든 스페이스 보기",
|
||||
"Error": "오류",
|
||||
"Failed to disable MFA": "MFA 비활성화 실패",
|
||||
"Disable two-factor authentication": "이중 인증 비활성화",
|
||||
"Failed to disable MFA": "MFA 비활성화에 실패했습니다",
|
||||
"Disable two-factor authentication": "2단계 인증 비활성화",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "이중 인증을 비활성화하면 계정의 보안이 낮아집니다. 로그인 시 비밀번호만 필요하게 됩니다.",
|
||||
"Please enter your password to disable two-factor authentication:": "이중 인증 비활성화를 위해 비밀번호를 입력하세요:",
|
||||
"Two-factor authentication has been enabled": "이중 인증이 활성화되었습니다",
|
||||
"Two-factor authentication has been disabled": "이중 인증이 비활성화되었습니다",
|
||||
"Two-factor authentication has been enabled": "2단계 인증이 활성화되었습니다",
|
||||
"Two-factor authentication has been disabled": "2단계 인증이 비활성화되었습니다",
|
||||
"2-step verification": "2단계 인증",
|
||||
"Protect your account with an additional verification layer when signing in.": "로그인 시 추가 인증 단계를 통해 계정을 보호하세요.",
|
||||
"Two-factor authentication is active on your account.": "이중 인증이 계정에 활성화되어 있습니다.",
|
||||
"Add 2FA method": "2FA 방법 추가",
|
||||
"Backup codes": "백업 코드",
|
||||
"Disable": "비활성화",
|
||||
"Invalid verification code": "유효하지 않은 인증 코드",
|
||||
"Invalid verification code": "유효하지 않은 인증 코드입니다",
|
||||
"New backup codes have been generated": "새 백업 코드가 생성되었습니다",
|
||||
"Failed to regenerate backup codes": "백업 코드 재생성 실패",
|
||||
"About backup codes": "백업 코드에 대하여",
|
||||
"Failed to regenerate backup codes": "백업 코드 재생성에 실패했습니다",
|
||||
"About backup codes": "백업 코드 정보",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "인증 앱에 접근할 수 없게 된 경우, 백업 코드를 사용하여 계정에 접근할 수 있습니다. 각 코드는 한 번만 사용할 수 있습니다.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "언제든지 새 백업 코드를 재생성할 수 있습니다. 이 작업은 기존 모든 코드를 무효화합니다.",
|
||||
"Confirm password": "비밀번호 확인",
|
||||
"Generate new backup codes": "새 백업 코드 생성하기",
|
||||
"Save your new backup codes": "새 백업 코드 저장하기",
|
||||
"Generate new backup codes": "새 백업 코드 생성",
|
||||
"Save your new backup codes": "새 백업 코드를 저장하세요",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "이 코드를 안전한 장소에 저장하세요. 이전 백업 코드는 더 이상 유효하지 않습니다.",
|
||||
"Your new backup codes": "새 백업 코드",
|
||||
"I've saved my backup codes": "백업 코드를 저장했습니다",
|
||||
"Failed to setup MFA": "MFA 설정 실패",
|
||||
"Failed to setup MFA": "MFA 설정에 실패했습니다",
|
||||
"Setup & Verify": "설정 및 확인",
|
||||
"Add to authenticator": "인증앱에 추가",
|
||||
"1. Scan this QR code with your authenticator app": "1. 인증앱으로 이 QR 코드를 스캔하십시오.",
|
||||
"Add to authenticator": "인증 앱에 추가",
|
||||
"1. Scan this QR code with your authenticator app": "1. 인증 앱으로 이 QR 코드를 스캔하세요",
|
||||
"Can't scan the code?": "코드를 스캔할 수 없습니까?",
|
||||
"Enter this code manually in your authenticator app:": "이 코드를 인증앱에 수동으로 입력해 주세요:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. 인증앱에서 6자리 코드를 입력하십시오",
|
||||
"Verify and enable": "확인 및 활성화",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. 인증 앱에 표시된 6자리 코드를 입력하세요",
|
||||
"Verify and enable": "확인 후 활성화",
|
||||
"Failed to generate QR code. Please try again.": "QR 코드 생성 실패. 다시 시도해 주세요.",
|
||||
"Backup": "백업",
|
||||
"Save codes": "코드 저장",
|
||||
"Save your backup codes": "백업 코드 저장하기",
|
||||
"Save your backup codes": "백업 코드를 저장하세요",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "인증 앱에 대한 접근 권한을 잃은 경우, 이 코드를 사용하여 귀하의 계정에 접근할 수 있습니다. 각 코드는 한 번만 사용할 수 있습니다.",
|
||||
"Print": "인쇄",
|
||||
"Two-factor authentication has been set up. Please log in again.": "이중 인증이 설정되었습니다. 다시 로그인해 주세요.",
|
||||
"Two-Factor authentication required": "이중 인증 필요",
|
||||
"Your workspace requires two-factor authentication for all users": "워크스페이스에서는 모든 사용자에게 이중 인증이 필요합니다.",
|
||||
"Two-Factor authentication required": "2단계 인증이 필요합니다",
|
||||
"Your workspace requires two-factor authentication for all users": "이 워크스페이스는 모든 사용자에게 2단계 인증을 요구합니다",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "워크스페이스 접근을 계속하려면 이중 인증을 설정해야 합니다. 이는 계정에 추가 보안 계층을 추가합니다.",
|
||||
"Set up two-factor authentication": "이중 인증 설정하기",
|
||||
"Cancel and logout": "취소 및 로그아웃",
|
||||
"Set up two-factor authentication": "2단계 인증 설정",
|
||||
"Cancel and logout": "취소하고 로그아웃",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "워크스페이스에서는 이중 인증이 필요합니다. 계속하려면 설정해 주세요.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "인증앱에서 얻은 인증 코드를 요구하여 계정의 보안에 추가적인 계층을 추가합니다.",
|
||||
"Password is required": "비밀번호가 필요합니다",
|
||||
"Password must be at least 8 characters": "비밀번호는 최소 8자 이상이어야 합니다",
|
||||
"Please enter a 6-digit code": "6자리 코드를 입력해 주세요",
|
||||
"Password is required": "비밀번호는 필수입니다",
|
||||
"Password must be at least 8 characters": "비밀번호는 8자 이상이어야 합니다",
|
||||
"Please enter a 6-digit code": "6자리 코드를 입력하세요",
|
||||
"Code must be exactly 6 digits": "코드는 정확히 6자리여야 합니다",
|
||||
"Enter the 6-digit code found in your authenticator app": "인증앱에서 찾은 6자리 코드를 입력하십시오",
|
||||
"Enter the 6-digit code found in your authenticator app": "인증 앱에 표시된 6자리 코드를 입력하세요",
|
||||
"Need help authenticating?": "인증에 도움이 필요하십니까?",
|
||||
"MFA QR Code": "MFA QR 코드",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "계정이 성공적으로 생성되었습니다. 이중 인증을 설정하려면 로그인해 주세요.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "비밀번호 재설정 성공. 새 비밀번호로 로그인하여 이중 인증을 완료하세요.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "비밀번호 재설정 성공. 새 비밀번호로 로그인하여 이중 인증을 설정하세요.",
|
||||
"Password reset was successful. Please log in with your new password.": "비밀번호 재설정이 성공적으로 완료되었습니다. 새 비밀번호로 로그인하세요.",
|
||||
"Two-factor authentication": "이중 인증",
|
||||
"Two-factor authentication": "2단계 인증",
|
||||
"Use authenticator app instead": "대신 인증 앱 사용",
|
||||
"Verify backup code": "백업 코드 확인",
|
||||
"Use backup code": "백업 코드 사용",
|
||||
@@ -489,7 +556,7 @@
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "백업 코드 중 하나를 입력하세요. 각 백업 코드는 한 번만 사용할 수 있습니다.",
|
||||
"Verify": "확인",
|
||||
"Trash": "휴지통",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "휴지통의 페이지는 30일 후에 영구적으로 삭제됩니다.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "휴지통의 페이지는 {{count}}일 후 영구적으로 삭제됩니다.",
|
||||
"Deleted": "삭제됨",
|
||||
"No pages in trash": "휴지통에 페이지가 없습니다",
|
||||
"Permanently delete page?": "페이지를 영구적으로 삭제하시겠습니까?",
|
||||
@@ -497,37 +564,37 @@
|
||||
"Restore '{{title}}' and its sub-pages?": "'{{title}}' 및 하위 페이지를 복구하시겠습니까?",
|
||||
"Move to trash": "휴지통으로 이동",
|
||||
"Move this page to trash?": "이 페이지를 휴지통으로 이동하시겠습니까?",
|
||||
"Restore page": "페이지 복구",
|
||||
"Restore page": "페이지 복원",
|
||||
"Page moved to trash": "페이지가 휴지통으로 이동되었습니다",
|
||||
"Page restored successfully": "페이지가 성공적으로 복구되었습니다",
|
||||
"Deleted by": "삭제자",
|
||||
"Page restored successfully": "페이지가 성공적으로 복원되었습니다",
|
||||
"Deleted by": "삭제한 사람",
|
||||
"Deleted at": "삭제 시간",
|
||||
"Preview": "미리보기",
|
||||
"Subpages": "하위 페이지",
|
||||
"Failed to load subpages": "하위 페이지 로드 실패",
|
||||
"No subpages": "하위 페이지 없음",
|
||||
"Failed to load subpages": "하위 페이지를 불러오지 못했습니다",
|
||||
"No subpages": "하위 페이지가 없습니다",
|
||||
"Subpages (Child pages)": "하위 페이지 (자식 페이지)",
|
||||
"List all subpages of the current page": "현재 페이지의 모든 하위 페이지 목록",
|
||||
"Attachments": "첨부 파일",
|
||||
"All spaces": "전체 공간",
|
||||
"List all subpages of the current page": "현재 페이지의 모든 하위 페이지 나열",
|
||||
"Attachments": "첨부파일",
|
||||
"All spaces": "모든 스페이스",
|
||||
"Unknown": "알 수 없음",
|
||||
"Find a space": "공간 찾기",
|
||||
"Search in all your spaces": "모든 공간에서 검색",
|
||||
"Find a space": "스페이스 찾기",
|
||||
"Search in all your spaces": "모든 스페이스에서 검색",
|
||||
"Type": "유형",
|
||||
"Enterprise": "기업",
|
||||
"Download attachment": "첨부 파일 다운로드",
|
||||
"Enterprise": "엔터프라이즈",
|
||||
"Download attachment": "첨부파일 다운로드",
|
||||
"Allowed email domains": "허용된 이메일 도메인",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "이 도메인의 이메일 주소를 가진 사용자만 SSO를 통해 가입할 수 있습니다.",
|
||||
"Enter valid domain names separated by comma or space": "콤마 또는 공백으로 구분하여 유효한 도메인 이름 입력",
|
||||
"Enforce two-factor authentication": "이중 인증 시행",
|
||||
"Enter valid domain names separated by comma or space": "쉼표 또는 공백으로 구분된 유효한 도메인 이름을 입력하세요",
|
||||
"Enforce two-factor authentication": "2단계 인증 강제",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "시행되면 모든 멤버가 작업 공간에 액세스하기 위해 이중 인증을 활성화해야 합니다.",
|
||||
"Toggle MFA enforcement": "MFA 시행 전환",
|
||||
"Toggle MFA enforcement": "MFA 강제 설정 전환",
|
||||
"Display name": "표시 이름",
|
||||
"Allow signup": "가입 허용",
|
||||
"Enabled": "활성화됨",
|
||||
"Advanced Settings": "고급 설정",
|
||||
"Enable TLS/SSL": "TLS\\/SSL 활성화",
|
||||
"Use secure connection to LDAP server": "LDAP 서버에 안전한 연결 사용",
|
||||
"Enable TLS/SSL": "TLS/SSL 활성화",
|
||||
"Use secure connection to LDAP server": "LDAP 서버에 보안 연결 사용",
|
||||
"Group sync": "그룹 동기화",
|
||||
"No SSO providers found.": "SSO 제공자를 찾을 수 없습니다.",
|
||||
"Delete SSO provider": "SSO 제공자 삭제",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "이 작업은 되돌릴 수 없습니다. 이 API 키를 사용하는 모든 응용 프로그램이 작동을 멈출 것입니다.",
|
||||
"Update API key": "API 키 갱신",
|
||||
"Manage API keys for all users in the workspace": "워크스페이스 내 모든 사용자의 API 키 관리",
|
||||
"Restrict API key creation to admins": "API 키 생성 권한을 관리자에게만 제한합니다",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "새로운 API 키는 관리자와 소유자만 생성할 수 있습니다. 기존 멤버 키는 계속 사용할 수 있습니다.",
|
||||
"Toggle restrict API keys to admins": "API 키 생성 제한(관리자 전용) 설정 전환",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "API 키 생성이 워크스페이스 관리자로 인해 관리자에게만 제한되었습니다.",
|
||||
"AI settings": "AI 설정",
|
||||
"AI search": "AI 검색",
|
||||
"AI Answer": "AI 답변",
|
||||
"Ask AI": "AI에게 묻기",
|
||||
"AI is thinking...": "AI가 생각 중입니다...",
|
||||
"Thinking": "생각 중",
|
||||
"Ask a question...": "질문하세요...",
|
||||
"AI-powered search (Ask AI)": "AI 지원 검색 (AI에게 묻기)",
|
||||
"AI Answers": "AI 답변",
|
||||
"AI-powered search (AI Answers)": "AI 구동 검색 (AI 답변)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI 검색은 벡터 임베딩을 사용하여 작업공간 콘텐츠에 대한 의미 검색 기능을 제공합니다.",
|
||||
"Toggle AI search": "AI 검색 전환",
|
||||
"Generative AI (Ask AI)": "생성 AI (Ask AI)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "편집기에서 AI 구동 콘텐츠 생성을 활성화합니다. 사용자가 텍스트를 생성, 개선, 번역 및 변환할 수 있습니다.",
|
||||
"Toggle generative AI": "생성 AI 토글",
|
||||
"Upgrade your plan": "요금제를 업그레이드하세요",
|
||||
"Available with a paid license": "유료 라이선스에서만 사용 가능합니다",
|
||||
"Upgrade your license tier.": "라이선스 등급을 업그레이드하세요.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI는 Docmost 엔터프라이즈 에디션에서만 제공됩니다. sales@docmost.com으로 문의하세요.",
|
||||
"AI & MCP": "AI 및 MCP",
|
||||
"AI": "AI",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "모델 컨텍스트 프로토콜(MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "AI 어시스턴트와 도구가 워크스페이스 콘텐츠와 상호작용할 수 있도록 MCP 서버를 활성화하세요.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP는 Docmost 엔터프라이즈 에디션에서만 제공됩니다. sales@docmost.com으로 문의하세요.",
|
||||
"MCP Server URL": "MCP 서버 URL",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "인증을 위해 API 키를 사용하세요. API 키는 계정 설정에서 관리할 수 있습니다.",
|
||||
"Supported tools": "지원되는 도구",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "워크스페이스에 MCP가 활성화되어 있습니다. AI 어시스턴트를 연결하려면 API 키를 사용하세요.",
|
||||
"MCP server URL:": "MCP 서버 URL:",
|
||||
"Learn more": "자세히 알아보기",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "워크스페이스의 모든 사용자를 위한 API 키를 관리하세요. 사용 방법은 <anchor>API 문서</anchor>를 참고하세요.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "사용 방법은 <anchor>API 문서</anchor>를 참고하세요.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "<anchor>MCP 문서</anchor>를 확인하세요.",
|
||||
"Sources": "출처",
|
||||
"Ask AI not available for attachments": "AI에게 묻기 기능은 첨부 파일에 대해 사용할 수 없습니다",
|
||||
"AI Answers not available for attachments": "첨부 파일에 대해 AI 답변을 사용할 수 없습니다",
|
||||
"No answer available": "답변을 제공할 수 없습니다",
|
||||
"Background color": "배경 색",
|
||||
"Highlight color": "강조 색",
|
||||
"Remove color": "색 제거"
|
||||
"Remove color": "색 제거",
|
||||
"Notifications": "알림",
|
||||
"No notifications": "알림 없음",
|
||||
"No unread notifications": "읽지 않은 알림 없음",
|
||||
"All notifications": "모든 알림",
|
||||
"Unread only": "읽지 않음만",
|
||||
"Mark all as read": "모두 읽음으로 표시",
|
||||
"Mark as read": "읽음으로 표시",
|
||||
"More options": "추가 옵션",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold>님이 댓글에서 당신을 언급했습니다",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold>님이 페이지에 댓글을 남겼습니다",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold>님이 댓글을 해결했습니다",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold>님이 페이지에서 나를 멘션했습니다",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold>님이 페이지 편집 권한을 부여했습니다",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold>님이 페이지 보기 권한을 부여했습니다",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold>님이 페이지를 업데이트했습니다",
|
||||
"Watch page": "페이지 구독",
|
||||
"Stop watching": "구독 중지",
|
||||
"Watch space": "스페이스 구독",
|
||||
"Stop watching space": "스페이스 구독 중지",
|
||||
"Email notifications": "이메일 알림",
|
||||
"Page updates": "페이지 업데이트",
|
||||
"Get notified when pages you watch are updated.": "구독한 페이지가 업데이트될 때 알림을 받으세요.",
|
||||
"Page mentions": "페이지 언급",
|
||||
"Get notified when someone mentions you on a page.": "누군가가 페이지에서 당신을 언급하면 알림을 받으세요.",
|
||||
"Comment mentions": "댓글 언급",
|
||||
"Get notified when someone mentions you in a comment.": "누군가가 댓글에서 당신을 언급하면 알림을 받으세요.",
|
||||
"New comments": "새 댓글",
|
||||
"Get notified about new comments on threads you participate in.": "참여 중인 스레드에 새 댓글이 달리면 알림을 받으세요.",
|
||||
"Resolved comments": "해결된 댓글",
|
||||
"Get notified when your comment is resolved.": "내 댓글이 해결되었을 때 알림을 받으세요.",
|
||||
"You are now watching this page": "이제 이 페이지를 주시합니다.",
|
||||
"You are no longer watching this page": "더 이상 이 페이지를 주시하지 않습니다.",
|
||||
"You are now watching this space": "이제 이 스페이스를 구독합니다",
|
||||
"You are no longer watching this space": "더 이상 이 스페이스를 구독하지 않습니다",
|
||||
"Direct": "직접",
|
||||
"Updates": "업데이트",
|
||||
"Today": "오늘",
|
||||
"Yesterday": "어제",
|
||||
"This week": "이번 주",
|
||||
"Older": "이전",
|
||||
"Restricted page": "제한된 페이지",
|
||||
"Restricted pages cannot be shared publicly.": "제한된 페이지는 공개적으로 공유할 수 없습니다.",
|
||||
"Restricted by parent": "상위 페이지에 의해 제한됨",
|
||||
"Restricted": "제한됨",
|
||||
"Open": "공개",
|
||||
"Inherits restrictions from ancestor page": "상위 페이지로부터 제한을 상속함",
|
||||
"Only people listed below can access this page": "아래에 나열된 사용자만 이 페이지에 접근할 수 있습니다.",
|
||||
"Everyone in this space can access": "이 공간의 모든 사용자가 접근할 수 있습니다.",
|
||||
"No additional restrictions on this page": "이 페이지에는 추가 제한이 없습니다.",
|
||||
"Only specific people can access": "특정 사용자만 접근할 수 있습니다.",
|
||||
"Use only inherited restrictions": "상속된 제한만 사용",
|
||||
"Add restrictions on top of inherited": "상속된 제한 위에 추가 제한 적용",
|
||||
"Inherited restriction": "상속된 제한",
|
||||
"Access limited by": "접근 제한:",
|
||||
"Restrict access to control who can view and edit this page": "이 페이지를 누가 조회하고 편집할 수 있는지 제어하려면 접근을 제한하세요.",
|
||||
"Add additional restrictions specific to this page": "이 페이지에 대한 추가 제한을 적용하세요.",
|
||||
"Access": "접근",
|
||||
"People with access": "접근 권한이 있는 사용자",
|
||||
"Remove all": "모두 제거",
|
||||
"Remove access": "접근 권한 제거",
|
||||
"Remove all access": "모든 접근 권한 제거",
|
||||
"Are you sure you want to remove this member's access to the page?": "이 멤버의 페이지 접근 권한을 제거하시겠습니까?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "모든 특정 접근 권한을 제거하시겠습니까? 이렇게 하면 페이지가 공간의 모든 사용자에게 공개됩니다.",
|
||||
"Trash retention": "휴지통 보관 기간",
|
||||
"Pages in trash will be permanently deleted after this period.": "이 기간이 지나면 휴지통의 페이지는 영구적으로 삭제됩니다.",
|
||||
"Trash retention updated": "휴지통 보관 기간이 업데이트되었습니다.",
|
||||
"Failed to update trash retention": "휴지통 보관 기간 업데이트에 실패했습니다.",
|
||||
"Removed page restriction": "페이지 제한이 제거됨",
|
||||
"Added page permission": "페이지 권한이 추가됨",
|
||||
"Removed page permission": "페이지 권한이 제거됨",
|
||||
"day": "일",
|
||||
"days": "일",
|
||||
"week": "주",
|
||||
"weeks": "주",
|
||||
"month": "개월",
|
||||
"months": "개월",
|
||||
"year": "년",
|
||||
"years": "년",
|
||||
"Period": "기간",
|
||||
"Fixed date": "고정 날짜",
|
||||
"Indefinitely": "무기한",
|
||||
"Days": "일",
|
||||
"Weeks": "주",
|
||||
"Months": "개월",
|
||||
"Years": "년",
|
||||
"Pick a date": "날짜 선택",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "이 단위의 최대값은 {{max}} {{unit}}입니다",
|
||||
"Never expires. Verifiers can re-verify at any time.": "만료되지 않습니다. 검증자는 언제든지 다시 검증할 수 있습니다.",
|
||||
"Verified": "검증됨",
|
||||
"Review needed": "검토 필요",
|
||||
"Verification expired": "검증 만료됨",
|
||||
"Draft": "초안",
|
||||
"In Approval": "승인 진행 중",
|
||||
"In approval": "승인 진행 중",
|
||||
"Approved": "승인됨",
|
||||
"Obsolete": "폐기됨",
|
||||
"Expiring": "만료 예정",
|
||||
"Set up verification": "검증 설정",
|
||||
"Verify page": "페이지 검증",
|
||||
"Page verification": "페이지 검증",
|
||||
"Add verification": "검증 추가",
|
||||
"Edit verification": "검증 편집",
|
||||
"Search by title": "제목으로 검색",
|
||||
"Choose how this page should stay accurate.": "이 페이지의 정확성을 유지할 방법을 선택하세요.",
|
||||
"Recurring verification": "반복 검증",
|
||||
"Verifiers re-confirm this page on a schedule.": "검증자가 일정에 따라 이 페이지를 다시 확인합니다.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "일정에 따라 다시 검증(예: 30일마다)",
|
||||
"Page stays editable at all times": "페이지는 항상 편집 가능합니다",
|
||||
"Best for runbooks, FAQs, living documentation": "런북, FAQ, 살아 있는 문서에 적합",
|
||||
"Approval workflow": "승인 워크플로",
|
||||
"Formal document lifecycle with named approvers.": "지정된 승인자가 있는 공식 문서 수명 주기입니다.",
|
||||
"Draft → In approval → Approved → Obsolete": "초안 → 승인 진행 중 → 승인됨 → 폐기됨",
|
||||
"Locked once approved, with full history": "승인되면 잠기며 전체 이력이 유지됩니다",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "ISO 9001, ISO 13485 및 FDA용으로 설계됨",
|
||||
"Best for SOPs and controlled documents": "SOP 및 관리 문서에 적합",
|
||||
"Back": "뒤로",
|
||||
"Quality management": "품질 관리",
|
||||
"Recurring": "반복",
|
||||
"Pages move through draft, approval, and approved stages.": "페이지는 초안, 승인, 승인됨 단계를 거칩니다.",
|
||||
"Verifiers": "검증자",
|
||||
"Add verifier": "검증자 추가",
|
||||
"I've reviewed this page for accuracy": "이 페이지의 정확성을 검토했습니다",
|
||||
"Set up": "설정",
|
||||
"Remove verification": "검증 제거",
|
||||
"Are you sure you want to remove verification from this page?": "이 페이지에서 검증을 제거하시겠습니까?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "지정된 검증자는 이 페이지를 주기적으로 다시 검증해야 합니다.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "마지막 검증자: {{name}} {{time}} (만료됨)",
|
||||
"The fixed expiration date has passed.": "고정된 만료일이 지났습니다.",
|
||||
"Verified by {{name}} {{time}}": "검증자: {{name}} {{time}}",
|
||||
"Expires {{date}}": "{{date}}에 만료",
|
||||
"Expired {{date}}": "{{date}}에 만료됨",
|
||||
"Mark as obsolete": "폐기로 표시",
|
||||
"Mark obsolete": "폐기 표시",
|
||||
"Returned by {{name}} {{time}}": "반려자: {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "아직 승인이 요청되지 않았습니다.",
|
||||
"Submitted by {{name}} {{time}}": "제출자: {{name}} {{time}}",
|
||||
"Someone": "누군가",
|
||||
"Approved by {{name}} {{time}}": "승인자: {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "이 문서는 폐기로 표시되었습니다.",
|
||||
"Rejection comment": "반려 사유",
|
||||
"Reason for returning this document...": "이 문서를 반려하는 이유...",
|
||||
"Confirm rejection": "반려 확인",
|
||||
"Submit for approval": "승인 요청",
|
||||
"Reject": "반려",
|
||||
"Approve": "승인",
|
||||
"Re-submit for approval": "승인 재요청",
|
||||
"Verified until": "다음까지 검증됨",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "검증된 페이지",
|
||||
"Search pages...": "페이지 검색...",
|
||||
"Filter by space": "스페이스별 필터",
|
||||
"Filter by type": "유형별 필터",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold>님이 페이지를 검증했습니다",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold>님이 승인을 위해 페이지를 제출했습니다",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold>님이 수정을 위해 페이지를 반려했습니다",
|
||||
"Page verification expires soon": "페이지 검토가 곧 만료됩니다",
|
||||
"Page verification has expired": "페이지 검토가 만료되었습니다",
|
||||
"Verifying your email": "이메일 확인 중",
|
||||
"Please wait...": "잠시만 기다려 주세요...",
|
||||
"Verification failed. The link may have expired.": "인증에 실패했습니다. 링크가 만료되었을 수 있습니다.",
|
||||
"Check your email": "이메일을 확인하세요",
|
||||
"We sent a verification link to {{email}}.": "{{email}} 주소로 인증 링크를 보냈습니다.",
|
||||
"We sent a verification link to your email.": "이메일로 인증 링크를 보냈습니다.",
|
||||
"Click the link to verify your email and access your workspace.": "이메일의 링크를 클릭하여 인증하고 워크스페이스에 접속하세요.",
|
||||
"Resend verification email": "인증 이메일 다시 보내기",
|
||||
"Verification email sent. Please check your inbox.": "인증 이메일이 전송되었습니다. 받은 편지함을 확인하세요.",
|
||||
"Failed to resend verification email. Please try again.": "인증 이메일 재전송에 실패했습니다. 다시 시도해 주세요.",
|
||||
"We've sent you an email with your associated workspaces.": "연결된 워크스페이스 목록이 포함된 이메일을 보내드렸습니다.",
|
||||
"Load more": "더 보기",
|
||||
"Log out of all devices": "모든 기기에서 로그아웃",
|
||||
"Log out of all sessions except this device": "이 기기를 제외한 모든 세션에서 로그아웃",
|
||||
"This Device": "이 기기",
|
||||
"Unknown device": "알 수 없는 기기",
|
||||
"No active sessions": "활성 세션이 없습니다",
|
||||
"Session revoked": "세션이 해제되었습니다",
|
||||
"All other sessions revoked": "다른 모든 세션이 해제되었습니다",
|
||||
"Last used": "마지막 사용",
|
||||
"Created": "생성됨",
|
||||
"Rename": "이름 변경",
|
||||
"Publish": "게시",
|
||||
"Security": "보안",
|
||||
"Enforce SSO": "SSO 강제",
|
||||
"Once enforced, members will not be able to login with email and password.": "강제 적용되면 멤버는 이메일과 비밀번호로 로그인할 수 없습니다.",
|
||||
"AI-generated content may not be accurate.": "AI가 생성한 콘텐츠는 정확하지 않을 수 있습니다.",
|
||||
"AI Chat": "AI 채팅",
|
||||
"Analyze for insights": "인사이트 분석",
|
||||
"Ask anything...": "무엇이든 물어보세요...",
|
||||
"Chat history": "채팅 기록",
|
||||
"Chat name": "채팅 이름",
|
||||
"Close": "닫기",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "채팅을 불러오지 못했습니다. 오류가 발생했습니다.",
|
||||
"Failed to render this message.": "이 메시지를 렌더링하지 못했습니다.",
|
||||
"How can I help you today?": "오늘 무엇을 도와드릴까요?",
|
||||
"New chat": "새 채팅",
|
||||
"No chat history": "채팅 기록이 없습니다",
|
||||
"No chats found": "채팅을 찾을 수 없습니다",
|
||||
"No conversations yet": "아직 대화가 없습니다",
|
||||
"Open full page": "전체 페이지 열기",
|
||||
"Previous 7 days": "지난 7일",
|
||||
"Previous 30 days": "지난 30일",
|
||||
"Search chats...": "채팅 검색...",
|
||||
"Start a new chat to see it here.": "여기에 표시하려면 새 채팅을 시작하세요.",
|
||||
"Summarize this page": "이 페이지 요약",
|
||||
"Toggle AI Chat": "AI 채팅 전환",
|
||||
"Translate this page": "이 페이지 번역",
|
||||
"Try a different search term.": "다른 검색어를 사용해 보세요.",
|
||||
"Try again": "다시 시도",
|
||||
"Untitled chat": "제목 없는 채팅",
|
||||
"What can I help you with?": "무엇을 도와드릴까요?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Leden toevoegen",
|
||||
"Add to groups": "Toevoegen aan groepen",
|
||||
"Add space members": "Voeg leden toe ruimte",
|
||||
"Add to favorites": "Toevoegen aan favorieten",
|
||||
"Admin": "Beheerder",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Weet je zeker dat je deze groep wilt verwijderen? Leden verliezen toegang tot documenten waar deze groep toegang toe heeft.",
|
||||
"Are you sure you want to delete this page?": "Weet u zeker dat u deze pagina wil verwijderen?",
|
||||
@@ -49,8 +50,8 @@
|
||||
"e.g Developers": "bijv. Ontwikkelaars",
|
||||
"e.g Group for developers": "bijv. Groep voor ontwikkelaars",
|
||||
"e.g product": "bijv. product",
|
||||
"e.g Product Team": "bijv. Product Team",
|
||||
"e.g Sales": "bijv. Verkopen",
|
||||
"e.g Product Team": "bijv. Productteam",
|
||||
"e.g Sales": "bijv. Verkoop",
|
||||
"e.g Space for product team": "bijv. Ruimte voor productteam",
|
||||
"e.g Space for sales team to collaborate": "bijv. Ruimte voor verkoopteam om samen te werken",
|
||||
"Edit": "Bewerken",
|
||||
@@ -61,7 +62,7 @@
|
||||
"Enter valid email addresses separated by comma or space max_50": "Voer geldige e-mailadressen in, gescheiden door komma of spatie [max: 50]",
|
||||
"enter valid emails addresses": "voer geldige e-mailadressen in",
|
||||
"Enter your current password": "Voer uw huidige wachtwoord in",
|
||||
"enter your full name": "voer uw volledige naam in",
|
||||
"enter your full name": "voer je volledige naam in",
|
||||
"Enter your new password": "Voer uw nieuwe wachtwoord in",
|
||||
"Enter your new preferred email": "Voer uw nieuwe e-mailadres in",
|
||||
"Enter your password": "Voer uw wachtwoord in",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Pagina's importeren mislukt",
|
||||
"Failed to load page. An error occurred.": "Laden van pagina mislukt. Er is een fout opgetreden.",
|
||||
"Failed to update data": "Bijwerken van gegevens mislukt",
|
||||
"Favorite spaces": "Favoriete ruimtes",
|
||||
"Favorite spaces appear here": "Favoriete ruimtes verschijnen hier",
|
||||
"Favorites": "Favorieten",
|
||||
"Full access": "Volledig toegang",
|
||||
"Full page width": "Volledige pagina breedte",
|
||||
"Full width": "Volledige breedte",
|
||||
@@ -92,6 +96,7 @@
|
||||
"Invite by email": "Uitnodigen via e-mail",
|
||||
"Invite members": "Leden uitnodigen",
|
||||
"Invite new members": "Nieuwe leden uitnodigen",
|
||||
"Invite People": "Mensen uitnodigen",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Uitgenodigde leden die hun uitnodiging nog moeten accepteren zullen hier worden getoond.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Uitgenodigde leden wordt toegang gegeven tot ruimtes de groepen toegang toe heeft",
|
||||
"Join the workspace": "Word lid van de werkruimte",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "Geen groep gevonden",
|
||||
"No page history saved yet.": "Er is nog geen pagina geschiedenis opgeslagen.",
|
||||
"No pages yet": "Nog geen pagina's",
|
||||
"No shared pages": "Geen gedeelde pagina's",
|
||||
"No results found...": "Geen resultaten gevonden...",
|
||||
"No user found": "Geen gebruiker gevonden",
|
||||
"Overview": "Overzicht",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "pagina's",
|
||||
"Password": "Wachtwoord",
|
||||
"Password changed successfully": "Wachtwoord met succes gewijzigd",
|
||||
"People": "Personen",
|
||||
"Pending": "Wachtende",
|
||||
"Please confirm your action": "Bevestig alstublieft uw actie",
|
||||
"Preferences": "Voorkeuren",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Profiel",
|
||||
"Recently updated": "Recent bijgewerkt",
|
||||
"Remove": "Verwijderen",
|
||||
"Remove from favorites": "Verwijderen uit favorieten",
|
||||
"Remove group member": "Lid uit groep verwijderd",
|
||||
"Remove space member": "Lid uit ruimte verwijderd",
|
||||
"Restore": "Herstellen",
|
||||
@@ -149,53 +157,54 @@
|
||||
"Search...": "Zoeken...",
|
||||
"Select language": "Selecteer taal",
|
||||
"Select role": "Selecteer rol",
|
||||
"Select role to assign to all invited members": "Selecteer rol en wijs toe aan alle uitgenodigde leden",
|
||||
"Select role to assign to all invited members": "Selecteer een rol om toe te wijzen aan alle uitgenodigde leden",
|
||||
"Select theme": "Selecteer thema",
|
||||
"Send invitation": "Uitnodiging versturen",
|
||||
"Send invitation": "Uitnodiging verzenden",
|
||||
"Invitation sent": "Uitnodiging verzonden",
|
||||
"Settings": "Instellingen",
|
||||
"Setup workspace": "Werkruimte instellen",
|
||||
"Sign In": "Inloggen",
|
||||
"Sign Up": "Aanmelden",
|
||||
"Slug": "Afkorting",
|
||||
"Sign Up": "Registreren",
|
||||
"Slug": "Slug",
|
||||
"Space": "Ruimte",
|
||||
"Space description": "Omschrijving van de ruimte",
|
||||
"Space menu": "Ruimte menu",
|
||||
"Space name": "Naam ruimte",
|
||||
"Space settings": "Ruimte instellingen",
|
||||
"Space slug": "Ruimte afkorting",
|
||||
"Space description": "Ruimtebeschrijving",
|
||||
"Space menu": "Ruimtemenu",
|
||||
"Space name": "Ruimtenaam",
|
||||
"Space settings": "Ruimte-instellingen",
|
||||
"Space slug": "Ruimte-slug",
|
||||
"Spaces": "Ruimtes",
|
||||
"Spaces you belong to": "Ruimtes waar je bij hoort",
|
||||
"Spaces you belong to": "Ruimtes waarvan je lid bent",
|
||||
"No space found": "Geen ruimte gevonden",
|
||||
"Search for spaces": "Zoek naar ruimtes",
|
||||
"Start typing to search...": "Begin met typen om te zoeken...",
|
||||
"Status": "Status",
|
||||
"Successfully imported": "Succesvol geïmporteerd",
|
||||
"Successfully restored": "Succesvol hersteld",
|
||||
"System settings": "Systeem instellingen",
|
||||
"System settings": "Systeeminstellingen",
|
||||
"Templates": "Sjablonen",
|
||||
"Theme": "Thema",
|
||||
"To change your email, you have to enter your password and new email.": "Om uw e-mailadres te wijzigen, moet u uw wachtwoord en nieuwe e-mail invullen.",
|
||||
"Toggle full page width": "Schakel volledige pagina breedte in",
|
||||
"Toggle full page width": "Volledige paginabreedte in-/uitschakelen",
|
||||
"Unable to import pages. Please try again.": "Pagina's importeren is niet gelukt. Probeer het opnieuw.",
|
||||
"untitled": "naamloos",
|
||||
"Untitled": "Naamloos",
|
||||
"untitled": "zonder titel",
|
||||
"Untitled": "Zonder titel",
|
||||
"Updated successfully": "Succesvol bijgewerkt",
|
||||
"User": "Gebruiker",
|
||||
"Workspace": "Werkruimte",
|
||||
"Workspace Name": "Naam werkruimte",
|
||||
"Workspace settings": "Instellingen werkruimte",
|
||||
"Workspace Name": "Naam van werkruimte",
|
||||
"Workspace settings": "Werkruimte-instellingen",
|
||||
"You can change your password here.": "U kunt hier uw wachtwoord wijzigen.",
|
||||
"Your Email": "Uw e-mailadres",
|
||||
"Your Email": "Je e-mailadres",
|
||||
"Your import is complete.": "Uw import is voltooid.",
|
||||
"Your name": "Uw naam",
|
||||
"Your Name": "Uw Naam",
|
||||
"Your password": "Uw wachtwoord",
|
||||
"Your name": "Je naam",
|
||||
"Your Name": "Je naam",
|
||||
"Your password": "Je wachtwoord",
|
||||
"Your password must be a minimum of 8 characters.": "Uw wachtwoord moet minimaal 8 tekens bevatten.",
|
||||
"Sidebar toggle": "Zijbalk toggelen",
|
||||
"Sidebar toggle": "Zijbalk in-/uitschakelen",
|
||||
"Comments": "Opmerkingen",
|
||||
"404 page not found": "404 pagina niet gevonden",
|
||||
"Sorry, we can't find the page you are looking for.": "Sorry, we kunnen de pagina die u zoekt niet vinden.",
|
||||
"Take me back to homepage": "Ga terug naar de homepage",
|
||||
"Take me back to homepage": "Breng me terug naar de homepage",
|
||||
"Forgot password": "Wachtwoord vergeten",
|
||||
"Forgot your password?": "Wachtwoord vergeten?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "Een link om uw wachtwoord te resetten is verstuurd naar uw e-mail. Controleer uw inbox.",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Antwoord...",
|
||||
"Error loading comments.": "Fout bij het laden van reacties.",
|
||||
"No comments yet.": "Nog geen reacties.",
|
||||
"No open comments.": "Geen openstaande opmerkingen.",
|
||||
"No resolved comments.": "Geen opgeloste reacties.",
|
||||
"Add a comment...": "Voeg een opmerking toe...",
|
||||
"Edit comment": "Bewerk reactie",
|
||||
"Delete comment": "Verwijder reactie",
|
||||
"Are you sure you want to delete this comment?": "Weet je zeker dat je deze reactie wilt verwijderen?",
|
||||
"Delete chat": "Chat verwijderen",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Weet je zeker dat je '{{title}}' wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
|
||||
"Comment created successfully": "Reactie succesvol aangemaakt",
|
||||
"Error creating comment": "Fout bij het aanmaken van reactie",
|
||||
"Comment updated successfully": "Opmerking succesvol bijgewerkt",
|
||||
@@ -217,18 +231,17 @@
|
||||
"Comment deleted successfully": "Reactie met succes verwijderd",
|
||||
"Failed to delete comment": "Verwijderen van reactie mislukt",
|
||||
"Comment resolved successfully": "Reactie succesvol opgelost",
|
||||
"Comment re-opened successfully": "Reactie succesvol heropend",
|
||||
"Comment unresolved successfully": "Reactie succesvol niet-opgelost gemaakt",
|
||||
"Comment re-opened successfully": "Opmerking succesvol heropend",
|
||||
"Comment unresolved successfully": "Opmerking succesvol als onopgelost gemarkeerd",
|
||||
"Failed to resolve comment": "Reactie oplossen mislukt",
|
||||
"Resolve comment": "Reactie oplossen",
|
||||
"Unresolve comment": "Reactie niet oplossen",
|
||||
"Resolve Comment Thread": "Reactiedraad oplossen",
|
||||
"Unresolve Comment Thread": "Reactiedraad niet oplossen",
|
||||
"Resolve comment": "Opmerking oplossen",
|
||||
"Unresolve comment": "Markering opgelost ongedaan maken",
|
||||
"Resolve Comment Thread": "Opmerkingsthread oplossen",
|
||||
"Unresolve Comment Thread": "Oplossen van opmerkingsthread ongedaan maken",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Weet u zeker dat u deze reactiedraad wilt oplossen? Dit zal het als voltooid markeren.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Weet u zeker dat u deze reactiedraad niet wilt oplossen?",
|
||||
"Resolved": "Opgelost",
|
||||
"No active comments.": "Geen actieve reacties.",
|
||||
"No resolved comments.": "Geen opgeloste reacties.",
|
||||
"Revoke invitation": "Uitnodiging intrekken",
|
||||
"Revoke": "Intrekken",
|
||||
"Don't": "Niet doen",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "Weet u zeker dat u deze ruimte wil verwijderen?",
|
||||
"Delete this space with all its pages and data.": "Verwijder deze ruimte met alle pagina's en gegevens.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "Alle pagina's, opmerkingen, bijlagen en permissies in deze ruimte zullen onherroepelijk worden verwijderd.",
|
||||
"Confirm space name": "Bevestig naam van ruimte",
|
||||
"Confirm space name": "Bevestig ruimtenaam",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Typ de ruimtenaam <b>{{spaceName}}</b> om uw actie te bevestigen.",
|
||||
"Format": "Formaat",
|
||||
"Include subpages": "Inclusief onderliggend pagina's",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Rij hieronder toevoegen",
|
||||
"Delete table": "Verwijder tabel",
|
||||
"Info": "Info",
|
||||
"Note": "Opmerking",
|
||||
"Success": "Geslaagd",
|
||||
"Warning": "Waarschuwing",
|
||||
"Danger": "Gevaar",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Opslaan & Afsluiten",
|
||||
"Double-click to edit Excalidraw diagram": "Dubbelklik om Excalidraw diagram te bewerken",
|
||||
"Paste link": "Link plakken",
|
||||
"Paste link or search pages": "Plak link of zoek pagina's",
|
||||
"Link to web page": "Link naar webpagina",
|
||||
"Recents": "Recent",
|
||||
"Page or URL": "Pagina of URL",
|
||||
"Link title": "Kop van de link",
|
||||
"Edit link": "Link bewerken",
|
||||
"Remove link": "Link verwijderen",
|
||||
"Add link": "Link toevoegen",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Roze",
|
||||
"Gray": "Grijs",
|
||||
"Embed link": "Link insluiten",
|
||||
"Invalid {{provider}} embed link": "Ongeldige {{provider}} insluitingslink",
|
||||
"Invalid {{provider}} embed link": "Ongeldige {{provider}}-insluitlink",
|
||||
"Embed {{provider}}": "Insluiten {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Voer {{provider}} link in om in te voegen",
|
||||
"Bold": "Dikgedrukt",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Horizontale lijn invoegen",
|
||||
"Upload any image from your device.": "Upload een afbeelding vanaf uw apparaat.",
|
||||
"Upload any video from your device.": "Upload een video vanaf uw apparaat.",
|
||||
"Upload any audio from your device.": "Upload een audio vanaf uw apparaat.",
|
||||
"Upload any file from your device.": "Upload een bestand vanaf uw apparaat.",
|
||||
"Uploading {{name}}": "Uploaden {{name}}",
|
||||
"Uploading file": "Bestand uploaden",
|
||||
@@ -339,25 +359,45 @@
|
||||
"Insert collapsible block.": "Inklapbaar blok invoegen.",
|
||||
"Video": "Video",
|
||||
"Divider": "Scheidingslijn",
|
||||
"Quote": "Quote",
|
||||
"Quote": "Citaat",
|
||||
"Image": "Afbeelding",
|
||||
"File attachment": "Bestand bijlage",
|
||||
"Toggle block": "Schakel blok in/uit",
|
||||
"Callout": "Opmerking",
|
||||
"Audio": "Audio",
|
||||
"Embed PDF": "PDF insluiten",
|
||||
"Upload and embed a PDF file.": "Upload en sluit een PDF-bestand in.",
|
||||
"Embed as PDF": "Insluiten als PDF",
|
||||
"Failed to load PDF": "Laden van PDF mislukt",
|
||||
"Convert to attachment": "Converteren naar bijlage",
|
||||
"File attachment": "Bestandsbijlage",
|
||||
"Toggle block": "In-/uitklapbaar blok",
|
||||
"Callout": "Uitgelicht blok",
|
||||
"Insert callout notice.": "Invoegen opmerking.",
|
||||
"Math inline": "Wiskundige inline",
|
||||
"Math inline": "Inline wiskunde",
|
||||
"Insert inline math equation.": "Wiskundige inline vergelijking invoegen.",
|
||||
"Math block": "Wiskunde blok",
|
||||
"Insert math equation": "Wiskundige inline vergelijking invoegen",
|
||||
"Mermaid diagram": "Mermaid diagram",
|
||||
"Insert mermaid diagram": "Voeg mermaid diagram in",
|
||||
"Insert and design Drawio diagrams": "Drawio diagrammen invoegen en ontwerpen",
|
||||
"Insert current date": "Huidige datum invoeren",
|
||||
"Draw and sketch excalidraw diagrams": "Teken en schets excalidraw diagrammen",
|
||||
"Math block": "Wiskundeblok",
|
||||
"Insert math equation": "Wiskundige vergelijking invoegen",
|
||||
"Mermaid diagram": "Mermaid-diagram",
|
||||
"Insert mermaid diagram": "Mermaid-diagram invoegen",
|
||||
"Insert and design Drawio diagrams": "Drawio-diagrammen invoegen en ontwerpen",
|
||||
"Insert current date": "Huidige datum invoegen",
|
||||
"Draw and sketch excalidraw diagrams": "Excalidraw-diagrammen tekenen en schetsen",
|
||||
"Multiple": "Meerdere",
|
||||
"Turn into": "Omzetten naar",
|
||||
"Text align": "Tekstuitlijning",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Deze pagina is mogelijk verwijderd of verplaatst, of u heeft er geen toegang toe.",
|
||||
"Go to homepage": "Ga naar de startpagina",
|
||||
"Pages you create will show up here.": "Pagina's die u aanmaakt, verschijnen hier.",
|
||||
"Heading {{level}}": "Kop {{level}}",
|
||||
"Toggle title": "Schakel titel in/uit",
|
||||
"Write anything. Enter \"/\" for commands": "Schrijf iets. Voer \"/\" in voor commando's",
|
||||
"Toggle title": "Titel in-/uitklappen",
|
||||
"Write anything. Enter \"/\" for commands": "Schrijf iets. Typ \"/\" voor opdrachten",
|
||||
"Write...": "Typ...",
|
||||
"Column count": "Aantal kolommen",
|
||||
"{{count}} Columns": "{{count}} kolommen",
|
||||
"Equal columns": "Gelijke kolommen",
|
||||
"Left sidebar": "Linker zijbalk",
|
||||
"Right sidebar": "Rechter zijbalk",
|
||||
"Wide center": "Brede middenkolom",
|
||||
"Left wide": "Brede linkerkolom",
|
||||
"Right wide": "Brede rechterkolom",
|
||||
"Names do not match": "Namen komen niet overeen",
|
||||
"Today, {{time}}": "Vandaag, {{time}}",
|
||||
"Yesterday, {{time}}": "Gisteren, {{time}}",
|
||||
@@ -366,20 +406,27 @@
|
||||
"Space deleted successfully": "Ruimte succesvol verwijderd",
|
||||
"Members added successfully": "Leden succesvol toegevoegd",
|
||||
"Member removed successfully": "Lid succesvol verwijderd",
|
||||
"Member role updated successfully": "Lidrol succesvol bijgewerkt",
|
||||
"Created by: <b>{{creatorName}}</b>": "Gemaakt door: <b>{{creatorName}}</b>",
|
||||
"Member role updated successfully": "Rol van lid succesvol bijgewerkt",
|
||||
"Created by: <b>{{creatorName}}</b>": "Aangemaakt door: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Aangemaakt op: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Bewerkt door {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Aantal woorden: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Aantal tekens: {{characterCount}}",
|
||||
"New update": "Nieuwe update",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} is beschikbaar",
|
||||
"Default page edit mode": "Standaard pagina bewerkmodus",
|
||||
"Default page edit mode": "Standaard bewerkingsmodus voor pagina",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Kies uw voorkeurs bewerkmodus voor pagina's. Vermijd per ongeluk bewerken.",
|
||||
"Reading": "Lezen",
|
||||
"Delete member": "Verwijder lid",
|
||||
"Delete member": "Lid verwijderen",
|
||||
"Member deleted successfully": "Lid succesvol verwijderd",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Weet u zeker dat u dit lid van de werkruimte wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden.",
|
||||
"Deactivate member": "Lid deactiveren",
|
||||
"Activate member": "Lid activeren",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Weet u zeker dat u dit lid van de werkruimte wilt deactiveren? Deze persoon heeft daarna geen toegang meer tot deze werkruimte.",
|
||||
"Are you sure you want to activate this workspace member?": "Weet u zeker dat u dit lid van de werkruimte wilt activeren?",
|
||||
"Deactivate": "Deactiveren",
|
||||
"Activate": "Activeren",
|
||||
"Deactivated": "Gedeactiveerd",
|
||||
"Move": "Verplaatsen",
|
||||
"Move page": "Pagina verplaatsen",
|
||||
"Move page to a different space.": "Verplaats pagina naar een andere ruimte.",
|
||||
@@ -391,22 +438,41 @@
|
||||
"Shared by": "Gedeeld door",
|
||||
"Shared at": "Gedeeld op",
|
||||
"Inherits public sharing from": "Erft openbaar delen van",
|
||||
"Share to web": "Delen naar web",
|
||||
"Shared to web": "Gedeeld naar web",
|
||||
"Share to web": "Delen op het web",
|
||||
"Shared to web": "Gedeeld op het web",
|
||||
"Anyone with the link can view this page": "Iedereen met de link kan deze pagina bekijken",
|
||||
"Make this page publicly accessible": "Maak deze pagina openbaar toegankelijk",
|
||||
"Include sub-pages": "Inclusief subpagina's",
|
||||
"Include sub-pages": "Subpagina's opnemen",
|
||||
"Make sub-pages public too": "Maak subpagina's ook openbaar",
|
||||
"Allow search engines to index page": "Sta zoekmachines toe om pagina te indexeren",
|
||||
"Allow search engines to index page": "Sta zoekmachines toe de pagina te indexeren",
|
||||
"Open page": "Pagina openen",
|
||||
"Page": "Pagina",
|
||||
"Delete public share link": "Verwijder openbare deel-link",
|
||||
"Delete share": "Verwijder deel",
|
||||
"Delete public share link": "Openbare deellink verwijderen",
|
||||
"Delete share": "Deel verwijderen",
|
||||
"Are you sure you want to delete this shared link?": "Weet u zeker dat u deze gedeelde link wilt verwijderen?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Openbaar gedeelde pagina's van ruimtes waarvan u lid bent, verschijnen hier",
|
||||
"Share deleted successfully": "Delen succesvol verwijderd",
|
||||
"Share not found": "Delen niet gevonden",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Openbaar gedeelde pagina's uit ruimtes waarvan je lid bent, verschijnen hier",
|
||||
"Share deleted successfully": "Deel succesvol verwijderd",
|
||||
"Share not found": "Deel niet gevonden",
|
||||
"Failed to share page": "Pagina delen mislukt",
|
||||
"Disable public sharing": "Openbaar delen uitschakelen",
|
||||
"Prevent members from sharing pages publicly.": "Voorkom dat leden pagina's openbaar delen.",
|
||||
"Toggle public sharing": "Wissel openbaar delen",
|
||||
"Toggle space public sharing": "Wissel openbaar delen van ruimte",
|
||||
"Allow viewers to comment": "Toestaan dat kijkers reageren",
|
||||
"Allow viewers to add comments on pages in this space.": "Sta kijkers toe om reacties toe te voegen op pagina\u0019s in deze ruimte.",
|
||||
"Toggle viewer comments": "Reacties van kijkers in- of uitschakelen",
|
||||
"Public sharing is disabled at the workspace level": "Openbaar delen is uitgeschakeld op werkruimteniveau",
|
||||
"Prevent pages in this space from being shared publicly.": "Voorkom dat pagina's in deze ruimte openbaar worden gedeeld.",
|
||||
"Page permissions": "Pagina rechten",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Beheer wie individuele pagina's kan bekijken en bewerken. Beschikbaar met een Enterprise-licentie.",
|
||||
"Enable public sharing": "Openbaar delen inschakelen",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Weet je zeker dat je openbaar delen wilt inschakelen? Leden kunnen pagina's openbaar delen.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Weet je zeker dat je openbaar delen wilt uitschakelen? Alle bestaande gedeelde links in deze werkruimte zullen worden verwijderd.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Weet je zeker dat je openbaar delen voor deze ruimte wilt inschakelen?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Weet je zeker dat je openbaar delen wilt uitschakelen? Alle bestaande gedeelde links in deze ruimte zullen worden verwijderd.",
|
||||
"Public sharing is disabled": "Openbaar delen is uitgeschakeld",
|
||||
"Public sharing has been disabled at the workspace level.": "Openbaar delen is uitgeschakeld op werkruimteniveau.",
|
||||
"Public sharing has been disabled for this space.": "Openbaar delen is uitgeschakeld voor deze ruimte.",
|
||||
"Copy page": "Pagina kopiëren",
|
||||
"Copy page to a different space.": "Kopieer pagina naar een andere ruimte.",
|
||||
"Page copied successfully": "Pagina succesvol gekopieerd",
|
||||
@@ -415,126 +481,127 @@
|
||||
"Not found": "Niet gevonden",
|
||||
"Previous Match (Shift+Enter)": "Vorige overeenkomst (Shift+Enter)",
|
||||
"Next match (Enter)": "Volgende overeenkomst (Enter)",
|
||||
"Match case (Alt+C)": "Hoofdlettergevoeligheid (Alt+C)",
|
||||
"Match case (Alt+C)": "Hoofdlettergevoelig (Alt+C)",
|
||||
"Replace": "Vervangen",
|
||||
"Close (Escape)": "Sluiten (Escape)",
|
||||
"Replace (Enter)": "Vervangen (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Alles vervangen (Ctrl+Alt+Enter)",
|
||||
"Replace all": "Alles vervangen",
|
||||
"View all spaces": "Bekijk alle ruimtes",
|
||||
"View all": "Alles bekijken",
|
||||
"View all spaces": "Alle ruimtes bekijken",
|
||||
"Error": "Fout",
|
||||
"Failed to disable MFA": "MFA uitschakelen mislukt",
|
||||
"Disable two-factor authentication": "Twee-factor authenticatie uitschakelen",
|
||||
"Disable two-factor authentication": "Tweefactorauthenticatie uitschakelen",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Indien u twee-factor authenticatie uitschakelt, zal uw account minder veilig zijn. U heeft alleen uw wachtwoord nodig om in te loggen.",
|
||||
"Please enter your password to disable two-factor authentication:": "Voer uw wachtwoord in om twee-factor authenticatie uit te schakelen:",
|
||||
"Two-factor authentication has been enabled": "Twee-factor authenticatie is ingeschakeld",
|
||||
"Two-factor authentication has been disabled": "Twee-factor authenticatie is uitgeschakeld",
|
||||
"2-step verification": "2-staps verificatie",
|
||||
"Two-factor authentication has been enabled": "Tweefactorauthenticatie is ingeschakeld",
|
||||
"Two-factor authentication has been disabled": "Tweefactorauthenticatie is uitgeschakeld",
|
||||
"2-step verification": "Verificatie in 2 stappen",
|
||||
"Protect your account with an additional verification layer when signing in.": "Bescherm uw account met een extra verificatielaag tijdens het inloggen.",
|
||||
"Two-factor authentication is active on your account.": "Twee-factor authenticatie is actief op uw account.",
|
||||
"Add 2FA method": "2FA-methode toevoegen",
|
||||
"Backup codes": "Back-up codes",
|
||||
"Backup codes": "Back-upcodes",
|
||||
"Disable": "Uitschakelen",
|
||||
"Invalid verification code": "Ongeldige verificatiecode",
|
||||
"New backup codes have been generated": "Nieuwe back-up codes zijn gegenereerd",
|
||||
"Failed to regenerate backup codes": "Back-up codes opnieuw genereren mislukt",
|
||||
"About backup codes": "Over back-up codes",
|
||||
"New backup codes have been generated": "Nieuwe back-upcodes zijn gegenereerd",
|
||||
"Failed to regenerate backup codes": "Back-upcodes opnieuw genereren mislukt",
|
||||
"About backup codes": "Over back-upcodes",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Back-up codes kunnen worden gebruikt om uw account te bereiken als u toegang tot uw authenticator-app verliest. Elke code kan slechts één keer worden gebruikt.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "U kunt te allen tijde nieuwe back-up codes genereren. Dit zal alle bestaande codes ongeldig maken.",
|
||||
"Confirm password": "Bevestig wachtwoord",
|
||||
"Generate new backup codes": "Genereer nieuwe back-up codes",
|
||||
"Save your new backup codes": "Sla uw nieuwe back-up codes op",
|
||||
"Generate new backup codes": "Nieuwe back-upcodes genereren",
|
||||
"Save your new backup codes": "Sla je nieuwe back-upcodes op",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Zorg ervoor dat u deze codes op een veilige plek opslaat. Uw oude back-up codes zijn niet langer geldig.",
|
||||
"Your new backup codes": "Uw nieuwe back-up codes",
|
||||
"I've saved my backup codes": "Ik heb mijn back-up codes opgeslagen",
|
||||
"Your new backup codes": "Je nieuwe back-upcodes",
|
||||
"I've saved my backup codes": "Ik heb mijn back-upcodes opgeslagen",
|
||||
"Failed to setup MFA": "MFA instellen mislukt",
|
||||
"Setup & Verify": "Instellen & Verifiëren",
|
||||
"Add to authenticator": "Toevoegen aan de authenticator",
|
||||
"1. Scan this QR code with your authenticator app": "1. Scan deze QR-code met uw authenticator-app",
|
||||
"Setup & Verify": "Instellen en verifiëren",
|
||||
"Add to authenticator": "Toevoegen aan authenticator",
|
||||
"1. Scan this QR code with your authenticator app": "1. Scan deze QR-code met je authenticator-app",
|
||||
"Can't scan the code?": "Kan de code niet scannen?",
|
||||
"Enter this code manually in your authenticator app:": "Voer deze code handmatig in uw authenticator-app in:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Voer de 6-cijferige code van uw authenticator in",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Voer de 6-cijferige code van je authenticator in",
|
||||
"Verify and enable": "Verifiëren en inschakelen",
|
||||
"Failed to generate QR code. Please try again.": "Het genereren van de QR-code is mislukt. Probeer het opnieuw.",
|
||||
"Backup": "Back-up",
|
||||
"Save codes": "Codes opslaan",
|
||||
"Save your backup codes": "Sla uw back-up codes op",
|
||||
"Save your backup codes": "Sla je back-upcodes op",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Deze codes kunnen worden gebruikt om toegang te krijgen tot uw account als u de toegang tot uw authenticator-app verliest. Elke code kan slechts één keer worden gebruikt.",
|
||||
"Print": "Afdrukken",
|
||||
"Two-factor authentication has been set up. Please log in again.": "Twee-factor authenticatie is ingesteld. Log alstublieft opnieuw in.",
|
||||
"Two-Factor authentication required": "Twee-factor authenticatie vereist",
|
||||
"Your workspace requires two-factor authentication for all users": "Uw werkruimte vereist twee-factor authenticatie voor alle gebruikers",
|
||||
"Two-Factor authentication required": "Tweefactorauthenticatie vereist",
|
||||
"Your workspace requires two-factor authentication for all users": "Je werkruimte vereist tweefactorauthenticatie voor alle gebruikers",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Om toegang te blijven krijgen tot uw werkruimte, moet u twee-factor authenticatie instellen. Dit voegt een extra beveiligingslaag toe aan uw account.",
|
||||
"Set up two-factor authentication": "Stel twee-factor authenticatie in",
|
||||
"Set up two-factor authentication": "Tweefactorauthenticatie instellen",
|
||||
"Cancel and logout": "Annuleren en uitloggen",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Uw werkruimte vereist twee-factor authenticatie. Stel het in om door te gaan.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Dit voegt een extra beveiligingslaag toe aan uw account door een verificatiecode van uw authenticator-app te vereisen.",
|
||||
"Password is required": "Wachtwoord is vereist",
|
||||
"Password must be at least 8 characters": "Wachtwoord moet minimaal 8 tekens zijn",
|
||||
"Please enter a 6-digit code": "Voer alstublieft een 6-cijferige code in",
|
||||
"Code must be exactly 6 digits": "Code moet exact 6 cijfers zijn",
|
||||
"Enter the 6-digit code found in your authenticator app": "Voer de 6-cijferige code in die in uw authenticator-app staat",
|
||||
"Password is required": "Wachtwoord is verplicht",
|
||||
"Password must be at least 8 characters": "Wachtwoord moet minimaal 8 tekens bevatten",
|
||||
"Please enter a 6-digit code": "Voer een 6-cijferige code in",
|
||||
"Code must be exactly 6 digits": "Code moet precies 6 cijfers bevatten",
|
||||
"Enter the 6-digit code found in your authenticator app": "Voer de 6-cijferige code uit je authenticator-app in",
|
||||
"Need help authenticating?": "Hulp nodig bij het authenticeren?",
|
||||
"MFA QR Code": "MFA QR-code",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Account succesvol aangemaakt. Log alstublieft in om twee-factor authenticatie in te stellen.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "Wachtwoord reset succesvol. Log in met uw nieuwe wachtwoord en voltooi twee-factor authenticatie.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Wachtwoord reset succesvol. Log in met uw nieuwe wachtwoord om twee-factor authenticatie in te stellen.",
|
||||
"Password reset was successful. Please log in with your new password.": "De wachtwoord reset was succesvol. Log in met uw nieuwe wachtwoord.",
|
||||
"Two-factor authentication": "Twee-factor authenticatie",
|
||||
"Use authenticator app instead": "Gebruik in plaats daarvan de authenticator-app",
|
||||
"Verify backup code": "Back-up code verifiëren",
|
||||
"Use backup code": "Gebruik back-up code",
|
||||
"Enter one of your backup codes": "Voer een van uw back-up codes in",
|
||||
"Backup code": "Back-up code",
|
||||
"Two-factor authentication": "Tweefactorauthenticatie",
|
||||
"Use authenticator app instead": "Gebruik in plaats daarvan een authenticator-app",
|
||||
"Verify backup code": "Back-upcode verifiëren",
|
||||
"Use backup code": "Back-upcode gebruiken",
|
||||
"Enter one of your backup codes": "Voer een van je back-upcodes in",
|
||||
"Backup code": "Back-upcode",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Voer een van uw back-up codes in. Elke back-up code kan slechts één keer worden gebruikt.",
|
||||
"Verify": "Verifiëren",
|
||||
"Trash": "Prullenbak",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Pagina's in de prullenbak worden na 30 dagen permanent verwijderd.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "Pagina's in de prullenbak worden na {{count}} dagen permanent verwijderd.",
|
||||
"Deleted": "Verwijderd",
|
||||
"No pages in trash": "Geen pagina's in de prullenbak",
|
||||
"Permanently delete page?": "Pagina permanent verwijderen?",
|
||||
"Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "Weet u zeker dat u '{{title}}' permanent wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
|
||||
"Restore '{{title}}' and its sub-pages?": "'{{title}}' en zijn subpagina's herstellen?",
|
||||
"Move to trash": "Naar de prullenbak verplaatsen",
|
||||
"Move to trash": "Verplaatsen naar prullenbak",
|
||||
"Move this page to trash?": "Deze pagina naar de prullenbak verplaatsen?",
|
||||
"Restore page": "Pagina herstellen",
|
||||
"Page moved to trash": "Pagina verplaatst naar de prullenbak",
|
||||
"Page moved to trash": "Pagina verplaatst naar prullenbak",
|
||||
"Page restored successfully": "Pagina succesvol hersteld",
|
||||
"Deleted by": "Verwijderd door",
|
||||
"Deleted at": "Verwijderd op",
|
||||
"Preview": "Voorbeeld",
|
||||
"Subpages": "Subpagina's",
|
||||
"Failed to load subpages": "Laden van subpagina's mislukt",
|
||||
"Failed to load subpages": "Subpagina's laden mislukt",
|
||||
"No subpages": "Geen subpagina's",
|
||||
"Subpages (Child pages)": "Subpagina's (Kindpagina's)",
|
||||
"List all subpages of the current page": "Lijst van alle subpagina's van de huidige pagina",
|
||||
"Subpages (Child pages)": "Subpagina's (onderliggende pagina's)",
|
||||
"List all subpages of the current page": "Toon alle subpagina's van de huidige pagina",
|
||||
"Attachments": "Bijlagen",
|
||||
"All spaces": "Alle ruimtes",
|
||||
"Unknown": "Onbekend",
|
||||
"Find a space": "Vind een ruimte",
|
||||
"Find a space": "Zoek een ruimte",
|
||||
"Search in all your spaces": "Zoek in al je ruimtes",
|
||||
"Type": "Type",
|
||||
"Enterprise": "Onderneming",
|
||||
"Enterprise": "Enterprise",
|
||||
"Download attachment": "Bijlage downloaden",
|
||||
"Allowed email domains": "Toegestane e-maildomeinen",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Alleen gebruikers met e-mailadressen van deze domeinen kunnen zich aanmelden via SSO.",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Alleen gebruikers met e-mailadressen van deze domeinen kunnen zich via SSO registreren.",
|
||||
"Enter valid domain names separated by comma or space": "Voer geldige domeinnamen in, gescheiden door komma of spatie",
|
||||
"Enforce two-factor authentication": "Handhaaf tweefactorauthenticatie",
|
||||
"Enforce two-factor authentication": "Tweefactorauthenticatie afdwingen",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Na handhaving moeten alle leden tweefactorauthenticatie inschakelen om toegang te krijgen tot de werkomgeving.",
|
||||
"Toggle MFA enforcement": "Schakel MFA-handhaving in of uit",
|
||||
"Toggle MFA enforcement": "MFA-afdwinging in-/uitschakelen",
|
||||
"Display name": "Weergavenaam",
|
||||
"Allow signup": "Aanmelden toestaan",
|
||||
"Allow signup": "Registratie toestaan",
|
||||
"Enabled": "Ingeschakeld",
|
||||
"Advanced Settings": "Geavanceerde instellingen",
|
||||
"Enable TLS/SSL": "TLS/SSL inschakelen",
|
||||
"Use secure connection to LDAP server": "Gebruik een beveiligde verbinding met de LDAP-server",
|
||||
"Group sync": "Groepssynchronisatie",
|
||||
"No SSO providers found.": "Geen SSO-providers gevonden.",
|
||||
"Delete SSO provider": "Verwijder SSO-provider",
|
||||
"Delete SSO provider": "SSO-provider verwijderen",
|
||||
"Are you sure you want to delete this SSO provider?": "Weet u zeker dat u deze SSO-provider wilt verwijderen?",
|
||||
"Action": "Actie",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} configuratie",
|
||||
"Icon": "Icoon",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}}-configuratie",
|
||||
"Icon": "Pictogram",
|
||||
"Upload image": "Afbeelding uploaden",
|
||||
"Remove image": "Afbeelding verwijderen",
|
||||
"Failed to remove image": "Afbeelding verwijderen mislukt",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Deze actie kan niet ongedaan worden gemaakt. Alle toepassingen die deze API-sleutel gebruiken, zullen niet meer werken.",
|
||||
"Update API key": "API-sleutel bijwerken",
|
||||
"Manage API keys for all users in the workspace": "Beheer API-sleutels voor alle gebruikers in de werkruimte",
|
||||
"Restrict API key creation to admins": "Beperk het aanmaken van API-sleutels tot beheerders.",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Alleen beheerders en eigenaren kunnen nieuwe API-sleutels aanmaken. Bestaande leden-sleutels blijven werken.",
|
||||
"Toggle restrict API keys to admins": "Schakel het beperken van API-sleutels tot beheerders in/uit",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "Het aanmaken van API-sleutels is door je werkruimtebeheerder beperkt tot beheerders.",
|
||||
"AI settings": "AI-instellingen",
|
||||
"AI search": "AI-zoekopdracht",
|
||||
"AI Answer": "AI Antwoord",
|
||||
"Ask AI": "Vraag AI",
|
||||
"AI is thinking...": "AI is aan het nadenken...",
|
||||
"Thinking": "Denken",
|
||||
"Ask a question...": "Stel een vraag...",
|
||||
"AI-powered search (Ask AI)": "AI-ondersteunde zoekopdracht (Vraag AI)",
|
||||
"AI Answers": "AI Antwoorden",
|
||||
"AI-powered search (AI Answers)": "AI-gestuurde zoekopdracht (AI Antwoorden)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI-zoekopdracht maakt gebruik van vectorembeddings om semantische zoekmogelijkheden te bieden in uw werkruimte-inhoud.",
|
||||
"Toggle AI search": "Schakel AI-zoekopdracht in/uit",
|
||||
"Generative AI (Ask AI)": "Generatieve AI (Vraag het AI)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Schakel AI-gestuurde inhoudsgeneratie in de editor in. Hiermee kunnen gebruikers tekst genereren, verbeteren, vertalen en transformeren.",
|
||||
"Toggle generative AI": "Generatieve AI schakelen",
|
||||
"Upgrade your plan": "Upgrade je abonnement",
|
||||
"Available with a paid license": "Beschikbaar met een betaalde licentie",
|
||||
"Upgrade your license tier.": "Upgrade je licentieniveau.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI is alleen beschikbaar in de Docmost Enterprise-editie. Neem contact op met sales@docmost.com.",
|
||||
"AI & MCP": "AI & MCP",
|
||||
"AI": "AI",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Model Context Protocol (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Schakel de MCP-server in zodat AI-assistenten en tools kunnen interageren met de inhoud van uw werkruimte.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP is alleen beschikbaar in de Docmost Enterprise-editie. Neem contact op met sales@docmost.com.",
|
||||
"MCP Server URL": "MCP-server-URL",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Gebruik uw API-sleutel voor authenticatie. U kunt API-sleutels beheren in uw accountinstellingen.",
|
||||
"Supported tools": "Ondersteunde tools",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "In uw werkruimte is MCP ingeschakeld. Gebruik uw API-sleutel om AI-assistenten te koppelen.",
|
||||
"MCP server URL:": "MCP-server-URL:",
|
||||
"Learn more": "Meer informatie",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Beheer API-sleutels voor alle gebruikers in de werkruimte. Bekijk de <anchor>API-documentatie</anchor> voor gebruiksdetails.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Bekijk de <anchor>API-documentatie</anchor> voor gebruiksdetails.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Bekijk de <anchor>MCP-documentatie</anchor>.",
|
||||
"Sources": "Bronnen",
|
||||
"Ask AI not available for attachments": "Vraag AI is niet beschikbaar voor bijlages",
|
||||
"AI Answers not available for attachments": "AI Antwoorden niet beschikbaar voor bijlagen",
|
||||
"No answer available": "Geen antwoord beschikbaar",
|
||||
"Background color": "Achtergrondkleur",
|
||||
"Highlight color": "Markeerkleur",
|
||||
"Remove color": "Kleur verwijderen"
|
||||
"Remove color": "Kleur verwijderen",
|
||||
"Notifications": "Meldingen",
|
||||
"No notifications": "Geen meldingen",
|
||||
"No unread notifications": "Geen ongelezen meldingen",
|
||||
"All notifications": "Alle meldingen",
|
||||
"Unread only": "Alleen ongelezen",
|
||||
"Mark all as read": "Markeer alles als gelezen",
|
||||
"Mark as read": "Markeer als gelezen",
|
||||
"More options": "Meer opties",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> noemde je in een reactie",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> heeft een reactie geplaatst op een pagina",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> heeft een opmerking opgelost",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> heeft je genoemd op een pagina",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> heeft je bewerkingsrechten gegeven voor een pagina",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> heeft je kijkrechten gegeven voor een pagina",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> heeft een pagina bijgewerkt",
|
||||
"Watch page": "Pagina volgen",
|
||||
"Stop watching": "Niet meer volgen",
|
||||
"Watch space": "Ruimte volgen",
|
||||
"Stop watching space": "Ruimte niet meer volgen",
|
||||
"Email notifications": "E-mailmeldingen",
|
||||
"Page updates": "Pagina-updates",
|
||||
"Get notified when pages you watch are updated.": "Ontvang een melding wanneer pagina's die je volgt worden bijgewerkt.",
|
||||
"Page mentions": "Pagina-vermeldingen",
|
||||
"Get notified when someone mentions you on a page.": "Ontvang een melding wanneer iemand je noemt op een pagina.",
|
||||
"Comment mentions": "Vermeldingen in opmerkingen",
|
||||
"Get notified when someone mentions you in a comment.": "Ontvang een melding wanneer iemand je noemt in een opmerking.",
|
||||
"New comments": "Nieuwe opmerkingen",
|
||||
"Get notified about new comments on threads you participate in.": "Ontvang meldingen over nieuwe reacties in threads waaraan je deelneemt.",
|
||||
"Resolved comments": "Opgeloste opmerkingen",
|
||||
"Get notified when your comment is resolved.": "Ontvang een melding wanneer je reactie is opgelost.",
|
||||
"You are now watching this page": "Je volgt nu deze pagina",
|
||||
"You are no longer watching this page": "Je volgt deze pagina niet meer",
|
||||
"You are now watching this space": "Je volgt deze ruimte nu",
|
||||
"You are no longer watching this space": "Je volgt deze ruimte niet meer",
|
||||
"Direct": "Direct",
|
||||
"Updates": "Updates",
|
||||
"Today": "Vandaag",
|
||||
"Yesterday": "Gisteren",
|
||||
"This week": "Deze week",
|
||||
"Older": "Ouder",
|
||||
"Restricted page": "Beperkte pagina",
|
||||
"Restricted pages cannot be shared publicly.": "Beperkte pagina's kunnen niet openbaar worden gedeeld.",
|
||||
"Restricted by parent": "Beperkt door bovenliggende",
|
||||
"Restricted": "Beperkt",
|
||||
"Open": "Open",
|
||||
"Inherits restrictions from ancestor page": "Erft restricties van de bovenliggende pagina",
|
||||
"Only people listed below can access this page": "Alleen onderstaande personen hebben toegang tot deze pagina",
|
||||
"Everyone in this space can access": "Iedereen in deze ruimte heeft toegang",
|
||||
"No additional restrictions on this page": "Geen aanvullende restricties op deze pagina",
|
||||
"Only specific people can access": "Alleen specifieke personen hebben toegang",
|
||||
"Use only inherited restrictions": "Gebruik alleen overgenomen restricties",
|
||||
"Add restrictions on top of inherited": "Restricties toevoegen bovenop geërfd",
|
||||
"Inherited restriction": "Overgenomen restrictie",
|
||||
"Access limited by": "Toegang beperkt door",
|
||||
"Restrict access to control who can view and edit this page": "Beperk de toegang om te bepalen wie deze pagina kan bekijken en bewerken",
|
||||
"Add additional restrictions specific to this page": "Voeg extra beperkingen toe voor deze pagina",
|
||||
"Access": "Toegang",
|
||||
"People with access": "Personen die toegang",
|
||||
"Remove all": "Alles verwijderen",
|
||||
"Remove access": "Toegang verwijderen",
|
||||
"Remove all access": "Alle toegang verwijderen",
|
||||
"Are you sure you want to remove this member's access to the page?": "Weet u zeker dat u de toegang van dit lid tot de pagina wilt intrekken?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Weet u zeker dat u alle specifieke toegang wilt verwijderen? Hiermee wordt de pagina voor iedereen in de ruimte beschikbaar.",
|
||||
"Trash retention": "Bewaartermijn prullenbak",
|
||||
"Pages in trash will be permanently deleted after this period.": "Pagina's in de prullenbak worden na deze periode permanent verwijderd.",
|
||||
"Trash retention updated": "Bewaartermijn prullenbak bijgewerkt",
|
||||
"Failed to update trash retention": "Bijwerken van de bewaartermijn voor de prullenbak is mislukt.",
|
||||
"Removed page restriction": "Pagina-restrictie verwijderd",
|
||||
"Added page permission": "Paginatoestemming toegevoegd",
|
||||
"Removed page permission": "Paginatoestemming verwijderd",
|
||||
"day": "dag",
|
||||
"days": "dagen",
|
||||
"week": "week",
|
||||
"weeks": "weken",
|
||||
"month": "maand",
|
||||
"months": "maanden",
|
||||
"year": "jaar",
|
||||
"years": "jaren",
|
||||
"Period": "Periode",
|
||||
"Fixed date": "Vaste datum",
|
||||
"Indefinitely": "Voor onbepaalde tijd",
|
||||
"Days": "Dagen",
|
||||
"Weeks": "Weken",
|
||||
"Months": "Maanden",
|
||||
"Years": "Jaren",
|
||||
"Pick a date": "Kies een datum",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Maximum is {{max}} {{unit}} voor deze eenheid",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Verloopt nooit. Verificateurs kunnen op elk moment opnieuw verifiëren.",
|
||||
"Verified": "Geverifieerd",
|
||||
"Review needed": "Beoordeling nodig",
|
||||
"Verification expired": "Verificatie verlopen",
|
||||
"Draft": "Concept",
|
||||
"In Approval": "In goedkeuring",
|
||||
"In approval": "In goedkeuring",
|
||||
"Approved": "Goedgekeurd",
|
||||
"Obsolete": "Verouderd",
|
||||
"Expiring": "Verloopt binnenkort",
|
||||
"Set up verification": "Verificatie instellen",
|
||||
"Verify page": "Pagina verifiëren",
|
||||
"Page verification": "Paginaverificatie",
|
||||
"Add verification": "Verificatie toevoegen",
|
||||
"Edit verification": "Verificatie bewerken",
|
||||
"Search by title": "Zoeken op titel",
|
||||
"Choose how this page should stay accurate.": "Kies hoe deze pagina accuraat moet blijven.",
|
||||
"Recurring verification": "Terugkerende verificatie",
|
||||
"Verifiers re-confirm this page on a schedule.": "Verificateurs bevestigen deze pagina opnieuw volgens een schema.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Opnieuw verifiëren volgens een schema (bijv. elke 30 dagen)",
|
||||
"Page stays editable at all times": "Pagina blijft altijd bewerkbaar",
|
||||
"Best for runbooks, FAQs, living documentation": "Het beste voor runbooks, veelgestelde vragen en levende documentatie",
|
||||
"Approval workflow": "Goedkeuringsworkflow",
|
||||
"Formal document lifecycle with named approvers.": "Formele documentlevenscyclus met benoemde goedkeurders.",
|
||||
"Draft → In approval → Approved → Obsolete": "Concept → In goedkeuring → Goedgekeurd → Verouderd",
|
||||
"Locked once approved, with full history": "Vergrendeld zodra goedgekeurd, met volledige geschiedenis",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Ontworpen voor ISO 9001, ISO 13485 en FDA",
|
||||
"Best for SOPs and controlled documents": "Het beste voor SOP's en beheerde documenten",
|
||||
"Back": "Terug",
|
||||
"Quality management": "Kwaliteitsmanagement",
|
||||
"Recurring": "Terugkerend",
|
||||
"Pages move through draft, approval, and approved stages.": "Pagina's doorlopen de fasen concept, goedkeuring en goedgekeurd.",
|
||||
"Verifiers": "Verificateurs",
|
||||
"Add verifier": "Verificateur toevoegen",
|
||||
"I've reviewed this page for accuracy": "Ik heb deze pagina op nauwkeurigheid beoordeeld",
|
||||
"Set up": "Instellen",
|
||||
"Remove verification": "Verificatie verwijderen",
|
||||
"Are you sure you want to remove verification from this page?": "Weet je zeker dat je verificatie van deze pagina wilt verwijderen?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Toegewezen verificateurs moeten deze pagina periodiek opnieuw verifiëren.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Laatst geverifieerd door {{name}} {{time}} (verlopen)",
|
||||
"The fixed expiration date has passed.": "De vaste vervaldatum is verstreken.",
|
||||
"Verified by {{name}} {{time}}": "Geverifieerd door {{name}} {{time}}",
|
||||
"Expires {{date}}": "Verloopt op {{date}}",
|
||||
"Expired {{date}}": "Verlopen op {{date}}",
|
||||
"Mark as obsolete": "Markeren als verouderd",
|
||||
"Mark obsolete": "Markeer als verouderd",
|
||||
"Returned by {{name}} {{time}}": "Teruggestuurd door {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Er is nog geen goedkeuring aangevraagd.",
|
||||
"Submitted by {{name}} {{time}}": "Ingediend door {{name}} {{time}}",
|
||||
"Someone": "Iemand",
|
||||
"Approved by {{name}} {{time}}": "Goedgekeurd door {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Dit document is als verouderd gemarkeerd.",
|
||||
"Rejection comment": "Afwijzingsopmerking",
|
||||
"Reason for returning this document...": "Reden om dit document terug te sturen...",
|
||||
"Confirm rejection": "Afwijzing bevestigen",
|
||||
"Submit for approval": "Indienen voor goedkeuring",
|
||||
"Reject": "Afwijzen",
|
||||
"Approve": "Goedkeuren",
|
||||
"Re-submit for approval": "Opnieuw indienen voor goedkeuring",
|
||||
"Verified until": "Geverifieerd tot",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "Geverifieerde pagina's",
|
||||
"Search pages...": "Pagina's zoeken...",
|
||||
"Filter by space": "Filteren op ruimte",
|
||||
"Filter by type": "Filteren op type",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> heeft een pagina geverifieerd",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> heeft een pagina voor jouw goedkeuring ingediend",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> heeft een pagina teruggestuurd voor revisie",
|
||||
"Page verification expires soon": "Paginaverificatie verloopt binnenkort",
|
||||
"Page verification has expired": "Paginaverificatie is verlopen",
|
||||
"Verifying your email": "Je e-mailadres wordt geverifieerd",
|
||||
"Please wait...": "Even geduld...",
|
||||
"Verification failed. The link may have expired.": "Verificatie mislukt. De link is mogelijk verlopen.",
|
||||
"Check your email": "Controleer je e-mail",
|
||||
"We sent a verification link to {{email}}.": "We hebben een verificatielink naar {{email}} gestuurd.",
|
||||
"We sent a verification link to your email.": "We hebben een verificatielink naar je e-mailadres gestuurd.",
|
||||
"Click the link to verify your email and access your workspace.": "Klik op de link om je e-mailadres te verifiëren en toegang te krijgen tot je werkruimte.",
|
||||
"Resend verification email": "Verificatie-e-mail opnieuw verzenden",
|
||||
"Verification email sent. Please check your inbox.": "Verificatie-e-mail verzonden. Controleer je inbox.",
|
||||
"Failed to resend verification email. Please try again.": "Het verzenden van de verificatie-e-mail is mislukt. Probeer het opnieuw.",
|
||||
"We've sent you an email with your associated workspaces.": "We hebben je een e-mail gestuurd met je gekoppelde werkruimtes.",
|
||||
"Load more": "Meer laden",
|
||||
"Log out of all devices": "Uitloggen op alle apparaten",
|
||||
"Log out of all sessions except this device": "Uitloggen uit alle sessies behalve op dit apparaat",
|
||||
"This Device": "Dit apparaat",
|
||||
"Unknown device": "Onbekend apparaat",
|
||||
"No active sessions": "Geen actieve sessies",
|
||||
"Session revoked": "Sessie ingetrokken",
|
||||
"All other sessions revoked": "Alle andere sessies ingetrokken",
|
||||
"Last used": "Laatst gebruikt",
|
||||
"Created": "Aangemaakt",
|
||||
"Rename": "Hernoemen",
|
||||
"Publish": "Publiceren",
|
||||
"Security": "Beveiliging",
|
||||
"Enforce SSO": "SSO afdwingen",
|
||||
"Once enforced, members will not be able to login with email and password.": "Zodra dit is afgedwongen, kunnen leden niet meer inloggen met e-mail en wachtwoord.",
|
||||
"AI-generated content may not be accurate.": "Door AI gegenereerde inhoud is mogelijk niet nauwkeurig.",
|
||||
"AI Chat": "AI-chat",
|
||||
"Analyze for insights": "Analyseren voor inzichten",
|
||||
"Ask anything...": "Vraag iets...",
|
||||
"Chat history": "Chatgeschiedenis",
|
||||
"Chat name": "Chatnaam",
|
||||
"Close": "Sluiten",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Chat laden mislukt. Er is een fout opgetreden.",
|
||||
"Failed to render this message.": "Dit bericht kon niet worden weergegeven.",
|
||||
"How can I help you today?": "Hoe kan ik je vandaag helpen?",
|
||||
"New chat": "Nieuwe chat",
|
||||
"No chat history": "Geen chatgeschiedenis",
|
||||
"No chats found": "Geen chats gevonden",
|
||||
"No conversations yet": "Nog geen gesprekken",
|
||||
"Open full page": "Volledige pagina openen",
|
||||
"Previous 7 days": "Afgelopen 7 dagen",
|
||||
"Previous 30 days": "Afgelopen 30 dagen",
|
||||
"Search chats...": "Chats zoeken...",
|
||||
"Start a new chat to see it here.": "Start een nieuwe chat om die hier te zien.",
|
||||
"Summarize this page": "Vat deze pagina samen",
|
||||
"Toggle AI Chat": "AI-chat in-/uitschakelen",
|
||||
"Translate this page": "Vertaal deze pagina",
|
||||
"Try a different search term.": "Probeer een andere zoekterm.",
|
||||
"Try again": "Probeer opnieuw",
|
||||
"Untitled chat": "Chat zonder titel",
|
||||
"What can I help you with?": "Waar kan ik je mee helpen?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Adicionar membros",
|
||||
"Add to groups": "Adicionar aos grupos",
|
||||
"Add space members": "Adicionar membros do espaço",
|
||||
"Add to favorites": "Adicionar aos favoritos",
|
||||
"Admin": "Administrador",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Tem certeza de que deseja excluir este grupo? Os membros perderão acesso aos recursos que este grupo possui.",
|
||||
"Are you sure you want to delete this page?": "Tem certeza de que deseja excluir esta página?",
|
||||
@@ -59,7 +60,7 @@
|
||||
"Email": "Email",
|
||||
"Enter a strong password": "Insira uma senha forte",
|
||||
"Enter valid email addresses separated by comma or space max_50": "Insira endereços de email válidos separados por vírgula ou espaço [máx: 50]",
|
||||
"enter valid emails addresses": "insira endereços de email válidos",
|
||||
"enter valid emails addresses": "insira endereços de e-mail válidos",
|
||||
"Enter your current password": "Insira sua senha atual",
|
||||
"enter your full name": "insira seu nome completo",
|
||||
"Enter your new password": "Insira sua nova senha",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Falha ao importar páginas",
|
||||
"Failed to load page. An error occurred.": "Falha ao carregar página. Ocorreu um erro.",
|
||||
"Failed to update data": "Falha ao atualizar dados",
|
||||
"Favorite spaces": "Espaços favoritos",
|
||||
"Favorite spaces appear here": "Os espaços favoritos aparecem aqui",
|
||||
"Favorites": "Favoritos",
|
||||
"Full access": "Acesso total",
|
||||
"Full page width": "Usar largura total da página",
|
||||
"Full width": "Largura total",
|
||||
@@ -92,6 +96,7 @@
|
||||
"Invite by email": "Convidar por email",
|
||||
"Invite members": "Convidar membros",
|
||||
"Invite new members": "Convidar novos membros",
|
||||
"Invite People": "Convidar pessoas",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Membros convidados que ainda não aceitaram o convite aparecerão aqui.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Os membros convidados terão acesso aos espaços que os grupos podem acessar",
|
||||
"Join the workspace": "Entrar no workspace",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "Nenhum grupo encontrado",
|
||||
"No page history saved yet.": "Nenhum histórico de página salvo ainda.",
|
||||
"No pages yet": "Nenhuma página ainda",
|
||||
"No shared pages": "Sem páginas compartilhadas",
|
||||
"No results found...": "Nenhum resultado encontrado...",
|
||||
"No user found": "Nenhum usuário encontrado",
|
||||
"Overview": "Visão geral",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "páginas",
|
||||
"Password": "Senha",
|
||||
"Password changed successfully": "Senha alterada com sucesso",
|
||||
"People": "Pessoas",
|
||||
"Pending": "Pendente",
|
||||
"Please confirm your action": "Por favor, confirme sua ação",
|
||||
"Preferences": "Preferências",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Perfil",
|
||||
"Recently updated": "Atualizado recentemente",
|
||||
"Remove": "Remover",
|
||||
"Remove from favorites": "Remover dos favoritos",
|
||||
"Remove group member": "Remover membro do grupo",
|
||||
"Remove space member": "Remover membro do espaço",
|
||||
"Restore": "Restaurar",
|
||||
@@ -147,16 +155,16 @@
|
||||
"Search for users": "Buscar usuários",
|
||||
"Search for users and groups": "Buscar usuários e grupos",
|
||||
"Search...": "Buscar...",
|
||||
"Select language": "Selecionar idioma",
|
||||
"Select role": "Selecionar função",
|
||||
"Select role to assign to all invited members": "Selecione a função para atribuir a todos os membros convidados",
|
||||
"Select theme": "Selecionar tema",
|
||||
"Select language": "Selecione o idioma",
|
||||
"Select role": "Selecione a função",
|
||||
"Select role to assign to all invited members": "Selecione a função a ser atribuída a todos os membros convidados",
|
||||
"Select theme": "Selecione o tema",
|
||||
"Send invitation": "Enviar convite",
|
||||
"Invitation sent": "Convite enviado",
|
||||
"Settings": "Configurações",
|
||||
"Setup workspace": "Configurar workspace",
|
||||
"Sign In": "Entrar",
|
||||
"Sign Up": "Registrar-se",
|
||||
"Sign Up": "Cadastrar-se",
|
||||
"Slug": "Slug",
|
||||
"Space": "Espaço",
|
||||
"Space description": "Descrição do espaço",
|
||||
@@ -169,34 +177,35 @@
|
||||
"No space found": "Nenhum espaço encontrado",
|
||||
"Search for spaces": "Pesquisar espaços",
|
||||
"Start typing to search...": "Comece a digitar para buscar...",
|
||||
"Status": "Estado",
|
||||
"Status": "Status",
|
||||
"Successfully imported": "Importado com sucesso",
|
||||
"Successfully restored": "Restaurado com sucesso",
|
||||
"System settings": "Configurações do sistema",
|
||||
"Templates": "Modelos",
|
||||
"Theme": "Tema",
|
||||
"To change your email, you have to enter your password and new email.": "Para alterar seu email, você precisa inserir sua senha e o novo email.",
|
||||
"Toggle full page width": "Alternar para largura total da página",
|
||||
"Toggle full page width": "Alternar largura total da página",
|
||||
"Unable to import pages. Please try again.": "Não foi possível importar as páginas. Por favor, tente novamente.",
|
||||
"untitled": "sem título",
|
||||
"Untitled": "Sem título",
|
||||
"Updated successfully": "Atualizado com sucesso",
|
||||
"User": "Usuário",
|
||||
"Workspace": "Espaço de Trabalho",
|
||||
"Workspace Name": "Nome do Workspace",
|
||||
"Workspace": "Workspace",
|
||||
"Workspace Name": "Nome do workspace",
|
||||
"Workspace settings": "Configurações do workspace",
|
||||
"You can change your password here.": "Você pode alterar sua senha aqui.",
|
||||
"Your Email": "Seu email",
|
||||
"Your Email": "Seu e-mail",
|
||||
"Your import is complete.": "Sua importação está concluída.",
|
||||
"Your name": "Seu nome",
|
||||
"Your Name": "Seu Nome",
|
||||
"Your password": "Sua senha",
|
||||
"Your password must be a minimum of 8 characters.": "Sua senha deve ter no mínimo 8 caracteres.",
|
||||
"Sidebar toggle": "Interruptor do painel lateral",
|
||||
"Sidebar toggle": "Alternar barra lateral",
|
||||
"Comments": "Comentários",
|
||||
"404 page not found": "Erro 404: Página não encontrada",
|
||||
"404 page not found": "404 página não encontrada",
|
||||
"Sorry, we can't find the page you are looking for.": "Desculpe, não conseguimos encontrar a página que você está procurando.",
|
||||
"Take me back to homepage": "Leve-me de volta para a página inicial",
|
||||
"Forgot password": "Esqueci a senha",
|
||||
"Take me back to homepage": "Voltar para a página inicial",
|
||||
"Forgot password": "Esqueceu a senha",
|
||||
"Forgot your password?": "Esqueceu sua senha?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "Um link de redefinição de senha foi enviado para o seu email. Por favor, verifique sua caixa de entrada.",
|
||||
"Send reset link": "Enviar link de recuperação",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Responder...",
|
||||
"Error loading comments.": "Erro ao carregar comentários.",
|
||||
"No comments yet.": "Ainda sem comentários.",
|
||||
"No open comments.": "Sem comentários em aberto.",
|
||||
"No resolved comments.": "Sem comentários resolvidos.",
|
||||
"Add a comment...": "Adicione um comentário...",
|
||||
"Edit comment": "Editar comentário",
|
||||
"Delete comment": "Excluir comentário",
|
||||
"Are you sure you want to delete this comment?": "Você tem certeza de que deseja excluir este comentário?",
|
||||
"Delete chat": "Excluir chat",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Tem certeza de que deseja excluir '{{title}}'? Esta ação não pode ser desfeita.",
|
||||
"Comment created successfully": "Comentário criado com sucesso",
|
||||
"Error creating comment": "Erro ao criar comentário",
|
||||
"Comment updated successfully": "Comentário atualizado com sucesso",
|
||||
@@ -218,17 +232,16 @@
|
||||
"Failed to delete comment": "Falha ao excluir comentário",
|
||||
"Comment resolved successfully": "Comentário resolvido com sucesso",
|
||||
"Comment re-opened successfully": "Comentário reaberto com sucesso",
|
||||
"Comment unresolved successfully": "Comentário não resolvido com sucesso",
|
||||
"Comment unresolved successfully": "Comentário marcado como não resolvido com sucesso",
|
||||
"Failed to resolve comment": "Falha ao resolver comentário",
|
||||
"Resolve comment": "Resolver comentário",
|
||||
"Unresolve comment": "Não resolver comentário",
|
||||
"Resolve Comment Thread": "Resolver Fio de Comentários",
|
||||
"Unresolve Comment Thread": "Não resolver Fio de Comentários",
|
||||
"Unresolve comment": "Marcar comentário como não resolvido",
|
||||
"Resolve Comment Thread": "Resolver tópico de comentários",
|
||||
"Unresolve Comment Thread": "Marcar tópico de comentários como não resolvido",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Tem certeza de que deseja resolver este fio de comentários? Isso o marcará como concluído.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Tem certeza de que deseja não resolver este fio de comentários?",
|
||||
"Resolved": "Resolvido",
|
||||
"No active comments.": "Sem comentários ativos.",
|
||||
"No resolved comments.": "Sem comentários resolvidos.",
|
||||
"Revoke invitation": "Cancelar o convite",
|
||||
"Revoke": "Anular",
|
||||
"Don't": "Não",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "Tem certeza de que deseja excluir este espaço?",
|
||||
"Delete this space with all its pages and data.": "Excluir este espaço com todas as suas páginas e dados.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "Todas as páginas, comentários, anexos e permissões neste espaço serão excluídos de forma irreversível.",
|
||||
"Confirm space name": "Confirme o nome do espaço",
|
||||
"Confirm space name": "Confirmar nome do espaço",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Digite o nome do espaço <b>{{spaceName}}</b> para confirmar sua ação.",
|
||||
"Format": "Formato",
|
||||
"Include subpages": "Incluir subpáginas",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Adicionar linha abaixo",
|
||||
"Delete table": "Excluir tabela",
|
||||
"Info": "Informação",
|
||||
"Note": "Observação",
|
||||
"Success": "Sucesso",
|
||||
"Warning": "Aviso",
|
||||
"Danger": "Perigo",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Salvar e Sair",
|
||||
"Double-click to edit Excalidraw diagram": "Clique duas vezes para editar o diagrama Excalidraw",
|
||||
"Paste link": "Colar link",
|
||||
"Paste link or search pages": "Cole o link ou pesquise páginas",
|
||||
"Link to web page": "Link para página da web",
|
||||
"Recents": "Recentes",
|
||||
"Page or URL": "Página ou URL",
|
||||
"Link title": "Título do link",
|
||||
"Edit link": "Editar link",
|
||||
"Remove link": "Remover link",
|
||||
"Add link": "Adicionar link",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Rosa",
|
||||
"Gray": "Cinza",
|
||||
"Embed link": "Link embutido",
|
||||
"Invalid {{provider}} embed link": "Link de incorporação {{provider}} inválido",
|
||||
"Invalid {{provider}} embed link": "Link de incorporação do {{provider}} inválido",
|
||||
"Embed {{provider}}": "Incorporar {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Digite o link do {{provider}} para incorporar",
|
||||
"Bold": "Negrito",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Insira um divisor horizontal",
|
||||
"Upload any image from your device.": "Envie qualquer imagem do seu dispositivo.",
|
||||
"Upload any video from your device.": "Envie qualquer vídeo do seu dispositivo.",
|
||||
"Upload any audio from your device.": "Envie qualquer áudio do seu dispositivo.",
|
||||
"Upload any file from your device.": "Envie qualquer arquivo do seu dispositivo.",
|
||||
"Uploading {{name}}": "Enviando {{name}}",
|
||||
"Uploading file": "Enviando arquivo",
|
||||
@@ -341,24 +361,44 @@
|
||||
"Divider": "Divisor",
|
||||
"Quote": "Citação",
|
||||
"Image": "Imagem",
|
||||
"Audio": "Áudio",
|
||||
"Embed PDF": "Incorporar PDF",
|
||||
"Upload and embed a PDF file.": "Envie e incorpore um arquivo PDF.",
|
||||
"Embed as PDF": "Incorporar como PDF",
|
||||
"Failed to load PDF": "Falha ao carregar PDF",
|
||||
"Convert to attachment": "Converter em anexo",
|
||||
"File attachment": "Anexo de arquivo",
|
||||
"Toggle block": "Bloco colapsável",
|
||||
"Callout": "Aviso",
|
||||
"Toggle block": "Bloco recolhível",
|
||||
"Callout": "Chamada",
|
||||
"Insert callout notice.": "Insira um aviso.",
|
||||
"Math inline": "Matemática inline",
|
||||
"Math inline": "Matemática em linha",
|
||||
"Insert inline math equation.": "Insira uma equação matemática inline.",
|
||||
"Math block": "Bloco de matemática",
|
||||
"Insert math equation": "Insira uma equação matemática",
|
||||
"Math block": "Bloco matemático",
|
||||
"Insert math equation": "Inserir equação matemática",
|
||||
"Mermaid diagram": "Diagrama Mermaid",
|
||||
"Insert mermaid diagram": "Insira um diagrama Mermaid",
|
||||
"Insert and design Drawio diagrams": "Insira e projete diagramas Drawio",
|
||||
"Insert current date": "Insira a data atual",
|
||||
"Draw and sketch excalidraw diagrams": "Desenhe e esboce diagramas Excalidraw",
|
||||
"Insert mermaid diagram": "Inserir diagrama Mermaid",
|
||||
"Insert and design Drawio diagrams": "Inserir e criar diagramas Drawio",
|
||||
"Insert current date": "Inserir data atual",
|
||||
"Draw and sketch excalidraw diagrams": "Desenhar e esboçar diagramas Excalidraw",
|
||||
"Multiple": "Múltiplo",
|
||||
"Turn into": "Transformar em",
|
||||
"Text align": "Alinhar texto",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Esta página pode ter sido excluída, movida ou você pode não ter acesso a ela.",
|
||||
"Go to homepage": "Ir para a página inicial",
|
||||
"Pages you create will show up here.": "As páginas que você criar aparecerão aqui.",
|
||||
"Heading {{level}}": "Título {{level}}",
|
||||
"Toggle title": "Alternar título",
|
||||
"Write anything. Enter \"/\" for commands": "Escreva qualquer coisa. Digite \"/\" para comandos",
|
||||
"Names do not match": "Os nomes não coincidem",
|
||||
"Toggle title": "Título do bloco recolhível",
|
||||
"Write anything. Enter \"/\" for commands": "Escreva qualquer coisa. Digite \"/\" para ver os comandos",
|
||||
"Write...": "Escreva...",
|
||||
"Column count": "Número de colunas",
|
||||
"{{count}} Columns": "{{count}} colunas",
|
||||
"Equal columns": "Colunas iguais",
|
||||
"Left sidebar": "Barra lateral esquerda",
|
||||
"Right sidebar": "Barra lateral direita",
|
||||
"Wide center": "Centro largo",
|
||||
"Left wide": "Largo à esquerda",
|
||||
"Right wide": "Largo à direita",
|
||||
"Names do not match": "Os nomes não correspondem",
|
||||
"Today, {{time}}": "Hoje, {{time}}",
|
||||
"Yesterday, {{time}}": "Ontem, {{time}}",
|
||||
"Space created successfully": "Espaço criado com sucesso",
|
||||
@@ -374,44 +414,70 @@
|
||||
"Character count: {{characterCount}}": "Contagem de caracteres: {{characterCount}}",
|
||||
"New update": "Nova atualização",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} está disponível",
|
||||
"Default page edit mode": "Modo de edição de página padrão",
|
||||
"Default page edit mode": "Modo padrão de edição da página",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Escolha o modo de edição de página preferido. Evite edições acidentais.",
|
||||
"Reading": "Leitura",
|
||||
"Delete member": "Excluir membro",
|
||||
"Member deleted successfully": "Membro removido com sucesso",
|
||||
"Member deleted successfully": "Membro excluído com sucesso",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Você tem certeza que deseja deletar este membro do workspace? Esta ação é irreversível.",
|
||||
"Deactivate member": "Desativar membro",
|
||||
"Activate member": "Ativar membro",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Tem certeza de que deseja desativar este membro do espaço de trabalho? Ele não poderá mais acessar este espaço de trabalho.",
|
||||
"Are you sure you want to activate this workspace member?": "Tem certeza de que deseja ativar este membro do espaço de trabalho?",
|
||||
"Deactivate": "Desativar",
|
||||
"Activate": "Ativar",
|
||||
"Deactivated": "Desativado",
|
||||
"Move": "Mover",
|
||||
"Move page": "Mover página",
|
||||
"Move page to a different space.": "Mover página para um espaço diferente.",
|
||||
"Real-time editor connection lost. Retrying...": "Conexão do editor em tempo real perdida. Tentando novamente...",
|
||||
"Table of contents": "Tabela de conteúdos",
|
||||
"Table of contents": "Sumário",
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Adicionar títulos (H1, H2, H3) para gerar uma tabela de conteúdo.",
|
||||
"Share": "Compartilhar",
|
||||
"Public sharing": "Compartilhamento público",
|
||||
"Shared by": "Compartilhado por",
|
||||
"Shared at": "Compartilhado em",
|
||||
"Inherits public sharing from": "Herdado do compartilhamento público de",
|
||||
"Inherits public sharing from": "Herda o compartilhamento público de",
|
||||
"Share to web": "Compartilhar na web",
|
||||
"Shared to web": "Compartilhado na web",
|
||||
"Anyone with the link can view this page": "Qualquer um com o link pode ver esta página",
|
||||
"Make this page publicly accessible": "Tornar esta página publicamente acessível",
|
||||
"Include sub-pages": "Incluir sub-páginas",
|
||||
"Make sub-pages public too": "Tornar as sub-páginas públicas também",
|
||||
"Anyone with the link can view this page": "Qualquer pessoa com o link pode visualizar esta página",
|
||||
"Make this page publicly accessible": "Tornar esta página acessível publicamente",
|
||||
"Include sub-pages": "Incluir subpáginas",
|
||||
"Make sub-pages public too": "Tornar as subpáginas públicas também",
|
||||
"Allow search engines to index page": "Permitir que mecanismos de busca indexem a página",
|
||||
"Open page": "Abrir página",
|
||||
"Page": "Página",
|
||||
"Delete public share link": "Excluir o link público compartilhado",
|
||||
"Delete public share link": "Excluir link de compartilhamento público",
|
||||
"Delete share": "Excluir compartilhamento",
|
||||
"Are you sure you want to delete this shared link?": "Tem certeza de que deseja excluir este link compartilhado?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Páginas compartilhadas publicamente de espaços que você é membro aparecerão aqui",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Páginas compartilhadas publicamente dos espaços dos quais você é membro aparecerão aqui",
|
||||
"Share deleted successfully": "Compartilhamento excluído com sucesso",
|
||||
"Share not found": "Compartilhamento não encontrado",
|
||||
"Failed to share page": "Falha ao compartilhar página",
|
||||
"Failed to share page": "Falha ao compartilhar a página",
|
||||
"Disable public sharing": "Desativar compartilhamento público",
|
||||
"Prevent members from sharing pages publicly.": "Impedir que os membros compartilhem páginas publicamente.",
|
||||
"Toggle public sharing": "Alternar compartilhamento público",
|
||||
"Toggle space public sharing": "Alternar compartilhamento público do espaço",
|
||||
"Allow viewers to comment": "Permitir que os visualizadores comentem",
|
||||
"Allow viewers to add comments on pages in this space.": "Permitir que os visualizadores adicionem comentários em páginas deste espaço.",
|
||||
"Toggle viewer comments": "Ativar/desativar comentários de visualizadores",
|
||||
"Public sharing is disabled at the workspace level": "O compartilhamento público está desativado no nível do espaço de trabalho",
|
||||
"Prevent pages in this space from being shared publicly.": "Impedir que as páginas neste espaço sejam compartilhadas publicamente.",
|
||||
"Page permissions": "Permissões da página},{",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Controle quem pode visualizar e editar páginas individuais. Disponível com licença empresarial.",
|
||||
"Enable public sharing": "Ativar compartilhamento público",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Tem certeza de que deseja ativar o compartilhamento público? Os membros poderão compartilhar páginas publicamente.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Tem certeza de que deseja desativar o compartilhamento público? Todos os links compartilhados existentes neste espaço de trabalho serão excluídos.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Tem certeza de que deseja ativar o compartilhamento público para este espaço?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Tem certeza de que deseja desativar o compartilhamento público? Todos os links compartilhados existentes neste espaço serão excluídos.",
|
||||
"Public sharing is disabled": "Compartilhamento público está desativado",
|
||||
"Public sharing has been disabled at the workspace level.": "O compartilhamento público foi desativado no nível do espaço de trabalho.",
|
||||
"Public sharing has been disabled for this space.": "O compartilhamento público foi desativado para este espaço.",
|
||||
"Copy page": "Copiar página",
|
||||
"Copy page to a different space.": "Copiar página para um espaço diferente.",
|
||||
"Page copied successfully": "Página copiada com sucesso",
|
||||
"Page duplicated successfully": "Página duplicada com sucesso",
|
||||
"Find": "Encontrar",
|
||||
"Find": "Localizar",
|
||||
"Not found": "Não encontrado",
|
||||
"Previous Match (Shift+Enter)": "Correspondência anterior (Shift+Enter)",
|
||||
"Next match (Enter)": "Próxima correspondência (Enter)",
|
||||
@@ -421,15 +487,16 @@
|
||||
"Replace (Enter)": "Substituir (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Substituir tudo (Ctrl+Alt+Enter)",
|
||||
"Replace all": "Substituir tudo",
|
||||
"View all": "Ver tudo",
|
||||
"View all spaces": "Ver todos os espaços",
|
||||
"Error": "Erro",
|
||||
"Failed to disable MFA": "Falha ao desativar a MFA",
|
||||
"Disable two-factor authentication": "Desativar autenticação de dois fatores",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Desativar a autenticação de dois fatores tornará sua conta menos segura. Você só precisará de sua senha para entrar.",
|
||||
"Please enter your password to disable two-factor authentication:": "Por favor, insira sua senha para desativar a autenticação de dois fatores:",
|
||||
"Two-factor authentication has been enabled": "Autenticação de dois fatores foi ativada",
|
||||
"Two-factor authentication has been disabled": "Autenticação de dois fatores foi desativada",
|
||||
"2-step verification": "Verificação em duas etapas",
|
||||
"Two-factor authentication has been enabled": "A autenticação de dois fatores foi ativada",
|
||||
"Two-factor authentication has been disabled": "A autenticação de dois fatores foi desativada",
|
||||
"2-step verification": "Verificação em 2 etapas",
|
||||
"Protect your account with an additional verification layer when signing in.": "Proteja sua conta com uma camada adicional de verificação ao entrar.",
|
||||
"Two-factor authentication is active on your account.": "Autenticação de dois fatores está ativa na sua conta.",
|
||||
"Add 2FA method": "Adicionar método de 2FA",
|
||||
@@ -437,43 +504,43 @@
|
||||
"Disable": "Desativar",
|
||||
"Invalid verification code": "Código de verificação inválido",
|
||||
"New backup codes have been generated": "Novos códigos de backup foram gerados",
|
||||
"Failed to regenerate backup codes": "Falha ao regenerar códigos de backup",
|
||||
"About backup codes": "Sobre códigos de backup",
|
||||
"Failed to regenerate backup codes": "Falha ao regenerar os códigos de backup",
|
||||
"About backup codes": "Sobre os códigos de backup",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Códigos de backup podem ser usados para acessar sua conta se perder acesso ao aplicativo autenticador. Cada código só pode ser usado uma vez.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Você pode regenerar novos códigos de backup a qualquer momento. Isso invalidará todos os códigos existentes.",
|
||||
"Confirm password": "Confirmar senha",
|
||||
"Generate new backup codes": "Gerar novos códigos de backup",
|
||||
"Save your new backup codes": "Salvar seus novos códigos de backup",
|
||||
"Save your new backup codes": "Salve seus novos códigos de backup",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Certifique-se de salvar esses códigos em um local seguro. Seus códigos de backup antigos não são mais válidos.",
|
||||
"Your new backup codes": "Seus novos códigos de backup",
|
||||
"I've saved my backup codes": "Eu salvei meus códigos de backup",
|
||||
"I've saved my backup codes": "Salvei meus códigos de backup",
|
||||
"Failed to setup MFA": "Falha ao configurar a MFA",
|
||||
"Setup & Verify": "Configurar & Verificar",
|
||||
"Setup & Verify": "Configurar e verificar",
|
||||
"Add to authenticator": "Adicionar ao autenticador",
|
||||
"1. Scan this QR code with your authenticator app": "1. Escaneie este código QR com seu aplicativo autenticador",
|
||||
"Can't scan the code?": "Não consegue escanear o código?",
|
||||
"Enter this code manually in your authenticator app:": "Digite este código manualmente em seu aplicativo autenticador:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Digite o código de 6 dígitos do seu autenticador",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Insira o código de 6 dígitos do seu autenticador",
|
||||
"Verify and enable": "Verificar e ativar",
|
||||
"Failed to generate QR code. Please try again.": "Falha ao gerar código QR. Por favor, tente novamente.",
|
||||
"Backup": "Backup",
|
||||
"Save codes": "Salvar códigos",
|
||||
"Save your backup codes": "Salvar seus códigos de backup",
|
||||
"Save your backup codes": "Salve seus códigos de backup",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Esses códigos podem ser usados para acessar sua conta se você perder o acesso ao aplicativo autenticador. Cada código só pode ser usado uma vez.",
|
||||
"Print": "Imprimir",
|
||||
"Two-factor authentication has been set up. Please log in again.": "A autenticação de dois fatores foi configurada. Por favor, faça login novamente.",
|
||||
"Two-Factor authentication required": "Autenticação de dois fatores necessária",
|
||||
"Your workspace requires two-factor authentication for all users": "Seu espaço de trabalho requer autenticação de dois fatores para todos os usuários",
|
||||
"Two-Factor authentication required": "Autenticação de dois fatores obrigatória",
|
||||
"Your workspace requires two-factor authentication for all users": "Seu workspace exige autenticação de dois fatores para todos os usuários",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Para continuar acessando seu espaço de trabalho, você deve configurar a autenticação de dois fatores. Isso adiciona uma camada extra de segurança à sua conta.",
|
||||
"Set up two-factor authentication": "Configurar autenticação de dois fatores",
|
||||
"Cancel and logout": "Cancelar e sair",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Seu espaço de trabalho requer autenticação de dois fatores. Por favor, configure para continuar.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Isso adiciona uma camada extra de segurança à sua conta, exigindo um código de verificação de seu aplicativo autenticador.",
|
||||
"Password is required": "Senha é necessária",
|
||||
"Password is required": "A senha é obrigatória",
|
||||
"Password must be at least 8 characters": "A senha deve ter pelo menos 8 caracteres",
|
||||
"Please enter a 6-digit code": "Por favor, insira um código de 6 dígitos",
|
||||
"Please enter a 6-digit code": "Insira um código de 6 dígitos",
|
||||
"Code must be exactly 6 digits": "O código deve ter exatamente 6 dígitos",
|
||||
"Enter the 6-digit code found in your authenticator app": "Insira o código de 6 dígitos encontrado em seu aplicativo autenticador",
|
||||
"Enter the 6-digit code found in your authenticator app": "Insira o código de 6 dígitos encontrado no seu aplicativo autenticador",
|
||||
"Need help authenticating?": "Precisa de ajuda para autenticar?",
|
||||
"MFA QR Code": "Código QR de MFA",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Conta criada com sucesso. Por favor, faça login para configurar a autenticação de dois fatores.",
|
||||
@@ -481,17 +548,17 @@
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Redefinição de senha bem-sucedida. Por favor, faça login com sua nova senha para configurar a autenticação de dois fatores.",
|
||||
"Password reset was successful. Please log in with your new password.": "Redefinição de senha foi bem-sucedida. Por favor, faça login com sua nova senha.",
|
||||
"Two-factor authentication": "Autenticação de dois fatores",
|
||||
"Use authenticator app instead": "Use o aplicativo autenticador em vez disso",
|
||||
"Use authenticator app instead": "Usar aplicativo autenticador em vez disso",
|
||||
"Verify backup code": "Verificar código de backup",
|
||||
"Use backup code": "Usar código de backup",
|
||||
"Enter one of your backup codes": "Digite um de seus códigos de backup",
|
||||
"Enter one of your backup codes": "Insira um dos seus códigos de backup",
|
||||
"Backup code": "Código de backup",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Digite um de seus códigos de backup. Cada código de backup só pode ser usado uma vez.",
|
||||
"Verify": "Verificar",
|
||||
"Trash": "Lixeira",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Páginas na lixeira serão excluídas permanentemente após 30 dias.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "{count, plural, one {A página na lixeira será excluída permanentemente após # dia.} other {As páginas na lixeira serão excluídas permanentemente após # dias.}}",
|
||||
"Deleted": "Excluído",
|
||||
"No pages in trash": "Sem páginas na lixeira",
|
||||
"No pages in trash": "Nenhuma página na lixeira",
|
||||
"Permanently delete page?": "Excluir página permanentemente?",
|
||||
"Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "Tem certeza de que deseja excluir permanentemente '{{title}}'? Esta ação não pode ser desfeita.",
|
||||
"Restore '{{title}}' and its sub-pages?": "Restaurar '{{title}}' e suas subpáginas?",
|
||||
@@ -504,9 +571,9 @@
|
||||
"Deleted at": "Excluído em",
|
||||
"Preview": "Visualização",
|
||||
"Subpages": "Subpáginas",
|
||||
"Failed to load subpages": "Falha ao carregar subpáginas",
|
||||
"No subpages": "Sem subpáginas",
|
||||
"Subpages (Child pages)": "Subpáginas (Páginas filhas)",
|
||||
"Failed to load subpages": "Falha ao carregar as subpáginas",
|
||||
"No subpages": "Nenhuma subpágina",
|
||||
"Subpages (Child pages)": "Subpáginas (páginas filhas)",
|
||||
"List all subpages of the current page": "Listar todas as subpáginas da página atual",
|
||||
"Attachments": "Anexos",
|
||||
"All spaces": "Todos os espaços",
|
||||
@@ -514,28 +581,28 @@
|
||||
"Find a space": "Encontrar um espaço",
|
||||
"Search in all your spaces": "Pesquisar em todos os seus espaços",
|
||||
"Type": "Tipo",
|
||||
"Enterprise": "Empresa",
|
||||
"Enterprise": "Enterprise",
|
||||
"Download attachment": "Baixar anexo",
|
||||
"Allowed email domains": "Domínios de email permitidos",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Apenas usuários com endereços de email desses domínios podem se inscrever via SSO.",
|
||||
"Allowed email domains": "Domínios de e-mail permitidos",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Somente usuários com endereços de e-mail desses domínios podem se cadastrar via SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Insira nomes de domínio válidos separados por vírgula ou espaço",
|
||||
"Enforce two-factor authentication": "Impor autenticação de dois fatores",
|
||||
"Enforce two-factor authentication": "Exigir autenticação de dois fatores",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Uma vez imposto, todos os membros devem habilitar a autenticação de dois fatores para acessar o espaço de trabalho.",
|
||||
"Toggle MFA enforcement": "Alternar imposição de MFA",
|
||||
"Toggle MFA enforcement": "Alternar exigência de MFA",
|
||||
"Display name": "Nome de exibição",
|
||||
"Allow signup": "Permitir inscrição",
|
||||
"Enabled": "Habilitado",
|
||||
"Advanced Settings": "Configurações Avançadas",
|
||||
"Enable TLS/SSL": "Habilitar TLS/SSL",
|
||||
"Allow signup": "Permitir cadastro",
|
||||
"Enabled": "Ativado",
|
||||
"Advanced Settings": "Configurações avançadas",
|
||||
"Enable TLS/SSL": "Ativar TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Usar conexão segura com o servidor LDAP",
|
||||
"Group sync": "Sincronização de grupo",
|
||||
"Group sync": "Sincronização de grupos",
|
||||
"No SSO providers found.": "Nenhum provedor de SSO encontrado.",
|
||||
"Delete SSO provider": "Excluir provedor de SSO",
|
||||
"Are you sure you want to delete this SSO provider?": "Tem certeza de que deseja excluir este provedor de SSO?",
|
||||
"Action": "Ação",
|
||||
"{{ssoProviderType}} configuration": "Configuração de {{ssoProviderType}}",
|
||||
"Icon": "Ícone",
|
||||
"Upload image": "Fazer upload da imagem",
|
||||
"Upload image": "Enviar imagem",
|
||||
"Remove image": "Remover imagem",
|
||||
"Failed to remove image": "Falha ao remover imagem",
|
||||
"Image exceeds 10MB limit.": "A imagem excede o limite de 10MB.",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Esta ação não pode ser desfeita. Qualquer aplicação usando esta chave API deixará de funcionar.",
|
||||
"Update API key": "Atualizar chave API",
|
||||
"Manage API keys for all users in the workspace": "Gerenciar chaves API para todos os usuários no espaço de trabalho",
|
||||
"Restrict API key creation to admins": "Restringir a criação de chave de API aos administradores",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Somente administradores e proprietários podem criar novas chaves de API. As chaves de membros já existentes continuarão funcionando.",
|
||||
"Toggle restrict API keys to admins": "Alternar restrição de chaves de API para administradores",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "A criação de chaves de API foi restringida aos administradores pelo administrador do seu workspace.",
|
||||
"AI settings": "Configurações de IA",
|
||||
"AI search": "Pesquisa IA",
|
||||
"AI Answer": "Resposta de IA",
|
||||
"Ask AI": "Pergunte à IA",
|
||||
"AI is thinking...": "IA está pensando...",
|
||||
"Thinking": "Pensando",
|
||||
"Ask a question...": "Faça uma pergunta...",
|
||||
"AI-powered search (Ask AI)": "Pesquisa com IA (Pergunte à IA)",
|
||||
"AI Answers": "Respostas de IA",
|
||||
"AI-powered search (AI Answers)": "Pesquisa com IA (Respostas de IA)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "A pesquisa IA usa vetores de incorporação para fornecer capacidades de pesquisa semântica em todo o conteúdo do seu espaço de trabalho.",
|
||||
"Toggle AI search": "Alternar pesquisa de IA",
|
||||
"Generative AI (Ask AI)": "IA generativa (Perguntar à IA)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Habilitar geração de conteúdo com IA no editor. Permite aos usuários gerar, melhorar, traduzir e transformar texto.",
|
||||
"Toggle generative AI": "Alternar IA generativa",
|
||||
"Upgrade your plan": "Faça upgrade do seu plano",
|
||||
"Available with a paid license": "Disponível com uma licença paga",
|
||||
"Upgrade your license tier.": "Faça upgrade do seu nível de licença.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "A IA está disponível apenas na edição empresarial do Docmost. Contate sales@docmost.com.",
|
||||
"AI & MCP": "IA e MCP",
|
||||
"AI": "IA",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Protocolo de Contexto de Modelo (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Ative o servidor MCP para permitir que assistentes de IA e ferramentas interajam com o conteúdo do seu espaço de trabalho.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "O MCP está disponível apenas na edição empresarial do Docmost. Contate sales@docmost.com.",
|
||||
"MCP Server URL": "URL do servidor MCP",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Use sua chave de API para autenticação. Você pode gerenciar chaves de API nas configurações da sua conta.",
|
||||
"Supported tools": "Ferramentas compatíveis",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Seu espaço de trabalho tem MCP habilitado. Use sua chave de API para conectar assistentes de IA.",
|
||||
"MCP server URL:": "URL do servidor MCP:",
|
||||
"Learn more": "Saiba mais",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gerencie as chaves de API de todos os usuários do workspace. Veja a <anchor>documentação da API</anchor> para detalhes de uso.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Veja a <anchor>documentação da API</anchor> para detalhes de uso.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Veja a <anchor>documentação MCP</anchor>.",
|
||||
"Sources": "Fontes",
|
||||
"Ask AI not available for attachments": "Perguntar à IA não está disponível para anexos",
|
||||
"AI Answers not available for attachments": "Respostas de IA não disponíveis para anexos",
|
||||
"No answer available": "Nenhuma resposta disponível",
|
||||
"Background color": "Cor de fundo",
|
||||
"Highlight color": "Cor de destaque",
|
||||
"Remove color": "Remover cor"
|
||||
"Remove color": "Remover cor",
|
||||
"Notifications": "Notificações",
|
||||
"No notifications": "Sem notificações",
|
||||
"No unread notifications": "Sem notificações não lidas",
|
||||
"All notifications": "Todas as notificações",
|
||||
"Unread only": "Somente não lidas",
|
||||
"Mark all as read": "Marcar todas como lidas",
|
||||
"Mark as read": "Marcar como lida",
|
||||
"More options": "Mais opções",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> mencionou você em um comentário",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> comentou em uma página",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolveu um comentário",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> mencionou você em uma página",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> concedeu a você acesso de edição a uma página",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> concedeu a você acesso de visualização a uma página",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> atualizou uma página",
|
||||
"Watch page": "Acompanhar página",
|
||||
"Stop watching": "Parar de acompanhar",
|
||||
"Watch space": "Acompanhar espaço",
|
||||
"Stop watching space": "Parar de acompanhar espaço",
|
||||
"Email notifications": "Notificações por e-mail",
|
||||
"Page updates": "Atualizações da página",
|
||||
"Get notified when pages you watch are updated.": "Receba notificações quando as páginas que você observa forem atualizadas.",
|
||||
"Page mentions": "Menções na página",
|
||||
"Get notified when someone mentions you on a page.": "Receba notificações quando alguém mencionar você em uma página.",
|
||||
"Comment mentions": "Menções em comentários",
|
||||
"Get notified when someone mentions you in a comment.": "Receba notificações quando alguém mencionar você em um comentário.",
|
||||
"New comments": "Novos comentários",
|
||||
"Get notified about new comments on threads you participate in.": "Receba notificações sobre novos comentários nas discussões em que você participa.",
|
||||
"Resolved comments": "Comentários resolvidos",
|
||||
"Get notified when your comment is resolved.": "Receba notificações quando seu comentário for resolvido.",
|
||||
"You are now watching this page": "Agora você está observando esta página",
|
||||
"You are no longer watching this page": "Você não está mais observando esta página",
|
||||
"You are now watching this space": "Agora você está acompanhando este espaço",
|
||||
"You are no longer watching this space": "Você não está mais acompanhando este espaço",
|
||||
"Direct": "Direto",
|
||||
"Updates": "Atualizações",
|
||||
"Today": "Hoje",
|
||||
"Yesterday": "Ontem",
|
||||
"This week": "Esta semana",
|
||||
"Older": "Mais antigo",
|
||||
"Restricted page": "Página restrita",
|
||||
"Restricted pages cannot be shared publicly.": "Páginas restritas não podem ser compartilhadas publicamente.",
|
||||
"Restricted by parent": "Restrita pela página pai",
|
||||
"Restricted": "Restrito",
|
||||
"Open": "Aberto",
|
||||
"Inherits restrictions from ancestor page": "Herda restrições da página ancestral",
|
||||
"Only people listed below can access this page": "Apenas as pessoas listadas abaixo podem acessar esta página",
|
||||
"Everyone in this space can access": "Todos neste espaço podem acessar",
|
||||
"No additional restrictions on this page": "Sem restrições adicionais nesta página",
|
||||
"Only specific people can access": "Apenas pessoas específicas podem acessar",
|
||||
"Use only inherited restrictions": "Usar apenas restrições herdadas",
|
||||
"Add restrictions on top of inherited": "Adicionar restrições além das herdadas",
|
||||
"Inherited restriction": "Restrição herdada",
|
||||
"Access limited by": "Acesso limitado por",
|
||||
"Restrict access to control who can view and edit this page": "Restringir o acesso para controlar quem pode visualizar e editar esta página",
|
||||
"Add additional restrictions specific to this page": "Adicionar restrições adicionais específicas para esta página",
|
||||
"Access": "Acesso",
|
||||
"People with access": "Pessoas com acesso",
|
||||
"Remove all": "Remover tudo",
|
||||
"Remove access": "Remover acesso",
|
||||
"Remove all access": "Remover todo o acesso",
|
||||
"Are you sure you want to remove this member's access to the page?": "Tem certeza de que deseja remover o acesso deste membro à página?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Tem certeza de que deseja remover todo o acesso específico? Isso fará com que a página fique aberta para todos no espaço.",
|
||||
"Trash retention": "Retenção da lixeira",
|
||||
"Pages in trash will be permanently deleted after this period.": "As páginas na lixeira serão excluídas permanentemente após este período.",
|
||||
"Trash retention updated": "Retenção da lixeira atualizada",
|
||||
"Failed to update trash retention": "Falha ao atualizar a retenção da lixeira",
|
||||
"Removed page restriction": "Restrição de página removida",
|
||||
"Added page permission": "Permissão de página adicionada",
|
||||
"Removed page permission": "Permissão de página removida",
|
||||
"day": "dia",
|
||||
"days": "dias",
|
||||
"week": "semana",
|
||||
"weeks": "semanas",
|
||||
"month": "mês",
|
||||
"months": "meses",
|
||||
"year": "ano",
|
||||
"years": "anos",
|
||||
"Period": "Período",
|
||||
"Fixed date": "Data fixa",
|
||||
"Indefinitely": "Indefinidamente",
|
||||
"Days": "Dias",
|
||||
"Weeks": "Semanas",
|
||||
"Months": "Meses",
|
||||
"Years": "Anos",
|
||||
"Pick a date": "Escolha uma data",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "O máximo é {{max}} {{unit}} para esta unidade",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Nunca expira. Os verificadores podem verificar novamente a qualquer momento.",
|
||||
"Verified": "Verificado",
|
||||
"Review needed": "Revisão necessária",
|
||||
"Verification expired": "A verificação expirou",
|
||||
"Draft": "Rascunho",
|
||||
"In Approval": "Em aprovação",
|
||||
"In approval": "Em aprovação",
|
||||
"Approved": "Aprovado",
|
||||
"Obsolete": "Obsoleto",
|
||||
"Expiring": "Expirando",
|
||||
"Set up verification": "Configurar verificação",
|
||||
"Verify page": "Verificar página",
|
||||
"Page verification": "Verificação da página",
|
||||
"Add verification": "Adicionar verificação",
|
||||
"Edit verification": "Editar verificação",
|
||||
"Search by title": "Pesquisar por título",
|
||||
"Choose how this page should stay accurate.": "Escolha como esta página deve permanecer precisa.",
|
||||
"Recurring verification": "Verificação recorrente",
|
||||
"Verifiers re-confirm this page on a schedule.": "Os verificadores confirmam novamente esta página em uma programação definida.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Verificar novamente em uma programação definida (ex.: a cada 30 dias)",
|
||||
"Page stays editable at all times": "A página permanece editável o tempo todo",
|
||||
"Best for runbooks, FAQs, living documentation": "Ideal para runbooks, FAQs e documentação viva",
|
||||
"Approval workflow": "Fluxo de aprovação",
|
||||
"Formal document lifecycle with named approvers.": "Ciclo de vida formal do documento com aprovadores nomeados.",
|
||||
"Draft → In approval → Approved → Obsolete": "Rascunho → Em aprovação → Aprovado → Obsoleto",
|
||||
"Locked once approved, with full history": "Bloqueado após a aprovação, com histórico completo",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Desenvolvido para ISO 9001, ISO 13485 e FDA",
|
||||
"Best for SOPs and controlled documents": "Ideal para POPs e documentos controlados",
|
||||
"Back": "Voltar",
|
||||
"Quality management": "Gestão da qualidade",
|
||||
"Recurring": "Recorrente",
|
||||
"Pages move through draft, approval, and approved stages.": "As páginas passam pelos estágios de rascunho, aprovação e aprovado.",
|
||||
"Verifiers": "Verificadores",
|
||||
"Add verifier": "Adicionar verificador",
|
||||
"I've reviewed this page for accuracy": "Revisei esta página quanto à precisão",
|
||||
"Set up": "Configurar",
|
||||
"Remove verification": "Remover verificação",
|
||||
"Are you sure you want to remove verification from this page?": "Tem certeza de que deseja remover a verificação desta página?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Os verificadores atribuídos devem verificar novamente esta página periodicamente.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Verificado pela última vez por {{name}} {{time}} (expirado)",
|
||||
"The fixed expiration date has passed.": "A data fixa de expiração já passou.",
|
||||
"Verified by {{name}} {{time}}": "Verificado por {{name}} {{time}}",
|
||||
"Expires {{date}}": "Expira em {{date}}",
|
||||
"Expired {{date}}": "Expirou em {{date}}",
|
||||
"Mark as obsolete": "Marcar como obsoleto",
|
||||
"Mark obsolete": "Marcar como obsoleto",
|
||||
"Returned by {{name}} {{time}}": "Devolvido por {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Nenhuma aprovação foi solicitada ainda.",
|
||||
"Submitted by {{name}} {{time}}": "Enviado por {{name}} {{time}}",
|
||||
"Someone": "Alguém",
|
||||
"Approved by {{name}} {{time}}": "Aprovado por {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Este documento foi marcado como obsoleto.",
|
||||
"Rejection comment": "Comentário de rejeição",
|
||||
"Reason for returning this document...": "Motivo para devolver este documento...",
|
||||
"Confirm rejection": "Confirmar rejeição",
|
||||
"Submit for approval": "Enviar para aprovação",
|
||||
"Reject": "Rejeitar",
|
||||
"Approve": "Aprovar",
|
||||
"Re-submit for approval": "Reenviar para aprovação",
|
||||
"Verified until": "Verificado até",
|
||||
"QMS": "SGQ",
|
||||
"Verified pages": "Páginas verificadas",
|
||||
"Search pages...": "Pesquisar páginas...",
|
||||
"Filter by space": "Filtrar por espaço",
|
||||
"Filter by type": "Filtrar por tipo",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> verificou uma página",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> enviou uma página para sua aprovação",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> devolveu uma página para revisão",
|
||||
"Page verification expires soon": "A verificação da página expirará em breve",
|
||||
"Page verification has expired": "A verificação da página expirou",
|
||||
"Verifying your email": "Verificando seu e-mail",
|
||||
"Please wait...": "Por favor, aguarde...",
|
||||
"Verification failed. The link may have expired.": "Falha na verificação. O link pode ter expirado.",
|
||||
"Check your email": "Verifique seu e-mail",
|
||||
"We sent a verification link to {{email}}.": "Enviamos um link de verificação para {{email}}.",
|
||||
"We sent a verification link to your email.": "Enviamos um link de verificação para seu e-mail.",
|
||||
"Click the link to verify your email and access your workspace.": "Clique no link para verificar seu e-mail e acessar seu workspace.",
|
||||
"Resend verification email": "Reenviar e-mail de verificação",
|
||||
"Verification email sent. Please check your inbox.": "E-mail de verificação enviado. Por favor, verifique sua caixa de entrada.",
|
||||
"Failed to resend verification email. Please try again.": "Falha ao reenviar o e-mail de verificação. Por favor, tente novamente.",
|
||||
"We've sent you an email with your associated workspaces.": "Enviamos um e-mail para você com seus workspaces associados.",
|
||||
"Load more": "Carregar mais",
|
||||
"Log out of all devices": "Sair de todos os dispositivos",
|
||||
"Log out of all sessions except this device": "Sair de todas as sessões, exceto deste dispositivo",
|
||||
"This Device": "Este dispositivo",
|
||||
"Unknown device": "Dispositivo desconhecido",
|
||||
"No active sessions": "Nenhuma sessão ativa",
|
||||
"Session revoked": "Sessão revogada",
|
||||
"All other sessions revoked": "Todas as outras sessões foram revogadas",
|
||||
"Last used": "Último uso",
|
||||
"Created": "Criado",
|
||||
"Rename": "Renomear",
|
||||
"Publish": "Publicar",
|
||||
"Security": "Segurança",
|
||||
"Enforce SSO": "Exigir SSO",
|
||||
"Once enforced, members will not be able to login with email and password.": "Depois de exigido, os membros não poderão fazer login com e-mail e senha.",
|
||||
"AI-generated content may not be accurate.": "O conteúdo gerado por IA pode não ser preciso.",
|
||||
"AI Chat": "Chat com IA",
|
||||
"Analyze for insights": "Analisar para obter insights",
|
||||
"Ask anything...": "Pergunte qualquer coisa...",
|
||||
"Chat history": "Histórico de chats",
|
||||
"Chat name": "Nome do chat",
|
||||
"Close": "Fechar",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Falha ao carregar o chat. Ocorreu um erro.",
|
||||
"Failed to render this message.": "Falha ao renderizar esta mensagem.",
|
||||
"How can I help you today?": "Como posso ajudar você hoje?",
|
||||
"New chat": "Novo chat",
|
||||
"No chat history": "Nenhum histórico de chat",
|
||||
"No chats found": "Nenhum chat encontrado",
|
||||
"No conversations yet": "Ainda não há conversas",
|
||||
"Open full page": "Abrir página inteira",
|
||||
"Previous 7 days": "Últimos 7 dias",
|
||||
"Previous 30 days": "Últimos 30 dias",
|
||||
"Search chats...": "Pesquisar chats...",
|
||||
"Start a new chat to see it here.": "Inicie um novo chat para vê-lo aqui.",
|
||||
"Summarize this page": "Resumir esta página",
|
||||
"Toggle AI Chat": "Alternar chat com IA",
|
||||
"Translate this page": "Traduzir esta página",
|
||||
"Try a different search term.": "Tente um termo de pesquisa diferente.",
|
||||
"Try again": "Tentar novamente",
|
||||
"Untitled chat": "Chat sem título",
|
||||
"What can I help you with?": "Com o que posso ajudar você?"
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
"Add members": "Добавить участников",
|
||||
"Add to groups": "Добавить в группы",
|
||||
"Add space members": "Добавить участников пространства",
|
||||
"Add to favorites": "Добавить в избранное",
|
||||
"Admin": "Администратор",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Вы уверены, что хотите удалить эту группу? Участники потеряют доступ к материалам, к которым у этой группы есть доступ.",
|
||||
"Are you sure you want to delete this page?": "Вы уверены, что хотите удалить эту страницу?",
|
||||
"Are you sure you want to remove this user from the group? The user will lose access to resources this group has access to.": "Вы уверены, что хотите удалить этого пользователя из группы? Пользователь потеряет доступ к материалам, к которым у этой группы есть доступ.",
|
||||
"Are you sure you want to remove this user from the group? The user will lose access to resources this group has access to.": "Вы уверены, что хотите удалить этого пользователя из группы? Пользователь потеряет доступ к материалам, к которым есть доступ у этой группы.",
|
||||
"Are you sure you want to remove this user from the space? The user will lose all access to this space.": "Вы уверены, что хотите удалить этого пользователя из пространства? Пользователь потеряет весь доступ к этому пространству.",
|
||||
"Are you sure you want to restore this version? Any changes not versioned will be lost.": "Вы уверены, что хотите восстановить эту версию? Все не зафиксированные изменения будут потеряны.",
|
||||
"Can become members of groups and spaces in workspace": "Могут становиться участниками групп и пространств в рабочей области",
|
||||
@@ -46,20 +47,20 @@
|
||||
"Details": "Подробности",
|
||||
"e.g ACME": "например, ACME",
|
||||
"e.g ACME Inc": "например, ACME Inc",
|
||||
"e.g Developers": "например, Разработчики",
|
||||
"e.g Developers": "например, Developers",
|
||||
"e.g Group for developers": "например, Группа для разработчиков",
|
||||
"e.g product": "например, продукт",
|
||||
"e.g Product Team": "например, Продуктовая команда",
|
||||
"e.g Sales": "например, Продажи",
|
||||
"e.g Space for product team": "например, Пространство для продуктовой команды",
|
||||
"e.g product": "например, product",
|
||||
"e.g Product Team": "например, Команда продукта",
|
||||
"e.g Sales": "например, Sales",
|
||||
"e.g Space for product team": "например, Пространство для команды продукта",
|
||||
"e.g Space for sales team to collaborate": "например, Пространство для совместной работы команды продаж",
|
||||
"Edit": "Редактировать",
|
||||
"Read": "Читать",
|
||||
"Read": "Чтение",
|
||||
"Edit group": "Редактировать группу",
|
||||
"Email": "Электронная почта",
|
||||
"Enter a strong password": "Введите надёжный пароль",
|
||||
"Enter valid email addresses separated by comma or space max_50": "Введите действительные адреса электронной почты, разделенные запятой или пробелом [макс: 50]",
|
||||
"enter valid emails addresses": "введите действительные адреса электронной почты",
|
||||
"enter valid emails addresses": "введите корректные адреса электронной почты",
|
||||
"Enter your current password": "Введите ваш текущий пароль",
|
||||
"enter your full name": "введите ваше полное имя",
|
||||
"Enter your new password": "Введите ваш новый пароль",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Не удалось импортировать страницы",
|
||||
"Failed to load page. An error occurred.": "Не удалось загрузить страницу. Произошла ошибка.",
|
||||
"Failed to update data": "Не удалось обновить данные",
|
||||
"Favorite spaces": "Избранные пространства",
|
||||
"Favorite spaces appear here": "Здесь отображаются избранные пространства",
|
||||
"Favorites": "Избранное",
|
||||
"Full access": "Полный доступ",
|
||||
"Full page width": "Ширина на всю страницу",
|
||||
"Full width": "Во всю ширину",
|
||||
@@ -87,11 +91,12 @@
|
||||
"Import pages": "Импорт страниц",
|
||||
"Import pages & space settings": "Импорт страниц и настройки пространства",
|
||||
"Importing pages": "Импортирование страниц",
|
||||
"invalid invitation link": "ссылка на приглашение недействительна",
|
||||
"invalid invitation link": "недействительная ссылка-приглашение",
|
||||
"Invitation signup": "Регистрация по приглашению",
|
||||
"Invite by email": "Пригласить по электронной почте",
|
||||
"Invite members": "Пригласить участников",
|
||||
"Invite new members": "Пригласить новых участников",
|
||||
"Invite People": "Пригласить людей",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Приглашённые участники, которые ещё не приняли приглашение, появятся здесь.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Приглашённые участники получат доступ к пространствам, доступ к которым есть у группы",
|
||||
"Join the workspace": "Присоединиться к рабочей области",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "Группа не найдена",
|
||||
"No page history saved yet.": "История страниц ещё не сохранена.",
|
||||
"No pages yet": "Страниц пока нет",
|
||||
"No shared pages": "Нет общих страниц",
|
||||
"No results found...": "Результаты не найдены...",
|
||||
"No user found": "Пользователь не найден",
|
||||
"Overview": "Обзор",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "страницы",
|
||||
"Password": "Пароль",
|
||||
"Password changed successfully": "Пароль успешно изменён",
|
||||
"People": "Люди",
|
||||
"Pending": "В ожидании",
|
||||
"Please confirm your action": "Пожалуйста, подтвердите ваше действие",
|
||||
"Preferences": "Настройки",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Профиль",
|
||||
"Recently updated": "Обновлено недавно",
|
||||
"Remove": "Удалить",
|
||||
"Remove from favorites": "Удалить из избранного",
|
||||
"Remove group member": "Удалить участника группы",
|
||||
"Remove space member": "Удалить участника пространства",
|
||||
"Restore": "Восстановить",
|
||||
@@ -149,49 +157,50 @@
|
||||
"Search...": "Поиск...",
|
||||
"Select language": "Выберите язык",
|
||||
"Select role": "Выберите роль",
|
||||
"Select role to assign to all invited members": "Выберите роль для всех приглашённых участников",
|
||||
"Select role to assign to all invited members": "Выберите роль, которую нужно назначить всем приглашённым участникам",
|
||||
"Select theme": "Выберите тему",
|
||||
"Send invitation": "Отправить приглашение",
|
||||
"Invitation sent": "Приглашение отправлено",
|
||||
"Settings": "Настройки",
|
||||
"Setup workspace": "Настроить рабочую область",
|
||||
"Sign In": "Вход",
|
||||
"Sign Up": "Регистрация",
|
||||
"Slug": "Slug",
|
||||
"Setup workspace": "Настроить рабочее пространство",
|
||||
"Sign In": "Войти",
|
||||
"Sign Up": "Зарегистрироваться",
|
||||
"Slug": "Слаг",
|
||||
"Space": "Пространство",
|
||||
"Space description": "Описание пространства",
|
||||
"Space menu": "Меню пространства",
|
||||
"Space name": "Название пространства",
|
||||
"Space settings": "Настройки пространства",
|
||||
"Space slug": "Slug пространства",
|
||||
"Space slug": "Слаг пространства",
|
||||
"Spaces": "Пространства",
|
||||
"Spaces you belong to": "Пространства, к которым вы принадлежите",
|
||||
"No space found": "Пространства не найдены",
|
||||
"Spaces you belong to": "Пространства, в которых вы состоите",
|
||||
"No space found": "Пространство не найдено",
|
||||
"Search for spaces": "Поиск пространств",
|
||||
"Start typing to search...": "Начните вводить для поиска...",
|
||||
"Status": "Статус",
|
||||
"Successfully imported": "Успешно импортировано",
|
||||
"Successfully restored": "Успешно восстановлено",
|
||||
"System settings": "Системные настройки",
|
||||
"Templates": "Шаблоны",
|
||||
"Theme": "Тема",
|
||||
"To change your email, you have to enter your password and new email.": "Чтобы изменить электронную почту, вам нужно ввести пароль и новый адрес.",
|
||||
"Toggle full page width": "Переключить ширину на всю страницу",
|
||||
"Toggle full page width": "Переключить полную ширину страницы",
|
||||
"Unable to import pages. Please try again.": "Не удалось импортировать страницы. Пожалуйста, попробуйте ещё раз.",
|
||||
"untitled": "без названия",
|
||||
"Untitled": "Без названия",
|
||||
"Updated successfully": "Обновлено успешно",
|
||||
"Updated successfully": "Успешно обновлено",
|
||||
"User": "Пользователь",
|
||||
"Workspace": "Рабочая область",
|
||||
"Workspace Name": "Имя рабочей области",
|
||||
"Workspace settings": "Настройки рабочей области",
|
||||
"Workspace": "Рабочее пространство",
|
||||
"Workspace Name": "Название рабочего пространства",
|
||||
"Workspace settings": "Настройки рабочего пространства",
|
||||
"You can change your password here.": "Вы можете изменить свой пароль здесь.",
|
||||
"Your Email": "Ваш адрес электронной почты",
|
||||
"Your import is complete.": "Ваш импорт завершен.",
|
||||
"Your name": "Ваше имя",
|
||||
"Your Name": "Ваше Имя",
|
||||
"Your Name": "Ваше имя",
|
||||
"Your password": "Ваш пароль",
|
||||
"Your password must be a minimum of 8 characters.": "Ваш пароль должен содержать минимум 8 символов.",
|
||||
"Sidebar toggle": "Переключить боковую панель",
|
||||
"Sidebar toggle": "Переключатель боковой панели",
|
||||
"Comments": "Комментарии",
|
||||
"404 page not found": "404 страница не найдена",
|
||||
"Sorry, we can't find the page you are looking for.": "К сожалению, мы не можем найти страницу, которую вы ищете.",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Ответить...",
|
||||
"Error loading comments.": "Ошибка при загрузке комментариев.",
|
||||
"No comments yet.": "Комментариев пока нет.",
|
||||
"No open comments.": "Нет открытых комментариев.",
|
||||
"No resolved comments.": "Нет решённых комментариев.",
|
||||
"Add a comment...": "Добавить комментарий...",
|
||||
"Edit comment": "Редактировать комментарий",
|
||||
"Delete comment": "Удалить комментарий",
|
||||
"Are you sure you want to delete this comment?": "Вы уверены, что хотите удалить этот комментарий?",
|
||||
"Delete chat": "Удалить чат",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Вы уверены, что хотите удалить '{{title}}'? Это действие нельзя отменить.",
|
||||
"Comment created successfully": "Комментарий успешно создан",
|
||||
"Error creating comment": "Ошибка при создании комментария",
|
||||
"Comment updated successfully": "Комментарий успешно обновлён",
|
||||
@@ -217,18 +231,17 @@
|
||||
"Comment deleted successfully": "Комментарий успешно удалён",
|
||||
"Failed to delete comment": "Не удалось удалить комментарий",
|
||||
"Comment resolved successfully": "Комментарий успешно разрешён",
|
||||
"Comment re-opened successfully": "Комментарий успешно открыт заново",
|
||||
"Comment unresolved successfully": "Комментарий успешно размечен как нерешённый",
|
||||
"Comment re-opened successfully": "Комментарий успешно открыт повторно",
|
||||
"Comment unresolved successfully": "Комментарий успешно переведён в нерешённые",
|
||||
"Failed to resolve comment": "Не удалось разрешить комментарий",
|
||||
"Resolve comment": "Разрешить комментарий",
|
||||
"Unresolve comment": "Отметить комментарий как нерешённый",
|
||||
"Resolve Comment Thread": "Закрыть цепочку комментариев",
|
||||
"Unresolve Comment Thread": "Отметить цепочку комментариев как нерешённую",
|
||||
"Resolve comment": "Решить комментарий",
|
||||
"Unresolve comment": "Снять статус решённого с комментария",
|
||||
"Resolve Comment Thread": "Решить ветку комментариев",
|
||||
"Unresolve Comment Thread": "Снять статус решённой с ветки комментариев",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Вы уверены, что хотите закрыть эту цепочку комментариев? Это пометит её как завершённую.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Вы уверены, что хотите отметить эту цепочку комментариев как нерешённую?",
|
||||
"Resolved": "Решено",
|
||||
"No active comments.": "Нет активных комментариев.",
|
||||
"No resolved comments.": "Нет решённых комментариев.",
|
||||
"Revoke invitation": "Отозвать приглашение",
|
||||
"Revoke": "Отозвать",
|
||||
"Don't": "Нет",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Добавить строку ниже",
|
||||
"Delete table": "Удалить таблицу",
|
||||
"Info": "Информация",
|
||||
"Note": "Примечание",
|
||||
"Success": "Успешно",
|
||||
"Warning": "Предупреждение",
|
||||
"Danger": "Важно",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Сохранить и выйти",
|
||||
"Double-click to edit Excalidraw diagram": "Кликните дважды для редактирования диаграммы Excalidraw",
|
||||
"Paste link": "Вставить ссылку",
|
||||
"Paste link or search pages": "Вставьте ссылку или найдите страницы",
|
||||
"Link to web page": "Ссылка на веб-страницу",
|
||||
"Recents": "Недавние",
|
||||
"Page or URL": "Страница или URL",
|
||||
"Link title": "Заголовок ссылки",
|
||||
"Edit link": "Редактировать ссылку",
|
||||
"Remove link": "Удалить ссылку",
|
||||
"Add link": "Добавить ссылку",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Розовый",
|
||||
"Gray": "Серый",
|
||||
"Embed link": "Встроенная ссылка",
|
||||
"Invalid {{provider}} embed link": "Неверная ссылка для встраивания {{provider}}",
|
||||
"Invalid {{provider}} embed link": "Недействительная ссылка для встраивания {{provider}}",
|
||||
"Embed {{provider}}": "Встроить {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Введите ссылку для встраивания {{provider}}",
|
||||
"Bold": "Жирный",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Вставить горизонтальный разделитель",
|
||||
"Upload any image from your device.": "Загрузить любое изображение с вашего устройства.",
|
||||
"Upload any video from your device.": "Загрузить любое видео с вашего устройства.",
|
||||
"Upload any audio from your device.": "Загрузите любой аудиофайл с вашего устройства.",
|
||||
"Upload any file from your device.": "Загрузить любой файл с вашего устройства.",
|
||||
"Uploading {{name}}": "Загрузка {{name}}",
|
||||
"Uploading file": "Загрузка файла",
|
||||
@@ -341,23 +361,43 @@
|
||||
"Divider": "Разделитель",
|
||||
"Quote": "Цитата",
|
||||
"Image": "Изображение",
|
||||
"File attachment": "Прикрепленный файл",
|
||||
"Audio": "Аудио",
|
||||
"Embed PDF": "Встроить PDF",
|
||||
"Upload and embed a PDF file.": "Загрузите и встроите PDF-файл.",
|
||||
"Embed as PDF": "Встроить как PDF",
|
||||
"Failed to load PDF": "Не удалось загрузить PDF",
|
||||
"Convert to attachment": "Преобразовать в вложение",
|
||||
"File attachment": "Вложение файла",
|
||||
"Toggle block": "Сворачиваемый блок",
|
||||
"Callout": "Выноска",
|
||||
"Insert callout notice.": "Вставить выноску с сообщением.",
|
||||
"Math inline": "Формула",
|
||||
"Math inline": "Строчная формула",
|
||||
"Insert inline math equation.": "Вставить математическое выражение в строку.",
|
||||
"Math block": "Блок формул",
|
||||
"Insert math equation": "Вставить математическое выражение",
|
||||
"Math block": "Блок формулы",
|
||||
"Insert math equation": "Вставить математическую формулу",
|
||||
"Mermaid diagram": "Диаграмма Mermaid",
|
||||
"Insert mermaid diagram": "Вставить диаграмму Mermaid",
|
||||
"Insert and design Drawio diagrams": "Вставить и рисовать диаграммы Draw.io",
|
||||
"Insert and design Drawio diagrams": "Вставляйте и редактируйте диаграммы Drawio",
|
||||
"Insert current date": "Вставить текущую дату",
|
||||
"Draw and sketch excalidraw diagrams": "Вставить и рисовать диаграммы Excalidraw",
|
||||
"Draw and sketch excalidraw diagrams": "Рисуйте и создавайте диаграммы Excalidraw",
|
||||
"Multiple": "Несколько",
|
||||
"Turn into": "Преобразовать в",
|
||||
"Text align": "Выравнивание текста",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Эта страница могла быть удалена, перемещена, или у вас может отсутствовать доступ к ней.",
|
||||
"Go to homepage": "Вернуться на главную",
|
||||
"Pages you create will show up here.": "Созданные вами страницы появятся здесь.",
|
||||
"Heading {{level}}": "Заголовок {{level}}",
|
||||
"Toggle title": "Переключить заголовок",
|
||||
"Write anything. Enter \"/\" for commands": "Начните писать. Введите \"/\" для списка команд",
|
||||
"Toggle title": "Заголовок сворачиваемого блока",
|
||||
"Write anything. Enter \"/\" for commands": "Пишите что угодно. Введите \"/\" для команд",
|
||||
"Write...": "Напишите...",
|
||||
"Column count": "Количество столбцов",
|
||||
"{{count}} Columns": "{count, plural, one{# столбец} few{# столбца} many{# столбцов} other{# столбца}}",
|
||||
"Equal columns": "Равные столбцы",
|
||||
"Left sidebar": "Левая боковая панель",
|
||||
"Right sidebar": "Правая боковая панель",
|
||||
"Wide center": "Широкий по центру",
|
||||
"Left wide": "Широкий слева",
|
||||
"Right wide": "Широкий справа",
|
||||
"Names do not match": "Названия не совпадают",
|
||||
"Today, {{time}}": "Сегодня, {{time}}",
|
||||
"Yesterday, {{time}}": "Вчера, {{time}}",
|
||||
@@ -365,48 +405,74 @@
|
||||
"Space updated successfully": "Пространство успешно обновлено",
|
||||
"Space deleted successfully": "Пространство успешно удалено",
|
||||
"Members added successfully": "Участники успешно добавлены",
|
||||
"Member removed successfully": "Участник успешно удален",
|
||||
"Member removed successfully": "Участник успешно удалён",
|
||||
"Member role updated successfully": "Роль участника успешно обновлена",
|
||||
"Created by: <b>{{creatorName}}</b>": "Автор: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Дата создания: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Изменено {{name}} {{time}}",
|
||||
"Created by: <b>{{creatorName}}</b>": "Создано: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Создано: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Изменено: {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Количество слов: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Количество символов: {{characterCount}}",
|
||||
"New update": "Новое обновление",
|
||||
"{{latestVersion}} is available": "Доступна новая версия {{latestVersion}}",
|
||||
"{{latestVersion}} is available": "Доступна версия {{latestVersion}}",
|
||||
"Default page edit mode": "Режим редактирования страницы по умолчанию",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Выберите предпочитаемый режим редактирования страницы. Избегайте случайных изменений.",
|
||||
"Reading": "Чтение",
|
||||
"Delete member": "Удалить участника",
|
||||
"Member deleted successfully": "Участник успешно удален",
|
||||
"Member deleted successfully": "Участник успешно удалён",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Вы уверены, что хотите удалить этого участника рабочей области? Это действие необратимо.",
|
||||
"Deactivate member": "Деактивировать участника",
|
||||
"Activate member": "Активировать участника",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Вы уверены, что хотите деактивировать этого участника рабочего пространства? Они больше не смогут получить доступ к этому рабочему пространству.",
|
||||
"Are you sure you want to activate this workspace member?": "Вы уверены, что хотите активировать этого участника рабочего пространства?",
|
||||
"Deactivate": "Деактивировать",
|
||||
"Activate": "Активировать",
|
||||
"Deactivated": "Деактивирован",
|
||||
"Move": "Переместить",
|
||||
"Move page": "Переместить страницу",
|
||||
"Move page to a different space.": "Переместите страницу в другое пространство.",
|
||||
"Real-time editor connection lost. Retrying...": "Соединение с редактором в реальном времени потеряно. Повторная попытка...",
|
||||
"Table of contents": "Содержание",
|
||||
"Table of contents": "Оглавление",
|
||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Добавьте заголовки (H1, H2, H3), чтобы создать оглавление.",
|
||||
"Share": "Поделиться",
|
||||
"Public sharing": "Общий доступ",
|
||||
"Public sharing": "Публичный доступ",
|
||||
"Shared by": "Поделился",
|
||||
"Shared at": "Поделился в",
|
||||
"Inherits public sharing from": "Наследует общий доступ от",
|
||||
"Share to web": "Поделиться в интернете",
|
||||
"Shared to web": "Размещено в интернете",
|
||||
"Anyone with the link can view this page": "Любой, у кого есть ссылка, может просмотреть эту страницу",
|
||||
"Shared at": "Дата публикации",
|
||||
"Inherits public sharing from": "Наследует публичный доступ от",
|
||||
"Share to web": "Опубликовать в интернете",
|
||||
"Shared to web": "Опубликовано в интернете",
|
||||
"Anyone with the link can view this page": "Любой, у кого есть ссылка, может просматривать эту страницу",
|
||||
"Make this page publicly accessible": "Сделать эту страницу общедоступной",
|
||||
"Include sub-pages": "Включить подстраницы",
|
||||
"Make sub-pages public too": "Сделать подстраницы также общедоступными",
|
||||
"Make sub-pages public too": "Сделать подстраницы тоже общедоступными",
|
||||
"Allow search engines to index page": "Разрешить поисковым системам индексировать страницу",
|
||||
"Open page": "Открыть страницу",
|
||||
"Page": "Страница",
|
||||
"Delete public share link": "Удалить ссылку на общий доступ",
|
||||
"Delete public share link": "Удалить публичную ссылку",
|
||||
"Delete share": "Удалить общий доступ",
|
||||
"Are you sure you want to delete this shared link?": "Вы уверены, что хотите удалить эту ссылку общего доступа?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Общие страницы из пространств, участником которых вы являетесь, появятся здесь",
|
||||
"Share deleted successfully": "Общий доступ успешно удален",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Здесь будут отображаться публично опубликованные страницы из пространств, участником которых вы являетесь",
|
||||
"Share deleted successfully": "Общий доступ успешно удалён",
|
||||
"Share not found": "Общий доступ не найден",
|
||||
"Failed to share page": "Не удалось поделиться страницей",
|
||||
"Failed to share page": "Не удалось предоставить доступ к странице",
|
||||
"Disable public sharing": "Отключить общий доступ",
|
||||
"Prevent members from sharing pages publicly.": "Запретить участникам делиться страницами публично.",
|
||||
"Toggle public sharing": "Переключить общий доступ",
|
||||
"Toggle space public sharing": "Переключить общий доступ для пространства",
|
||||
"Allow viewers to comment": "Разрешить зрителям комментировать",
|
||||
"Allow viewers to add comments on pages in this space.": "Разрешить зрителям добавлять комментарии на страницах в этом пространстве.",
|
||||
"Toggle viewer comments": "Переключить комментарии зрителей",
|
||||
"Public sharing is disabled at the workspace level": "Общий доступ отключен на уровне рабочего пространства",
|
||||
"Prevent pages in this space from being shared publicly.": "Запретить делиться страницами в этом пространстве публично.",
|
||||
"Page permissions": "Права доступа к странице},{",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Контролируйте, кто может просматривать и редактировать отдельные страницы. Доступно при наличии лицензии Enterprise.",
|
||||
"Enable public sharing": "Включить общий доступ",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Вы уверены, что хотите включить общий доступ? Участники смогут делиться страницами публично.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Вы уверены, что хотите отключить общий доступ? Все существующие ссылки в этом рабочем пространстве будут удалены.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Вы уверены, что хотите включить общий доступ для этого пространства?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Вы уверены, что хотите отключить общий доступ? Все существующие ссылки в этом пространстве будут удалены.",
|
||||
"Public sharing is disabled": "Общий доступ отключен",
|
||||
"Public sharing has been disabled at the workspace level.": "Общий доступ был отключен на уровне рабочего пространства.",
|
||||
"Public sharing has been disabled for this space.": "Общий доступ был отключен для этого пространства.",
|
||||
"Copy page": "Копировать страницу",
|
||||
"Copy page to a different space.": "Копировать страницу в другое пространство.",
|
||||
"Page copied successfully": "Страница успешно скопирована",
|
||||
@@ -421,9 +487,10 @@
|
||||
"Replace (Enter)": "Заменить (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Заменить все (Ctrl+Alt+Enter)",
|
||||
"Replace all": "Заменить все",
|
||||
"View all": "Просмотреть все",
|
||||
"View all spaces": "Просмотреть все пространства",
|
||||
"Error": "Ошибка",
|
||||
"Failed to disable MFA": "Не удалось отключить двухфакторную аутентификацию",
|
||||
"Failed to disable MFA": "Не удалось отключить MFA",
|
||||
"Disable two-factor authentication": "Отключить двухфакторную аутентификацию",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Отключение двухфакторной аутентификации сделает вашу учетную запись менее безопасной. Для входа потребуется только пароль.",
|
||||
"Please enter your password to disable two-factor authentication:": "Пожалуйста, введите ваш пароль, чтобы отключить двухфакторную аутентификацию:",
|
||||
@@ -435,61 +502,61 @@
|
||||
"Add 2FA method": "Добавить метод 2FA",
|
||||
"Backup codes": "Резервные коды",
|
||||
"Disable": "Отключить",
|
||||
"Invalid verification code": "Неверный код проверки",
|
||||
"New backup codes have been generated": "Созданы новые резервные коды",
|
||||
"Failed to regenerate backup codes": "Не удалось создать новые резервные коды",
|
||||
"Invalid verification code": "Недействительный код подтверждения",
|
||||
"New backup codes have been generated": "Новые резервные коды сгенерированы",
|
||||
"Failed to regenerate backup codes": "Не удалось заново сгенерировать резервные коды",
|
||||
"About backup codes": "О резервных кодах",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Резервные коды можно использовать для доступа к вашей учетной записи, если вы потеряли доступ к приложению-аутентификатору. Каждый код можно использовать только один раз.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Вы можете создать новые резервные коды в любое время. Это аннулирует все существующие коды.",
|
||||
"Confirm password": "Подтвердите пароль",
|
||||
"Generate new backup codes": "Создать новые резервные коды",
|
||||
"Generate new backup codes": "Сгенерировать новые резервные коды",
|
||||
"Save your new backup codes": "Сохраните ваши новые резервные коды",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Убедитесь, что сохранили эти коды в безопасном месте. Ваши старые резервные коды больше недействительны.",
|
||||
"Your new backup codes": "Ваши новые резервные коды",
|
||||
"I've saved my backup codes": "Я сохранил(а) свои резервные коды",
|
||||
"Failed to setup MFA": "Не удалось настроить многофакторную аутентификацию",
|
||||
"Setup & Verify": "Настроить и проверить",
|
||||
"Add to authenticator": "Добавить в аутентификатор",
|
||||
"1. Scan this QR code with your authenticator app": "1. Отсканируйте этот QR-код с помощью вашего приложения-аутентификатора",
|
||||
"I've saved my backup codes": "Я сохранил резервные коды",
|
||||
"Failed to setup MFA": "Не удалось настроить MFA",
|
||||
"Setup & Verify": "Настроить и подтвердить",
|
||||
"Add to authenticator": "Добавить в приложение-аутентификатор",
|
||||
"1. Scan this QR code with your authenticator app": "1. Отсканируйте этот QR-код с помощью приложения-аутентификатора",
|
||||
"Can't scan the code?": "Не удается сканировать код?",
|
||||
"Enter this code manually in your authenticator app:": "Введите этот код вручную в приложении-аутентификаторе:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Введите 6-значный код из вашего аутентификатора",
|
||||
"Verify and enable": "Проверить и включить",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Введите 6-значный код из приложения-аутентификатора",
|
||||
"Verify and enable": "Подтвердить и включить",
|
||||
"Failed to generate QR code. Please try again.": "Не удалось создать QR-код. Пожалуйста, попробуйте снова.",
|
||||
"Backup": "Резервное копирование",
|
||||
"Backup": "Резервный",
|
||||
"Save codes": "Сохранить коды",
|
||||
"Save your backup codes": "Сохраните ваши резервные коды",
|
||||
"Save your backup codes": "Сохраните резервные коды",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Эти коды можно использовать для доступа к вашей учетной записи, если вы потеряли доступ к приложению-аутентификатору. Каждый код можно использовать только один раз.",
|
||||
"Print": "Печать",
|
||||
"Two-factor authentication has been set up. Please log in again.": "Двухфакторная аутентификация настроена. Пожалуйста, войдите снова.",
|
||||
"Two-Factor authentication required": "Требуется двухфакторная аутентификация",
|
||||
"Your workspace requires two-factor authentication for all users": "Ваше рабочее пространство требует двухфакторной аутентификации для всех пользователей",
|
||||
"Your workspace requires two-factor authentication for all users": "Ваше рабочее пространство требует двухфакторную аутентификацию для всех пользователей",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Чтобы продолжать доступ к вашему рабочему пространству, вы должны настроить двухфакторную аутентификацию. Это добавляет дополнительный уровень безопасности к вашей учетной записи.",
|
||||
"Set up two-factor authentication": "Настройте двухфакторную аутентификацию",
|
||||
"Set up two-factor authentication": "Настроить двухфакторную аутентификацию",
|
||||
"Cancel and logout": "Отменить и выйти",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Ваше рабочее пространство требует двухфакторной аутентификации. Пожалуйста, настройте её, чтобы продолжить.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Это добавляет дополнительный уровень безопасности к вашей учетной записи, требуя код проверки из вашего приложения-аутентификатора.",
|
||||
"Password is required": "Требуется пароль",
|
||||
"Password must be at least 8 characters": "Пароль должен содержать как минимум 8 символов",
|
||||
"Password must be at least 8 characters": "Пароль должен содержать не менее 8 символов",
|
||||
"Please enter a 6-digit code": "Пожалуйста, введите 6-значный код",
|
||||
"Code must be exactly 6 digits": "Код должен содержать ровно 6 цифр",
|
||||
"Code must be exactly 6 digits": "Код должен состоять ровно из 6 цифр",
|
||||
"Enter the 6-digit code found in your authenticator app": "Введите 6-значный код из вашего приложения-аутентификатора",
|
||||
"Need help authenticating?": "Нужна помощь с аутентификацией?",
|
||||
"MFA QR Code": "QR-код двухфакторной аутентификации",
|
||||
"MFA QR Code": "QR-код MFA",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Учетная запись успешно создана. Пожалуйста, войдите, чтобы настроить двухфакторную аутентификацию.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "Сброс пароля выполнен успешно. Пожалуйста, войдите с вашим новым паролем и завершите настройку двухфакторной аутентификации.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Сброс пароля выполнен успешно. Пожалуйста, войдите с вашим новым паролем, чтобы настроить двухфакторную аутентификацию.",
|
||||
"Password reset was successful. Please log in with your new password.": "Сброс пароля выполнен успешно. Пожалуйста, войдите с вашим новым паролем.",
|
||||
"Two-factor authentication": "Двухфакторная аутентификация",
|
||||
"Use authenticator app instead": "Используйте приложение-аутентификатор вместо этого",
|
||||
"Verify backup code": "Проверка резервного кода",
|
||||
"Use authenticator app instead": "Использовать приложение-аутентификатор вместо этого",
|
||||
"Verify backup code": "Подтвердить резервный код",
|
||||
"Use backup code": "Использовать резервный код",
|
||||
"Enter one of your backup codes": "Введите один из ваших резервных кодов",
|
||||
"Backup code": "Резервный код",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Введите один из ваших резервных кодов. Каждый резервный код можно использовать только один раз.",
|
||||
"Verify": "Проверить",
|
||||
"Verify": "Подтвердить",
|
||||
"Trash": "Корзина",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Страницы в корзине будут окончательно удалены через 30 дней.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "{count, plural, one {Страница в корзине будет окончательно удалена через # день.} few {Страницы в корзине будут окончательно удалены через # дня.} many {Страницы в корзине будут окончательно удалены через # дней.} other {Страницы в корзине будут окончательно удалены через # дней.}}",
|
||||
"Deleted": "Удалено",
|
||||
"No pages in trash": "В корзине нет страниц",
|
||||
"Permanently delete page?": "Удалить страницу окончательно?",
|
||||
@@ -501,24 +568,24 @@
|
||||
"Page moved to trash": "Страница перемещена в корзину",
|
||||
"Page restored successfully": "Страница успешно восстановлена",
|
||||
"Deleted by": "Удалено пользователем",
|
||||
"Deleted at": "Удалено в",
|
||||
"Deleted at": "Удалено",
|
||||
"Preview": "Предпросмотр",
|
||||
"Subpages": "Подстраницы",
|
||||
"Failed to load subpages": "Не удалось загрузить под страницы",
|
||||
"Failed to load subpages": "Не удалось загрузить подстраницы",
|
||||
"No subpages": "Нет подстраниц",
|
||||
"Subpages (Child pages)": "Подстраницы (вложенные страницы)",
|
||||
"List all subpages of the current page": "Показать все под страницы",
|
||||
"Subpages (Child pages)": "Подстраницы (дочерние страницы)",
|
||||
"List all subpages of the current page": "Показать все подстраницы текущей страницы",
|
||||
"Attachments": "Вложения",
|
||||
"All spaces": "Все пространства",
|
||||
"Unknown": "Неизвестно",
|
||||
"Find a space": "Найти пространство",
|
||||
"Search in all your spaces": "Поиск во всех ваших пространствах",
|
||||
"Search in all your spaces": "Искать во всех ваших пространствах",
|
||||
"Type": "Тип",
|
||||
"Enterprise": "Предприятие",
|
||||
"Enterprise": "Корпоративный",
|
||||
"Download attachment": "Скачать вложение",
|
||||
"Allowed email domains": "Разрешенные домены электронной почты",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Только пользователи с электронными адресами из этих доменов могут зарегистрироваться через SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Введите допустимые доменные имена, разделённые запятыми или пробелами",
|
||||
"Allowed email domains": "Разрешённые домены электронной почты",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Только пользователи с адресами электронной почты из этих доменов могут зарегистрироваться через SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Введите корректные доменные имена, разделённые запятой или пробелом",
|
||||
"Enforce two-factor authentication": "Обязательная двухфакторная аутентификация",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "После введения обязательности все участники должны будут включить двухфакторную аутентификацию для доступа к рабочему пространству.",
|
||||
"Toggle MFA enforcement": "Переключить обязательность MFA",
|
||||
@@ -527,13 +594,13 @@
|
||||
"Enabled": "Включено",
|
||||
"Advanced Settings": "Расширенные настройки",
|
||||
"Enable TLS/SSL": "Включить TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Использовать защищённое соединение с сервером LDAP",
|
||||
"Group sync": "Синхронизация группы",
|
||||
"Use secure connection to LDAP server": "Использовать защищённое подключение к серверу LDAP",
|
||||
"Group sync": "Синхронизация групп",
|
||||
"No SSO providers found.": "Поставщики SSO не найдены.",
|
||||
"Delete SSO provider": "Удалить поставщика SSO",
|
||||
"Delete SSO provider": "Удалить провайдера SSO",
|
||||
"Are you sure you want to delete this SSO provider?": "Вы уверены, что хотите удалить этого поставщика SSO?",
|
||||
"Action": "Действие",
|
||||
"{{ssoProviderType}} configuration": "Настройка {{ssoProviderType}}",
|
||||
"{{ssoProviderType}} configuration": "Конфигурация {{ssoProviderType}}",
|
||||
"Icon": "Иконка",
|
||||
"Upload image": "Загрузить изображение",
|
||||
"Remove image": "Удалить изображение",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Это действие необратимо. Любые приложения, использующие этот API ключ, перестанут работать.",
|
||||
"Update API key": "Обновить API ключ",
|
||||
"Manage API keys for all users in the workspace": "Управлять API ключами для всех пользователей в рабочей области",
|
||||
"Restrict API key creation to admins": "Ограничить создание API-ключей только администраторами.",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Только администраторы и владельцы могут создавать новые API-ключи. Существующие ключи участников продолжат работать.",
|
||||
"Toggle restrict API keys to admins": "Переключить ограничение создания API-ключей только для администраторов",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "Создание API-ключей ограничено администраторами вашего рабочего пространства.",
|
||||
"AI settings": "Настройки ИИ",
|
||||
"AI search": "Поиск ИИ",
|
||||
"AI Answer": "Ответ ИИ",
|
||||
"Ask AI": "Спросить ИИ",
|
||||
"AI is thinking...": "ИИ обрабатывает запрос...",
|
||||
"Thinking": "Думаю",
|
||||
"Ask a question...": "Задайте вопрос...",
|
||||
"AI-powered search (Ask AI)": "Поиск на базе ИИ (Спросить ИИ)",
|
||||
"AI Answers": "Ответы ИИ",
|
||||
"AI-powered search (AI Answers)": "Поиск на базе ИИ (Ответы ИИ)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "Поиск ИИ использует векторные встраивания для обеспечения семантического поиска по содержимому вашего рабочего пространства.",
|
||||
"Toggle AI search": "Переключить поиск ИИ",
|
||||
"Generative AI (Ask AI)": "Генеративный ИИ (Спросить ИИ)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Включите создание контента на базе ИИ в редакторе. Позволяет пользователям генерировать, улучшать, переводить и преобразовывать текст.",
|
||||
"Toggle generative AI": "Переключить генеративный ИИ",
|
||||
"Upgrade your plan": "Обновите свой тарифный план",
|
||||
"Available with a paid license": "Доступно с платной лицензией",
|
||||
"Upgrade your license tier.": "Обновите уровень вашей лицензии.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "ИИ доступен только в корпоративной версии Docmost. Свяжитесь по адресу sales@docmost.com.",
|
||||
"AI & MCP": "ИИ и MCP",
|
||||
"AI": "ИИ",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Протокол контекста модели (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Включите сервер MCP, чтобы ИИ-ассистенты и инструменты могли взаимодействовать с содержимым вашего рабочего пространства.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP доступен только в корпоративной версии Docmost. Свяжитесь по адресу sales@docmost.com.",
|
||||
"MCP Server URL": "URL сервера MCP",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Используйте ваш API-ключ для аутентификации. Управлять API-ключами можно в настройках аккаунта.",
|
||||
"Supported tools": "Поддерживаемые инструменты",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "В вашем рабочем пространстве включён MCP. Используйте свой API-ключ для подключения ИИ-ассистентов.",
|
||||
"MCP server URL:": "URL сервера MCP:",
|
||||
"Learn more": "Подробнее",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Управляйте API-ключами для всех пользователей в рабочем пространстве. Смотрите <anchor>документацию по API</anchor> для получения информации об использовании.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Смотрите <anchor>документацию по API</anchor> для получения информации об использовании.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Смотрите <anchor>документацию по MCP</anchor>.",
|
||||
"Sources": "Источники",
|
||||
"Ask AI not available for attachments": "Функция \"Спросить ИИ\" недоступна для вложений",
|
||||
"AI Answers not available for attachments": "Ответы ИИ недоступны для вложений",
|
||||
"No answer available": "Ответ недоступен",
|
||||
"Background color": "Цвет фона",
|
||||
"Highlight color": "Цвет выделения",
|
||||
"Remove color": "Удалить цвет"
|
||||
"Remove color": "Удалить цвет",
|
||||
"Notifications": "Уведомления",
|
||||
"No notifications": "Нет уведомлений",
|
||||
"No unread notifications": "Нет непрочитанных уведомлений",
|
||||
"All notifications": "Все уведомления",
|
||||
"Unread only": "Только непрочитанные",
|
||||
"Mark all as read": "Отметить все как прочитанные",
|
||||
"Mark as read": "Отметить как прочитанное",
|
||||
"More options": "Больше возможностей",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> упомянул вас в комментарии",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> оставил комментарий на странице",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> отметил(а) комментарий как решённый",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> упомянул(а) вас на странице",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> предоставил(а) вам доступ на редактирование страницы",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> предоставил(а) вам доступ на просмотр страницы",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> обновил(а) страницу",
|
||||
"Watch page": "Следить за страницей",
|
||||
"Stop watching": "Перестать следить",
|
||||
"Watch space": "Следить за пространством",
|
||||
"Stop watching space": "Перестать следить за пространством",
|
||||
"Email notifications": "Уведомления на email",
|
||||
"Page updates": "Обновления страницы",
|
||||
"Get notified when pages you watch are updated.": "Получайте уведомления, когда отслеживаемые вами страницы обновляются.",
|
||||
"Page mentions": "Упоминания на странице",
|
||||
"Get notified when someone mentions you on a page.": "Получайте уведомления, когда кто-то упоминает вас на странице.",
|
||||
"Comment mentions": "Упоминания в комментариях",
|
||||
"Get notified when someone mentions you in a comment.": "Получайте уведомления, когда кто-то упоминает вас в комментарии.",
|
||||
"New comments": "Новые комментарии",
|
||||
"Get notified about new comments on threads you participate in.": "Получайте уведомления о новых комментариях в цепочках, в которых вы участвуете.",
|
||||
"Resolved comments": "Разрешённые комментарии",
|
||||
"Get notified when your comment is resolved.": "Получайте уведомление, когда ваш комментарий разрешён.",
|
||||
"You are now watching this page": "Вы теперь следите за этой страницей",
|
||||
"You are no longer watching this page": "Вы больше не следите за этой страницей",
|
||||
"You are now watching this space": "Теперь вы следите за этим пространством",
|
||||
"You are no longer watching this space": "Вы больше не следите за этим пространством",
|
||||
"Direct": "Прямые",
|
||||
"Updates": "Обновления",
|
||||
"Today": "Сегодня",
|
||||
"Yesterday": "Вчера",
|
||||
"This week": "На этой неделе",
|
||||
"Older": "Старше",
|
||||
"Restricted page": "Страница с ограниченным доступом",
|
||||
"Restricted pages cannot be shared publicly.": "Страницы с ограниченным доступом нельзя сделать общедоступными.",
|
||||
"Restricted by parent": "Ограничено родительской страницей",
|
||||
"Restricted": "Ограничено",
|
||||
"Open": "Открыто",
|
||||
"Inherits restrictions from ancestor page": "Наследует ограничения от родительской страницы",
|
||||
"Only people listed below can access this page": "Доступ к этой странице имеют только перечисленные ниже пользователи",
|
||||
"Everyone in this space can access": "Доступ имеют все участники этого пространства",
|
||||
"No additional restrictions on this page": "На этой странице нет дополнительных ограничений",
|
||||
"Only specific people can access": "Доступ имеют только определённые пользователи",
|
||||
"Use only inherited restrictions": "Использовать только унаследованные ограничения",
|
||||
"Add restrictions on top of inherited": "Добавить ограничения поверх унаследованных",
|
||||
"Inherited restriction": "Унаследованное ограничение",
|
||||
"Access limited by": "Доступ ограничен",
|
||||
"Restrict access to control who can view and edit this page": "Ограничьте доступ, чтобы контролировать, кто может просматривать и редактировать эту страницу",
|
||||
"Add additional restrictions specific to this page": "Добавить дополнительные ограничения, применимые только к этой странице",
|
||||
"Access": "Доступ",
|
||||
"People with access": "Пользователи с доступом",
|
||||
"Remove all": "Удалить всё",
|
||||
"Remove access": "Удалить доступ",
|
||||
"Remove all access": "Удалить весь доступ",
|
||||
"Are you sure you want to remove this member's access to the page?": "Вы уверены, что хотите удалить доступ этого участника к странице?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Вы уверены, что хотите удалить все специальные права доступа? Это сделает страницу доступной всем участникам пространства.",
|
||||
"Trash retention": "Срок хранения корзины",
|
||||
"Pages in trash will be permanently deleted after this period.": "Страницы в корзине будут окончательно удалены по истечении этого срока.",
|
||||
"Trash retention updated": "Срок хранения корзины обновлён",
|
||||
"Failed to update trash retention": "Не удалось обновить срок хранения корзины",
|
||||
"Removed page restriction": "Ограничение доступа к странице удалено",
|
||||
"Added page permission": "Добавлено разрешение доступа к странице",
|
||||
"Removed page permission": "Удалено разрешение доступа к странице",
|
||||
"day": "день",
|
||||
"days": "дни",
|
||||
"week": "неделя",
|
||||
"weeks": "недели",
|
||||
"month": "месяц",
|
||||
"months": "месяцы",
|
||||
"year": "год",
|
||||
"years": "годы",
|
||||
"Period": "Период",
|
||||
"Fixed date": "Фиксированная дата",
|
||||
"Indefinitely": "Бессрочно",
|
||||
"Days": "Дни",
|
||||
"Weeks": "Недели",
|
||||
"Months": "Месяцы",
|
||||
"Years": "Годы",
|
||||
"Pick a date": "Выберите дату",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Максимум для этой единицы измерения — {{max}} {{unit}}",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Срок действия не истекает. Проверяющие могут повторно подтверждать в любое время.",
|
||||
"Verified": "Проверено",
|
||||
"Review needed": "Требуется проверка",
|
||||
"Verification expired": "Срок проверки истёк",
|
||||
"Draft": "Черновик",
|
||||
"In Approval": "На утверждении",
|
||||
"In approval": "На утверждении",
|
||||
"Approved": "Утверждено",
|
||||
"Obsolete": "Устарело",
|
||||
"Expiring": "Истекает",
|
||||
"Set up verification": "Настроить проверку",
|
||||
"Verify page": "Проверить страницу",
|
||||
"Page verification": "Проверка страницы",
|
||||
"Add verification": "Добавить проверку",
|
||||
"Edit verification": "Изменить проверку",
|
||||
"Search by title": "Поиск по заголовку",
|
||||
"Choose how this page should stay accurate.": "Выберите, как поддерживать актуальность этой страницы.",
|
||||
"Recurring verification": "Регулярная проверка",
|
||||
"Verifiers re-confirm this page on a schedule.": "Проверяющие повторно подтверждают эту страницу по расписанию.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Повторно проверять по расписанию (например, каждые 30 дней)",
|
||||
"Page stays editable at all times": "Страница остаётся редактируемой в любое время",
|
||||
"Best for runbooks, FAQs, living documentation": "Лучше всего подходит для инструкций, FAQ и живой документации",
|
||||
"Approval workflow": "Процесс утверждения",
|
||||
"Formal document lifecycle with named approvers.": "Формальный жизненный цикл документа с назначенными утверждающими.",
|
||||
"Draft → In approval → Approved → Obsolete": "Черновик → На утверждении → Утверждено → Устарело",
|
||||
"Locked once approved, with full history": "После утверждения блокируется, с полной историей",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Разработано для ISO 9001, ISO 13485 и FDA",
|
||||
"Best for SOPs and controlled documents": "Лучше всего подходит для СОП и контролируемых документов",
|
||||
"Back": "Назад",
|
||||
"Quality management": "Управление качеством",
|
||||
"Recurring": "Регулярно",
|
||||
"Pages move through draft, approval, and approved stages.": "Страницы проходят стадии черновика, утверждения и утверждённого состояния.",
|
||||
"Verifiers": "Проверяющие",
|
||||
"Add verifier": "Добавить проверяющего",
|
||||
"I've reviewed this page for accuracy": "Я проверил(а) эту страницу на точность",
|
||||
"Set up": "Настроить",
|
||||
"Remove verification": "Удалить проверку",
|
||||
"Are you sure you want to remove verification from this page?": "Вы уверены, что хотите удалить проверку с этой страницы?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Назначенные проверяющие должны периодически повторно проверять эту страницу.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Последняя проверка: {{name}}, {{time}} (срок истёк)",
|
||||
"The fixed expiration date has passed.": "Фиксированная дата истечения срока уже прошла.",
|
||||
"Verified by {{name}} {{time}}": "Проверено: {{name}}, {{time}}",
|
||||
"Expires {{date}}": "Истекает {{date}}",
|
||||
"Expired {{date}}": "Срок истёк {{date}}",
|
||||
"Mark as obsolete": "Отметить как устаревшее",
|
||||
"Mark obsolete": "Отметить как устаревшее",
|
||||
"Returned by {{name}} {{time}}": "Возвращено: {{name}}, {{time}}",
|
||||
"No approval has been requested yet.": "Запрос на утверждение ещё не отправлен.",
|
||||
"Submitted by {{name}} {{time}}": "Отправлено: {{name}}, {{time}}",
|
||||
"Someone": "Кто-то",
|
||||
"Approved by {{name}} {{time}}": "Утверждено: {{name}}, {{time}}",
|
||||
"This document has been marked as obsolete.": "Этот документ был отмечен как устаревший.",
|
||||
"Rejection comment": "Комментарий к отклонению",
|
||||
"Reason for returning this document...": "Причина возврата этого документа...",
|
||||
"Confirm rejection": "Подтвердить отклонение",
|
||||
"Submit for approval": "Отправить на утверждение",
|
||||
"Reject": "Отклонить",
|
||||
"Approve": "Утвердить",
|
||||
"Re-submit for approval": "Повторно отправить на утверждение",
|
||||
"Verified until": "Проверено до",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "Проверенные страницы",
|
||||
"Search pages...": "Поиск страниц...",
|
||||
"Filter by space": "Фильтр по пространству",
|
||||
"Filter by type": "Фильтр по типу",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> проверил(а) страницу",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> отправил(а) страницу вам на утверждение",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> вернул(а) страницу на доработку",
|
||||
"Page verification expires soon": "Срок проверки страницы скоро истекает",
|
||||
"Page verification has expired": "Срок проверки страницы истёк",
|
||||
"Verifying your email": "Подтверждение вашего адреса электронной почты",
|
||||
"Please wait...": "Пожалуйста, подождите...",
|
||||
"Verification failed. The link may have expired.": "Ошибка проверки. Ссылка могла устареть.",
|
||||
"Check your email": "Проверьте вашу электронную почту",
|
||||
"We sent a verification link to {{email}}.": "Мы отправили ссылку для подтверждения на {{email}}.",
|
||||
"We sent a verification link to your email.": "Мы отправили ссылку для подтверждения на вашу электронную почту.",
|
||||
"Click the link to verify your email and access your workspace.": "Перейдите по ссылке, чтобы подтвердить электронную почту и получить доступ к рабочему пространству.",
|
||||
"Resend verification email": "Отправить письмо для подтверждения повторно",
|
||||
"Verification email sent. Please check your inbox.": "Письмо для подтверждения отправлено. Пожалуйста, проверьте ваш почтовый ящик.",
|
||||
"Failed to resend verification email. Please try again.": "Не удалось отправить письмо для подтверждения. Пожалуйста, попробуйте снова.",
|
||||
"We've sent you an email with your associated workspaces.": "Мы отправили вам электронное письмо с привязанными рабочими пространствами.",
|
||||
"Load more": "Загрузить ещё",
|
||||
"Log out of all devices": "Выйти на всех устройствах",
|
||||
"Log out of all sessions except this device": "Выйти из всех сеансов, кроме этого устройства",
|
||||
"This Device": "Это устройство",
|
||||
"Unknown device": "Неизвестное устройство",
|
||||
"No active sessions": "Нет активных сеансов",
|
||||
"Session revoked": "Сеанс отозван",
|
||||
"All other sessions revoked": "Все остальные сеансы отозваны",
|
||||
"Last used": "Последнее использование",
|
||||
"Created": "Создано",
|
||||
"Rename": "Переименовать",
|
||||
"Publish": "Опубликовать",
|
||||
"Security": "Безопасность",
|
||||
"Enforce SSO": "Сделать SSO обязательным",
|
||||
"Once enforced, members will not be able to login with email and password.": "После включения участники не смогут входить с помощью электронной почты и пароля.",
|
||||
"AI-generated content may not be accurate.": "Контент, созданный ИИ, может быть неточным.",
|
||||
"AI Chat": "Чат с ИИ",
|
||||
"Analyze for insights": "Проанализировать и получить выводы",
|
||||
"Ask anything...": "Спросите что угодно...",
|
||||
"Chat history": "История чатов",
|
||||
"Chat name": "Название чата",
|
||||
"Close": "Закрыть",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Не удалось загрузить чат. Произошла ошибка.",
|
||||
"Failed to render this message.": "Не удалось отобразить это сообщение.",
|
||||
"How can I help you today?": "Чем я могу помочь вам сегодня?",
|
||||
"New chat": "Новый чат",
|
||||
"No chat history": "Нет истории чатов",
|
||||
"No chats found": "Чаты не найдены",
|
||||
"No conversations yet": "Пока нет разговоров",
|
||||
"Open full page": "Открыть полную страницу",
|
||||
"Previous 7 days": "Предыдущие 7 дней",
|
||||
"Previous 30 days": "Предыдущие 30 дней",
|
||||
"Search chats...": "Поиск чатов...",
|
||||
"Start a new chat to see it here.": "Начните новый чат, чтобы увидеть его здесь.",
|
||||
"Summarize this page": "Суммировать эту страницу",
|
||||
"Toggle AI Chat": "Переключить чат с ИИ",
|
||||
"Translate this page": "Перевести эту страницу",
|
||||
"Try a different search term.": "Попробуйте другой поисковый запрос.",
|
||||
"Try again": "Попробовать снова",
|
||||
"Untitled chat": "Чат без названия",
|
||||
"What can I help you with?": "Чем я могу вам помочь?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "Додати учасників",
|
||||
"Add to groups": "Додати до груп",
|
||||
"Add space members": "Додати учасників простору",
|
||||
"Add to favorites": "Додати до обраного",
|
||||
"Admin": "Адміністратор",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "Ви впевнені, що хочете видалити цю групу? Учасники втратять доступ до матеріалів, до яких ця група має доступ.",
|
||||
"Are you sure you want to delete this page?": "Ви впевнені, що хочете видалити цю сторінку?",
|
||||
@@ -44,15 +45,15 @@
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Ви впевнені, що хочете видалити цю сторінку? Це видалить її дочірні сторінки, а також історію сторінки. Ця дія необоротна.",
|
||||
"Description": "Опис",
|
||||
"Details": "Деталі",
|
||||
"e.g ACME": "наприклад, ACME",
|
||||
"e.g ACME Inc": "наприклад, ACME Inc",
|
||||
"e.g Developers": "наприклад, Розробники",
|
||||
"e.g Group for developers": "наприклад, Група для розробників",
|
||||
"e.g product": "наприклад, продукт",
|
||||
"e.g Product Team": "наприклад, Продуктова команда",
|
||||
"e.g Sales": "наприклад, Продажі",
|
||||
"e.g Space for product team": "наприклад, Простір для продуктової команди",
|
||||
"e.g Space for sales team to collaborate": "наприклад, Простір для спільної роботи команди продажів",
|
||||
"e.g ACME": "напр., ACME",
|
||||
"e.g ACME Inc": "напр., ACME Inc",
|
||||
"e.g Developers": "напр., Розробники",
|
||||
"e.g Group for developers": "напр., Група для розробників",
|
||||
"e.g product": "напр., продукт",
|
||||
"e.g Product Team": "напр., Команда продукту",
|
||||
"e.g Sales": "напр., Продажі",
|
||||
"e.g Space for product team": "напр., Простір для команди продукту",
|
||||
"e.g Space for sales team to collaborate": "напр., Простір для співпраці команди продажів",
|
||||
"Edit": "Редагувати",
|
||||
"Read": "Читати",
|
||||
"Edit group": "Редагувати групу",
|
||||
@@ -61,7 +62,7 @@
|
||||
"Enter valid email addresses separated by comma or space max_50": "Введіть дійсні адреси електронної пошти, розділені комою або пробілом [макс: 50]",
|
||||
"enter valid emails addresses": "введіть дійсні адреси електронної пошти",
|
||||
"Enter your current password": "Введіть ваш поточний пароль",
|
||||
"enter your full name": "введіть ваше повне ім'я",
|
||||
"enter your full name": "введіть своє повне ім’я",
|
||||
"Enter your new password": "Введіть ваш новий пароль",
|
||||
"Enter your new preferred email": "Введіть вашу нову бажану електронну пошту",
|
||||
"Enter your password": "Введіть ваш пароль",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "Не вдалося імпортувати сторінки",
|
||||
"Failed to load page. An error occurred.": "Не вдалося завантажити сторінку. Сталася помилка.",
|
||||
"Failed to update data": "Не вдалося оновити дані",
|
||||
"Favorite spaces": "Обрані простори",
|
||||
"Favorite spaces appear here": "Тут відображаються обрані простори",
|
||||
"Favorites": "Обране",
|
||||
"Full access": "Повний доступ",
|
||||
"Full page width": "Ширина на всю сторінку",
|
||||
"Full width": "На всю ширину",
|
||||
@@ -87,11 +91,12 @@
|
||||
"Import pages": "Імпорт сторінок",
|
||||
"Import pages & space settings": "Імпорт сторінок і налаштування простору",
|
||||
"Importing pages": "Імпортування сторінок",
|
||||
"invalid invitation link": "посилання на запрошення недійсне",
|
||||
"invalid invitation link": "недійсне посилання запрошення",
|
||||
"Invitation signup": "Реєстрація за запрошенням",
|
||||
"Invite by email": "Запросити електронною поштою",
|
||||
"Invite members": "Запросити учасників",
|
||||
"Invite new members": "Запросити нових учасників",
|
||||
"Invite People": "Запросити людей",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Запрошені учасники, які ще не прийняли запрошення, з'являться тут.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Запрошені учасники отримають доступ до просторів, доступ до яких має група",
|
||||
"Join the workspace": "Приєднатися до робочої області",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "Групу не знайдено",
|
||||
"No page history saved yet.": "Історія сторінок ще не збережена.",
|
||||
"No pages yet": "Сторінок поки немає",
|
||||
"No shared pages": "Немає спільних сторінок",
|
||||
"No results found...": "Результати не знайдено...",
|
||||
"No user found": "Користувача не знайдено",
|
||||
"Overview": "Огляд",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "сторінки",
|
||||
"Password": "Пароль",
|
||||
"Password changed successfully": "Пароль успішно змінено",
|
||||
"People": "Користувачі",
|
||||
"Pending": "В очікуванні",
|
||||
"Please confirm your action": "Будь ласка, підтвердіть вашу дію",
|
||||
"Preferences": "Налаштування",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "Профіль",
|
||||
"Recently updated": "Нещодавно оновлено",
|
||||
"Remove": "Видалити",
|
||||
"Remove from favorites": "Видалити з обраного",
|
||||
"Remove group member": "Видалити учасника групи",
|
||||
"Remove space member": "Видалити учасника простору",
|
||||
"Restore": "Відновити",
|
||||
@@ -147,16 +155,16 @@
|
||||
"Search for users": "Пошук користувачів",
|
||||
"Search for users and groups": "Пошук користувачів та груп",
|
||||
"Search...": "Пошук...",
|
||||
"Select language": "Оберіть мову",
|
||||
"Select role": "Оберіть роль",
|
||||
"Select role to assign to all invited members": "Оберіть роль для всіх запрошених учасників",
|
||||
"Select theme": "Оберіть тему",
|
||||
"Select language": "Виберіть мову",
|
||||
"Select role": "Виберіть роль",
|
||||
"Select role to assign to all invited members": "Виберіть роль, яку буде призначено всім запрошеним учасникам",
|
||||
"Select theme": "Виберіть тему",
|
||||
"Send invitation": "Надіслати запрошення",
|
||||
"Invitation sent": "Запрошення надіслано",
|
||||
"Settings": "Налаштування",
|
||||
"Setup workspace": "Налаштувати робочу область",
|
||||
"Sign In": "Вхід",
|
||||
"Sign Up": "Реєстрація",
|
||||
"Setup workspace": "Налаштувати робочий простір",
|
||||
"Sign In": "Увійти",
|
||||
"Sign Up": "Зареєструватися",
|
||||
"Slug": "Slug",
|
||||
"Space": "Простір",
|
||||
"Space description": "Опис простору",
|
||||
@@ -166,36 +174,37 @@
|
||||
"Space slug": "Slug простору",
|
||||
"Spaces": "Простори",
|
||||
"Spaces you belong to": "Простори, до яких ви належите",
|
||||
"No space found": "Простори не знайдено",
|
||||
"No space found": "Простір не знайдено",
|
||||
"Search for spaces": "Пошук просторів",
|
||||
"Start typing to search...": "Почніть вводити для пошуку...",
|
||||
"Status": "Статус",
|
||||
"Successfully imported": "Успішно імпортовано",
|
||||
"Successfully restored": "Успішно відновлено",
|
||||
"System settings": "Системні налаштування",
|
||||
"Templates": "Шаблони",
|
||||
"Theme": "Тема",
|
||||
"To change your email, you have to enter your password and new email.": "Щоб змінити електронну пошту, вам потрібно ввести пароль і нову адресу.",
|
||||
"Toggle full page width": "Перемкнути ширину на всю сторінку",
|
||||
"Toggle full page width": "Перемкнути повну ширину сторінки",
|
||||
"Unable to import pages. Please try again.": "Не вдалося імпортувати сторінки. Будь ласка, спробуйте ще раз.",
|
||||
"untitled": "без назви",
|
||||
"Untitled": "Без назви",
|
||||
"Updated successfully": "Оновлено успішно",
|
||||
"Updated successfully": "Успішно оновлено",
|
||||
"User": "Користувач",
|
||||
"Workspace": "Робоча область",
|
||||
"Workspace Name": "Ім'я робочої області",
|
||||
"Workspace settings": "Налаштування робочої області",
|
||||
"Workspace": "Робочий простір",
|
||||
"Workspace Name": "Назва робочого простору",
|
||||
"Workspace settings": "Налаштування робочого простору",
|
||||
"You can change your password here.": "Ви можете змінити свій пароль тут.",
|
||||
"Your Email": "Ваша електронна пошта",
|
||||
"Your import is complete.": "Ваш імпорт завершено.",
|
||||
"Your name": "Ваше ім'я",
|
||||
"Your Name": "Ваше ім'я",
|
||||
"Your name": "Ваше ім’я",
|
||||
"Your Name": "Ваше ім’я",
|
||||
"Your password": "Ваш пароль",
|
||||
"Your password must be a minimum of 8 characters.": "Ваш пароль повинен містити мінімум 8 символів.",
|
||||
"Sidebar toggle": "Перемкнути бічну панель",
|
||||
"Sidebar toggle": "Перемикач бічної панелі",
|
||||
"Comments": "Коментарі",
|
||||
"404 page not found": "404 сторінку не знайдено",
|
||||
"Sorry, we can't find the page you are looking for.": "На жаль, ми не можемо знайти сторінку, яку ви шукаєте.",
|
||||
"Take me back to homepage": "Повернутися на головну сторінку",
|
||||
"Take me back to homepage": "Повернути мене на головну сторінку",
|
||||
"Forgot password": "Забули пароль",
|
||||
"Forgot your password?": "Забули пароль?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "Посилання для скидання пароля було надіслано на вашу електронну адресу. Будь ласка, перевірте вхідні повідомлення.",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "Відповісти...",
|
||||
"Error loading comments.": "Помилка при завантаженні коментарів.",
|
||||
"No comments yet.": "Коментарів поки немає.",
|
||||
"No open comments.": "Немає відкритих коментарів.",
|
||||
"No resolved comments.": "Немає вирішених коментарів.",
|
||||
"Add a comment...": "Додати коментар...",
|
||||
"Edit comment": "Редагувати коментар",
|
||||
"Delete comment": "Видалити коментар",
|
||||
"Are you sure you want to delete this comment?": "Ви впевнені, що хочете видалити цей коментар?",
|
||||
"Delete chat": "Видалити чат",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "Ви впевнені, що хочете видалити '{{title}}'? Цю дію неможливо скасувати.",
|
||||
"Comment created successfully": "Коментар успішно створено",
|
||||
"Error creating comment": "Помилка при створенні коментаря",
|
||||
"Comment updated successfully": "Коментар успішно оновлено",
|
||||
@@ -217,18 +231,17 @@
|
||||
"Comment deleted successfully": "Коментар успішно видалено",
|
||||
"Failed to delete comment": "Не вдалося видалити коментар",
|
||||
"Comment resolved successfully": "Коментар успішно вирішено",
|
||||
"Comment re-opened successfully": "Коментар успішно відкрито повторно",
|
||||
"Comment unresolved successfully": "Коментар успішно розв'язано",
|
||||
"Comment re-opened successfully": "Коментар успішно знову відкрито",
|
||||
"Comment unresolved successfully": "Позначку вирішення коментаря успішно знято",
|
||||
"Failed to resolve comment": "Не вдалося вирішити коментар",
|
||||
"Resolve comment": "Вирішити коментар",
|
||||
"Unresolve comment": "Розв'язати коментар",
|
||||
"Resolve Comment Thread": "Вирішити ланцюжок коментарів",
|
||||
"Unresolve Comment Thread": "Розв'язати ланцюжок коментарів",
|
||||
"Resolve comment": "Позначити коментар як вирішений",
|
||||
"Unresolve comment": "Зняти позначку вирішення з коментаря",
|
||||
"Resolve Comment Thread": "Позначити гілку коментарів як вирішену",
|
||||
"Unresolve Comment Thread": "Зняти позначку вирішення з гілки коментарів",
|
||||
"Are you sure you want to resolve this comment thread? This will mark it as completed.": "Ви впевнені, що хочете вирішити цей ланцюжок коментарів? Це позначить його як завершений.",
|
||||
"Are you sure you want to unresolve this comment thread?": "Ви впевнені, що хочете розв'язати цей ланцюжок коментарів?",
|
||||
"Resolved": "Вирішено",
|
||||
"No active comments.": "Немає активних коментарів.",
|
||||
"No resolved comments.": "Немає вирішених коментарів.",
|
||||
"Revoke invitation": "Відкликати запрошення",
|
||||
"Revoke": "Відкликати",
|
||||
"Don't": "Ні",
|
||||
@@ -237,7 +250,7 @@
|
||||
"Anyone with this link can join this workspace.": "Будь-хто, хто має це посилання, може приєднатися до цієї робочої області.",
|
||||
"Invite link": "Посилання для запрошення",
|
||||
"Copy": "Копіювати",
|
||||
"Copy to space": "Скопіювати в простір",
|
||||
"Copy to space": "Копіювати до простору",
|
||||
"Copied": "Скопійовано",
|
||||
"Duplicate": "Дублювати",
|
||||
"Select a user": "Оберіть користувача",
|
||||
@@ -247,7 +260,7 @@
|
||||
"Are you sure you want to delete this space?": "Ви впевнені, що хочете видалити цей простір?",
|
||||
"Delete this space with all its pages and data.": "Видалити цей простір з усіма його сторінками та даними.",
|
||||
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "Усі сторінки, коментарі, вкладення та дозволи в цьому просторі будуть видалені безповоротно.",
|
||||
"Confirm space name": "Підтвердіть назву простору",
|
||||
"Confirm space name": "Підтвердьте назву простору",
|
||||
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Введіть назву простору <b>{{spaceName}}</b>, щоб підтвердити вашу дію.",
|
||||
"Format": "Формат",
|
||||
"Include subpages": "Включити вкладені сторінки",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "Додати рядок нижче",
|
||||
"Delete table": "Видалити таблицю",
|
||||
"Info": "Інформація",
|
||||
"Note": "Примітка",
|
||||
"Success": "Успішно",
|
||||
"Warning": "Попередження",
|
||||
"Danger": "Важливо",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "Зберегти та вийти",
|
||||
"Double-click to edit Excalidraw diagram": "Клацніть двічі для редагування діаграми Excalidraw",
|
||||
"Paste link": "Вставити посилання",
|
||||
"Paste link or search pages": "Вставте посилання або знайдіть сторінки",
|
||||
"Link to web page": "Посилання на веб-сторінку",
|
||||
"Recents": "Нещодавні",
|
||||
"Page or URL": "Сторінка або URL",
|
||||
"Link title": "Назва посилання",
|
||||
"Edit link": "Редагувати посилання",
|
||||
"Remove link": "Видалити посилання",
|
||||
"Add link": "Додати посилання",
|
||||
@@ -302,7 +321,7 @@
|
||||
"Pink": "Рожевий",
|
||||
"Gray": "Сірий",
|
||||
"Embed link": "Вбудоване посилання",
|
||||
"Invalid {{provider}} embed link": "Невірне посилання для вбудовування {{provider}}",
|
||||
"Invalid {{provider}} embed link": "Недійсне посилання для вбудовування {{provider}}",
|
||||
"Embed {{provider}}": "Вбудувати {{provider}}",
|
||||
"Enter {{provider}} link to embed": "Введіть посилання для вбудовування {{provider}}",
|
||||
"Bold": "Жирний",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "Вставити горизонтальний роздільник",
|
||||
"Upload any image from your device.": "Завантажити будь-яке зображення з вашого пристрою.",
|
||||
"Upload any video from your device.": "Завантажити будь-яке відео з вашого пристрою.",
|
||||
"Upload any audio from your device.": "Завантажте будь-який аудіофайл зі свого пристрою.",
|
||||
"Upload any file from your device.": "Завантажити будь-який файл з вашого пристрою.",
|
||||
"Uploading {{name}}": "Завантаження {{name}}",
|
||||
"Uploading file": "Завантаження файлу",
|
||||
@@ -341,24 +361,44 @@
|
||||
"Divider": "Роздільник",
|
||||
"Quote": "Цитата",
|
||||
"Image": "Зображення",
|
||||
"File attachment": "Прикріплений файл",
|
||||
"Toggle block": "Блок, що згортається",
|
||||
"Audio": "Аудіо",
|
||||
"Embed PDF": "Вбудувати PDF",
|
||||
"Upload and embed a PDF file.": "Завантажте та вбудуйте файл PDF.",
|
||||
"Embed as PDF": "Вбудувати як PDF",
|
||||
"Failed to load PDF": "Не вдалося завантажити PDF",
|
||||
"Convert to attachment": "Перетворити на вкладення",
|
||||
"File attachment": "Вкладення файлу",
|
||||
"Toggle block": "Розкривний блок",
|
||||
"Callout": "Виноска",
|
||||
"Insert callout notice.": "Вставити виноску з повідомленням.",
|
||||
"Math inline": "Формула",
|
||||
"Math inline": "Вбудована формула",
|
||||
"Insert inline math equation.": "Вставити математичне рівняння в рядок.",
|
||||
"Math block": "Блок формул",
|
||||
"Insert math equation": "Вставити математичне рівняння",
|
||||
"Math block": "Блок формули",
|
||||
"Insert math equation": "Вставити математичну формулу",
|
||||
"Mermaid diagram": "Діаграма Mermaid",
|
||||
"Insert mermaid diagram": "Вставити діаграму Mermaid",
|
||||
"Insert and design Drawio diagrams": "Вставити та розробити діаграми Draw.io",
|
||||
"Insert and design Drawio diagrams": "Вставляйте та створюйте діаграми Drawio",
|
||||
"Insert current date": "Вставити поточну дату",
|
||||
"Draw and sketch excalidraw diagrams": "Вставити та малювати діаграми Excalidraw",
|
||||
"Multiple": "Декілька",
|
||||
"Draw and sketch excalidraw diagrams": "Створюйте та кресліть діаграми Excalidraw",
|
||||
"Multiple": "Кілька",
|
||||
"Turn into": "Перетворити",
|
||||
"Text align": "Вирівнювання тексту",
|
||||
"This page may have been deleted, moved, or you may not have access.": "Цю сторінку могли видалити, перемістити або у вас може не бути до неї доступу.",
|
||||
"Go to homepage": "Перейти на головну",
|
||||
"Pages you create will show up here.": "Сторінки, які ви створите, з'являться тут.",
|
||||
"Heading {{level}}": "Заголовок {{level}}",
|
||||
"Toggle title": "Перемкнути заголовок",
|
||||
"Write anything. Enter \"/\" for commands": "Почніть писати. Введіть \"/\" для списку команд",
|
||||
"Names do not match": "Назви не співпадають",
|
||||
"Toggle title": "Назва розкривного блоку",
|
||||
"Write anything. Enter \"/\" for commands": "Пишіть що завгодно. Введіть \"/\" для команд",
|
||||
"Write...": "Напишіть...",
|
||||
"Column count": "Кількість колонок",
|
||||
"{{count}} Columns": "{count, plural, one{# колонка} few{# колонки} many{# колонок} other{# колонки}}",
|
||||
"Equal columns": "Рівні колонки",
|
||||
"Left sidebar": "Ліва бічна панель",
|
||||
"Right sidebar": "Права бічна панель",
|
||||
"Wide center": "Широка центральна колонка",
|
||||
"Left wide": "Широка ліва колонка",
|
||||
"Right wide": "Широка права колонка",
|
||||
"Names do not match": "Назви не збігаються",
|
||||
"Today, {{time}}": "Сьогодні, {{time}}",
|
||||
"Yesterday, {{time}}": "Вчора, {{time}}",
|
||||
"Space created successfully": "Простір успішно створено",
|
||||
@@ -367,19 +407,26 @@
|
||||
"Members added successfully": "Учасників успішно додано",
|
||||
"Member removed successfully": "Учасника успішно видалено",
|
||||
"Member role updated successfully": "Роль учасника успішно оновлено",
|
||||
"Created by: <b>{{creatorName}}</b>": "Автор: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Дата створення: {{time}}",
|
||||
"Created by: <b>{{creatorName}}</b>": "Створено: <b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "Створено: {{time}}",
|
||||
"Edited by {{name}} {{time}}": "Змінено {{name}} {{time}}",
|
||||
"Word count: {{wordCount}}": "Кількість слів: {{wordCount}}",
|
||||
"Character count: {{characterCount}}": "Кількість символів: {{characterCount}}",
|
||||
"New update": "Нове оновлення",
|
||||
"{{latestVersion}} is available": "Доступна нова версія {{latestVersion}}",
|
||||
"{{latestVersion}} is available": "Доступна версія {{latestVersion}}",
|
||||
"Default page edit mode": "Режим редагування сторінки за замовчуванням",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "Виберіть бажаний режим редагування сторінки. Уникайте випадкових редагувань.",
|
||||
"Reading": "Читання",
|
||||
"Delete member": "Видалити учасника",
|
||||
"Member deleted successfully": "Учасника успішно видалено",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "Ви впевнені, що хочете видалити цього учасника робочої області? Ця дія незворотна.",
|
||||
"Deactivate member": "Деактивувати учасника",
|
||||
"Activate member": "Активувати учасника",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "Ви впевнені, що хочете деактивувати цього учасника робочого простору? Вони більше не зможуть отримати доступ до цього робочого простору.",
|
||||
"Are you sure you want to activate this workspace member?": "Ви впевнені, що хочете активувати цього учасника робочого простору?",
|
||||
"Deactivate": "Деактивувати",
|
||||
"Activate": "Активувати",
|
||||
"Deactivated": "Деактивовано",
|
||||
"Move": "Перемістити",
|
||||
"Move page": "Перемістити сторінку",
|
||||
"Move page to a different space.": "Перемістити сторінку в інший простір.",
|
||||
@@ -389,25 +436,44 @@
|
||||
"Share": "Поділитися",
|
||||
"Public sharing": "Публічний доступ",
|
||||
"Shared by": "Поділився",
|
||||
"Shared at": "Поділився в",
|
||||
"Shared at": "Поділено",
|
||||
"Inherits public sharing from": "Успадковує публічний доступ від",
|
||||
"Share to web": "Поділитися в інтернеті",
|
||||
"Shared to web": "Розміщено в інтернеті",
|
||||
"Anyone with the link can view this page": "Будь-хто, хто має посилання, може переглянути цю сторінку",
|
||||
"Make this page publicly accessible": "Зробити цю сторінку загальнодоступною",
|
||||
"Share to web": "Опублікувати в інтернеті",
|
||||
"Shared to web": "Опубліковано в інтернеті",
|
||||
"Anyone with the link can view this page": "Будь-хто, хто має посилання, може переглядати цю сторінку",
|
||||
"Make this page publicly accessible": "Зробити цю сторінку публічно доступною",
|
||||
"Include sub-pages": "Включити підсторінки",
|
||||
"Make sub-pages public too": "Зробити підсторінки також загальнодоступними",
|
||||
"Make sub-pages public too": "Зробити підсторінки також публічними",
|
||||
"Allow search engines to index page": "Дозволити пошуковим системам індексувати сторінку",
|
||||
"Open page": "Відкрити сторінку",
|
||||
"Page": "Сторінка",
|
||||
"Delete public share link": "Видалити посилання на публічний доступ",
|
||||
"Delete public share link": "Видалити публічне посилання",
|
||||
"Delete share": "Видалити спільний доступ",
|
||||
"Are you sure you want to delete this shared link?": "Ви впевнені, що хочете видалити це посилання спільного доступу?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Публічні сторінки з просторів, учасником яких ви є, з'являться тут",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "Тут з’являться публічно поширені сторінки з просторів, учасником яких ви є",
|
||||
"Share deleted successfully": "Спільний доступ успішно видалено",
|
||||
"Share not found": "Спільний доступ не знайдено",
|
||||
"Failed to share page": "Не вдалося поділитися сторінкою",
|
||||
"Copy page": "Копіювати сторінки",
|
||||
"Failed to share page": "Не вдалося надати спільний доступ до сторінки",
|
||||
"Disable public sharing": "Вимкнути публічний доступ",
|
||||
"Prevent members from sharing pages publicly.": "Перешкодити учасникам публічно ділитися сторінками.",
|
||||
"Toggle public sharing": "Перемикання публічного доступу",
|
||||
"Toggle space public sharing": "Перемикання публічного доступу до просторів",
|
||||
"Allow viewers to comment": "Дозволити глядачам коментувати",
|
||||
"Allow viewers to add comments on pages in this space.": "Дозволити глядачам додавати коментарі на сторінках у цьому просторі.",
|
||||
"Toggle viewer comments": "Увімкнути або вимкнути коментарі глядачів",
|
||||
"Public sharing is disabled at the workspace level": "Публічний доступ вимкнуто на рівні робочого простору",
|
||||
"Prevent pages in this space from being shared publicly.": "Перешкодити публічному поширенню сторінок у цьому просторі.",
|
||||
"Page permissions": "Права доступу до сторінки.",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "Керуйте тим, хто може переглядати та редагувати окремі сторінки. Доступно з корпоративною ліцензією.",
|
||||
"Enable public sharing": "Увімкнути публічний доступ",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "Ви впевнені, що хочете увімкнути публічний доступ? Учасники зможуть публічно ділитися сторінками.",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "Ви впевнені, що хочете вимкнути публічний доступ? Усі існуючі посилання для спільного доступу в цьому робочому просторі будуть видалені.",
|
||||
"Are you sure you want to enable public sharing for this space?": "Ви впевнені, що хочете увімкнути публічний доступ для цього простору?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "Ви впевнені, що хочете вимкнути публічний доступ? Усі існуючі посилання для спільного доступу в цьому просторі будуть видалені.",
|
||||
"Public sharing is disabled": "Публічний доступ вимкнуто",
|
||||
"Public sharing has been disabled at the workspace level.": "Публічний доступ було вимкнено на рівні робочого простору.",
|
||||
"Public sharing has been disabled for this space.": "Публічний доступ було вимкнено для цього простору.",
|
||||
"Copy page": "Копіювати сторінку",
|
||||
"Copy page to a different space.": "Скопіювати сторінку в інший простір.",
|
||||
"Page copied successfully": "Сторінку успішно скопійовано",
|
||||
"Page duplicated successfully": "Сторінку успішно дубльовано",
|
||||
@@ -421,120 +487,121 @@
|
||||
"Replace (Enter)": "Замінити (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "Замінити все (Ctrl+Alt+Enter)",
|
||||
"Replace all": "Замінити все",
|
||||
"View all": "Переглянути все",
|
||||
"View all spaces": "Переглянути всі простори",
|
||||
"Error": "Помилка",
|
||||
"Failed to disable MFA": "Не вдалося вимкнути MFA",
|
||||
"Disable two-factor authentication": "Вимкнути двоетапну аутентифікацію",
|
||||
"Disable two-factor authentication": "Вимкнути двофакторну автентифікацію",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Вимкнення двоетапної аутентифікації зробить ваш обліковий запис менш захищеним. Для входу потрібен лише пароль.",
|
||||
"Please enter your password to disable two-factor authentication:": "Будь ласка, введіть свій пароль, щоб вимкнути двоетапну аутентифікацію:",
|
||||
"Two-factor authentication has been enabled": "Двоетапну аутентифікацію включено",
|
||||
"Two-factor authentication has been disabled": "Двоетапну аутентифікацію вимкнено",
|
||||
"Two-factor authentication has been enabled": "Двофакторну автентифікацію ввімкнено",
|
||||
"Two-factor authentication has been disabled": "Двофакторну автентифікацію вимкнено",
|
||||
"2-step verification": "Двоетапна перевірка",
|
||||
"Protect your account with an additional verification layer when signing in.": "Захистіть свій обліковий запис за допомогою додаткового шару перевірки при вході.",
|
||||
"Two-factor authentication is active on your account.": "Двоетапну аутентифікацію активовано у вашому обліковому записі.",
|
||||
"Add 2FA method": "Додати метод 2FA",
|
||||
"Backup codes": "Резервні коди",
|
||||
"Disable": "Вимкнути",
|
||||
"Invalid verification code": "Невірний код перевірки",
|
||||
"New backup codes have been generated": "Нові резервні коди створено",
|
||||
"Failed to regenerate backup codes": "Не вдалося повторно створити резервні коди",
|
||||
"Invalid verification code": "Недійсний код перевірки",
|
||||
"New backup codes have been generated": "Нові резервні коди згенеровано",
|
||||
"Failed to regenerate backup codes": "Не вдалося повторно згенерувати резервні коди",
|
||||
"About backup codes": "Про резервні коди",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Резервні коди можуть бути використані для доступу до вашого облікового запису, якщо ви втратите доступ до додатку аутентифікатора. Кожен код можна використовувати лише один раз.",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "Ви можете повторно створити нові резервні коди в будь-який час. Це зробить усі існуючі коди недійсними.",
|
||||
"Confirm password": "Підтвердити пароль",
|
||||
"Generate new backup codes": "Створити нові резервні коди",
|
||||
"Confirm password": "Підтвердьте пароль",
|
||||
"Generate new backup codes": "Згенерувати нові резервні коди",
|
||||
"Save your new backup codes": "Збережіть нові резервні коди",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Обов'язково збережіть ці коди у безпечному місці. Ваші старі резервні коди більше не дійсні.",
|
||||
"Your new backup codes": "Ваші нові резервні коди",
|
||||
"I've saved my backup codes": "Я зберіг резервні коди",
|
||||
"I've saved my backup codes": "Я зберіг(-ла) свої резервні коди",
|
||||
"Failed to setup MFA": "Не вдалося налаштувати MFA",
|
||||
"Setup & Verify": "Налаштувати та перевірити",
|
||||
"Add to authenticator": "Додати до аутентифікатора",
|
||||
"1. Scan this QR code with your authenticator app": "1. Скануйте цей QR-код за допомогою додатку аутентифікатора",
|
||||
"Setup & Verify": "Налаштувати й підтвердити",
|
||||
"Add to authenticator": "Додати до застосунку автентифікації",
|
||||
"1. Scan this QR code with your authenticator app": "1. Відскануйте цей QR-код за допомогою застосунку автентифікації",
|
||||
"Can't scan the code?": "Не можете відсканувати код?",
|
||||
"Enter this code manually in your authenticator app:": "Введіть цей код вручну у додатку аутентифікатора:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Введіть 6-значний код із аутентифікатора",
|
||||
"Verify and enable": "Перевірити та увімкнути",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. Введіть 6-значний код із застосунку автентифікації",
|
||||
"Verify and enable": "Підтвердити й увімкнути",
|
||||
"Failed to generate QR code. Please try again.": "Не вдалося створити QR-код. Будь ласка, спробуйте ще раз.",
|
||||
"Backup": "Резервне копіювання",
|
||||
"Backup": "Резервний",
|
||||
"Save codes": "Зберегти коди",
|
||||
"Save your backup codes": "Зберегти резервні коди",
|
||||
"Save your backup codes": "Збережіть свої резервні коди",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Ці коди можуть бути використані для доступу до вашого облікового запису, якщо ви втратите доступ до додатку аутентифікатора. Кожен код можна використовувати лише один раз.",
|
||||
"Print": "Друкувати",
|
||||
"Print": "Друк",
|
||||
"Two-factor authentication has been set up. Please log in again.": "Двоетапну аутентифікацію налаштовано. Будь ласка, увійдіть знову.",
|
||||
"Two-Factor authentication required": "Потрібна двоетапна аутентифікація",
|
||||
"Your workspace requires two-factor authentication for all users": "Ваш робочий простір вимагає двоетапної аутентифікації для всіх користувачів",
|
||||
"Two-Factor authentication required": "Потрібна двофакторна автентифікація",
|
||||
"Your workspace requires two-factor authentication for all users": "Ваш робочий простір вимагає двофакторну автентифікацію для всіх користувачів",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "Щоб продовжити доступ до робочого простору, вам потрібно налаштувати двоетапну аутентифікацію. Це додає додатковий шар захисту до вашого облікового запису.",
|
||||
"Set up two-factor authentication": "Налаштувати двоетапну аутентифікацію",
|
||||
"Cancel and logout": "Скасувати та вийти",
|
||||
"Set up two-factor authentication": "Налаштувати двофакторну автентифікацію",
|
||||
"Cancel and logout": "Скасувати й вийти",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "Ваш робочий простір вимагає двоетапної аутентифікації. Будь ласка, налаштуйте це щоб продовжити.",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "Це додає додатковий шар захисту до вашого облікового запису, вимагаючи код підтвердження з вашого додатку аутентифікатора.",
|
||||
"Password is required": "Вимагається пароль",
|
||||
"Password must be at least 8 characters": "Пароль повинен містити щонайменше 8 символів",
|
||||
"Password is required": "Пароль обов’язковий",
|
||||
"Password must be at least 8 characters": "Пароль має містити щонайменше 8 символів",
|
||||
"Please enter a 6-digit code": "Будь ласка, введіть 6-значний код",
|
||||
"Code must be exactly 6 digits": "Код повинен мати точно 6 цифр",
|
||||
"Enter the 6-digit code found in your authenticator app": "Введіть 6-значний код з вашого додатку аутентифікатора",
|
||||
"Code must be exactly 6 digits": "Код має містити рівно 6 цифр",
|
||||
"Enter the 6-digit code found in your authenticator app": "Введіть 6-значний код із застосунку автентифікації",
|
||||
"Need help authenticating?": "Потрібна допомога з аутентифікацією?",
|
||||
"MFA QR Code": "MFA QR-код",
|
||||
"MFA QR Code": "QR-код MFA",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "Обліковий запис успішно створено. Будь ласка, увійдіть, щоб налаштувати двоетапну аутентифікацію.",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "Скидання паролю успішне. Будь ласка, увійдіть за допомогою нового паролю та завершіть двоетапну аутентифікацію.",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "Скидання паролю успішне. Будь ласка, увійдіть за допомогою нового паролю, щоб налаштувати двоетапну аутентифікацію.",
|
||||
"Password reset was successful. Please log in with your new password.": "Скидання паролю успішне. Будь ласка, увійдіть за допомогою нового паролю.",
|
||||
"Two-factor authentication": "Двоетапна аутентифікація",
|
||||
"Use authenticator app instead": "Використовуйте додаток аутентифікатора замість цього",
|
||||
"Verify backup code": "Перевірити резервний код",
|
||||
"Use backup code": "Використовуйте резервний код",
|
||||
"Enter one of your backup codes": "Введіть один з ваших резервних кодів",
|
||||
"Two-factor authentication": "Двофакторна автентифікація",
|
||||
"Use authenticator app instead": "Натомість використати застосунок автентифікації",
|
||||
"Verify backup code": "Підтвердити резервний код",
|
||||
"Use backup code": "Використати резервний код",
|
||||
"Enter one of your backup codes": "Введіть один зі своїх резервних кодів",
|
||||
"Backup code": "Резервний код",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "Введіть один з ваших резервних кодів. Кожен резервний код можна використовувати лише один раз.",
|
||||
"Verify": "Перевірити",
|
||||
"Verify": "Підтвердити",
|
||||
"Trash": "Кошик",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "Сторінки у кошику будуть остаточно видалені через 30 днів.",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "Сторінки в кошику будуть остаточно видалені через {count, plural, one{# день} few{# дні} many{# днів} other{# дня}}.",
|
||||
"Deleted": "Видалено",
|
||||
"No pages in trash": "Немає сторінок у кошику",
|
||||
"No pages in trash": "У кошику немає сторінок",
|
||||
"Permanently delete page?": "Остаточно видалити сторінку?",
|
||||
"Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "Ви впевнені, що хочете остаточно видалити '{{title}}'? Цю дію не можна скасувати.",
|
||||
"Restore '{{title}}' and its sub-pages?": "Відновити '{{title}}' та її підсторінки?",
|
||||
"Move to trash": "Перемістити до кошика",
|
||||
"Move to trash": "Перемістити в кошик",
|
||||
"Move this page to trash?": "Перемістити цю сторінку до кошика?",
|
||||
"Restore page": "Відновити сторінку",
|
||||
"Page moved to trash": "Сторінка переміщена до кошика",
|
||||
"Page moved to trash": "Сторінку переміщено в кошик",
|
||||
"Page restored successfully": "Сторінку успішно відновлено",
|
||||
"Deleted by": "Видалено",
|
||||
"Deleted at": "Видалено о",
|
||||
"Deleted by": "Видалив",
|
||||
"Deleted at": "Видалено",
|
||||
"Preview": "Попередній перегляд",
|
||||
"Subpages": "Підсторінки",
|
||||
"Failed to load subpages": "Не вдалося завантажити підсторінки",
|
||||
"No subpages": "Немає підсторінок",
|
||||
"Subpages (Child pages)": "Підсторінки (дочірні сторінки)",
|
||||
"List all subpages of the current page": "Перелік всіх підсторінок поточної сторінки",
|
||||
"List all subpages of the current page": "Показати всі підсторінки поточної сторінки",
|
||||
"Attachments": "Вкладення",
|
||||
"All spaces": "Усі простори",
|
||||
"Unknown": "Невідомо",
|
||||
"Find a space": "Знайти простір",
|
||||
"Search in all your spaces": "Шукати у всіх ваших просторах",
|
||||
"Search in all your spaces": "Шукати в усіх ваших просторах",
|
||||
"Type": "Тип",
|
||||
"Enterprise": "Підприємство",
|
||||
"Enterprise": "Enterprise",
|
||||
"Download attachment": "Завантажити вкладення",
|
||||
"Allowed email domains": "Дозволені домени електронної пошти",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Лише користувачі з адресами електронної пошти з цих доменів можуть реєструватися через SSO.",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "Лише користувачі з адресами електронної пошти з цих доменів можуть зареєструватися через SSO.",
|
||||
"Enter valid domain names separated by comma or space": "Введіть дійсні доменні імена, розділені комою або пробілом",
|
||||
"Enforce two-factor authentication": "Вимагати двофакторну автентифікацію",
|
||||
"Enforce two-factor authentication": "Зробити двофакторну автентифікацію обов’язковою",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "Після увімкнення всі учасники повинні ввімкнути двофакторну автентифікацію для доступу до робочого простору.",
|
||||
"Toggle MFA enforcement": "Перемикання вимоги MFA",
|
||||
"Display name": "Відображуване ім'я",
|
||||
"Toggle MFA enforcement": "Перемкнути обов’язковість MFA",
|
||||
"Display name": "Ім’я для відображення",
|
||||
"Allow signup": "Дозволити реєстрацію",
|
||||
"Enabled": "Увімкнено",
|
||||
"Advanced Settings": "Розширені налаштування",
|
||||
"Enable TLS/SSL": "Увімкнути TLS/SSL",
|
||||
"Use secure connection to LDAP server": "Використовувати захищене з'єднання з сервером LDAP",
|
||||
"Group sync": "Синхронізація групи",
|
||||
"Use secure connection to LDAP server": "Використовувати захищене з’єднання з сервером LDAP",
|
||||
"Group sync": "Синхронізація груп",
|
||||
"No SSO providers found.": "Постачальників SSO не знайдено.",
|
||||
"Delete SSO provider": "Видалити постачальника SSO",
|
||||
"Are you sure you want to delete this SSO provider?": "Ви впевнені, що хочете видалити цього постачальника SSO?",
|
||||
"Action": "Дія",
|
||||
"{{ssoProviderType}} configuration": "Конфігурація {{ssoProviderType}}",
|
||||
"Icon": "Іконка",
|
||||
"Icon": "Значок",
|
||||
"Upload image": "Завантажити зображення",
|
||||
"Remove image": "Видалити зображення",
|
||||
"Failed to remove image": "Не вдалося видалити зображення",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Цю дію не можна скасувати. Будь-які додатки, що використовують цей ключ API, перестануть працювати.",
|
||||
"Update API key": "Оновити ключ API",
|
||||
"Manage API keys for all users in the workspace": "Керувати ключами API для всіх користувачів у робочій області",
|
||||
"Restrict API key creation to admins": "Обмежити створення API-ключів лише для адміністраторів",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Тільки адміністратори та власники можуть створювати нові API-ключі. Існуючі ключі учасників і надалі працюватимуть.",
|
||||
"Toggle restrict API keys to admins": "Увімкнути або вимкнути обмеження створення API-ключів лише для адміністраторів",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "Створення API-ключів дозволено лише адміністраторам за налаштуванням адміністратора робочого простору.",
|
||||
"AI settings": "Налаштування ШІ",
|
||||
"AI search": "Пошук з ШІ",
|
||||
"AI Answer": "Відповідь ШІ",
|
||||
"Ask AI": "Запитати ШІ",
|
||||
"AI is thinking...": "ШІ думає...",
|
||||
"Thinking": "Думаю",
|
||||
"Ask a question...": "Задайте питання...",
|
||||
"AI-powered search (Ask AI)": "Пошук на базі ШІ (Запитати ШІ)",
|
||||
"AI Answers": "Відповіді ШІ",
|
||||
"AI-powered search (AI Answers)": "Пошук на базі ШІ (Відповіді ШІ)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "Пошук з ШІ використовує векторні вбудовування для надання можливостей семантичного пошуку у вашому робочому вмісті.",
|
||||
"Toggle AI search": "Переключити пошук з ШІ",
|
||||
"Generative AI (Ask AI)": "Генеративний ШІ (Запитати ШІ)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Увімкнути генерацію контенту за допомогою ШІ в редакторі. Дозволяє користувачам генерувати, покращувати, перекладати та трансформувати текст.",
|
||||
"Toggle generative AI": "Переключити генеративний ШІ",
|
||||
"Upgrade your plan": "Оновіть свій тарифний план",
|
||||
"Available with a paid license": "Доступно за платною ліцензією",
|
||||
"Upgrade your license tier.": "Оновіть рівень своєї ліцензії.",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "ШІ доступний лише в корпоративній редакції Docmost. Зверніться до sales@docmost.com.",
|
||||
"AI & MCP": "ШІ та MCP",
|
||||
"AI": "ШІ",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "Протокол контексту моделі (MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Увімкніть MCP‑сервер, щоб дозволити ШІ‑помічникам та інструментам взаємодіяти з вмістом вашого робочого простору.",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP доступний лише в корпоративній редакції Docmost. Зверніться до sales@docmost.com.",
|
||||
"MCP Server URL": "URL сервера MCP",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "Використовуйте свій API‑ключ для аутентифікації. Ви можете керувати API‑ключами в налаштуваннях облікового запису.",
|
||||
"Supported tools": "Підтримувані інструменти",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "У вашому робочому просторі MCP увімкнено. Використайте свій API‑ключ, щоб підключити ШІ‑помічників.",
|
||||
"MCP server URL:": "URL сервера MCP:",
|
||||
"Learn more": "Дізнатися більше",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Керуйте ключами API для всіх користувачів у робочому просторі. Перегляньте <anchor>документацію API</anchor> для деталей використання.",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "Перегляньте <anchor>документацію API</anchor> для деталей використання.",
|
||||
"View the <anchor>MCP documentation</anchor>.": "Перегляньте <anchor>документацію MCP</anchor>.",
|
||||
"Sources": "Джерела",
|
||||
"Ask AI not available for attachments": "Запитати ШІ недоступно для вкладень",
|
||||
"AI Answers not available for attachments": "Відповіді ШІ недоступні для вкладень",
|
||||
"No answer available": "Відповідь недоступна",
|
||||
"Background color": "Колір фону",
|
||||
"Highlight color": "Колір підсвічування",
|
||||
"Remove color": "Видалити колір"
|
||||
"Remove color": "Видалити колір",
|
||||
"Notifications": "Сповіщення",
|
||||
"No notifications": "Немає сповіщень",
|
||||
"No unread notifications": "Немає непрочитаних сповіщень",
|
||||
"All notifications": "Усі сповіщення",
|
||||
"Unread only": "Тільки непрочитані",
|
||||
"Mark all as read": "Позначити все як прочитане",
|
||||
"Mark as read": "Позначити як прочитане",
|
||||
"More options": "Більше опцій",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> згадав вас у коментарі",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> залишив коментар на сторінці",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> позначив(-ла) коментар як вирішений",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> згадав(-ла) вас на сторінці",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> надав(-ла) вам доступ до редагування сторінки",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> надав(-ла) вам доступ до перегляду сторінки",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> оновив(-ла) сторінку",
|
||||
"Watch page": "Стежити за сторінкою",
|
||||
"Stop watching": "Припинити стежити",
|
||||
"Watch space": "Стежити за простором",
|
||||
"Stop watching space": "Припинити стежити за простором",
|
||||
"Email notifications": "Сповіщення електронною поштою",
|
||||
"Page updates": "Оновлення сторінки",
|
||||
"Get notified when pages you watch are updated.": "Отримуйте сповіщення, коли сторінки, за якими ви стежите, оновлюються.",
|
||||
"Page mentions": "Згадки на сторінці",
|
||||
"Get notified when someone mentions you on a page.": "Отримуйте сповіщення, коли хтось згадує вас на сторінці.",
|
||||
"Comment mentions": "Згадки у коментарях",
|
||||
"Get notified when someone mentions you in a comment.": "Отримуйте сповіщення, коли хтось згадує вас у коментарі.",
|
||||
"New comments": "Нові коментарі",
|
||||
"Get notified about new comments on threads you participate in.": "Отримуйте сповіщення про нові коментарі у темах, у яких ви берете участь.",
|
||||
"Resolved comments": "Вирішені коментарі",
|
||||
"Get notified when your comment is resolved.": "Отримайте сповіщення, коли ваш коментар вирішено.",
|
||||
"You are now watching this page": "Ви зараз стежите за цією сторінкою",
|
||||
"You are no longer watching this page": "Ви більше не стежите за цією сторінкою",
|
||||
"You are now watching this space": "Тепер ви стежите за цим простором",
|
||||
"You are no longer watching this space": "Ви більше не стежите за цим простором",
|
||||
"Direct": "Прямі",
|
||||
"Updates": "Оновлення",
|
||||
"Today": "Сьогодні",
|
||||
"Yesterday": "Вчора",
|
||||
"This week": "Цього тижня",
|
||||
"Older": "Старіші",
|
||||
"Restricted page": "Сторінка з обмеженим доступом",
|
||||
"Restricted pages cannot be shared publicly.": "Сторінки з обмеженим доступом не можна робити загальнодоступними.",
|
||||
"Restricted by parent": "Обмежено батьківською сторінкою",
|
||||
"Restricted": "Обмежено",
|
||||
"Open": "Відкрита",
|
||||
"Inherits restrictions from ancestor page": "Наслідує обмеження від батьківської сторінки",
|
||||
"Only people listed below can access this page": "Доступ до цієї сторінки мають лише люди, вказані нижче.",
|
||||
"Everyone in this space can access": "Усі в цьому просторі мають доступ",
|
||||
"No additional restrictions on this page": "Додаткових обмежень на цій сторінці немає.",
|
||||
"Only specific people can access": "Доступ мають лише конкретні особи.",
|
||||
"Use only inherited restrictions": "Використовувати лише успадковані обмеження",
|
||||
"Add restrictions on top of inherited": "Додати обмеження поверх успадкованих",
|
||||
"Inherited restriction": "Успадковане обмеження",
|
||||
"Access limited by": "Доступ обмежено через",
|
||||
"Restrict access to control who can view and edit this page": "Обмежте доступ, щоб контролювати, хто може переглядати та редагувати цю сторінку.",
|
||||
"Add additional restrictions specific to this page": "Додати додаткові обмеження для цієї сторінки.",
|
||||
"Access": "Доступ",
|
||||
"People with access": "Особи з доступом",
|
||||
"Remove all": "Видалити все",
|
||||
"Remove access": "Видалити доступ",
|
||||
"Remove all access": "Видалити весь доступ",
|
||||
"Are you sure you want to remove this member's access to the page?": "Ви впевнені, що хочете видалити доступ цього учасника до сторінки?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "Ви впевнені, що хочете видалити всі індивідуальні дозволи доступу? Це зробить сторінку доступною для всіх у просторі.",
|
||||
"Trash retention": "Термін зберігання у кошику",
|
||||
"Pages in trash will be permanently deleted after this period.": "Сторінки в кошику будуть остаточно видалені після цього періоду.",
|
||||
"Trash retention updated": "Термін зберігання у кошику оновлено",
|
||||
"Failed to update trash retention": "Не вдалося оновити термін зберігання у кошику",
|
||||
"Removed page restriction": "Обмеження сторінки видалено",
|
||||
"Added page permission": "Додано дозвіл на сторінку",
|
||||
"Removed page permission": "Дозвіл на сторінку видалено",
|
||||
"day": "день",
|
||||
"days": "дні",
|
||||
"week": "тиждень",
|
||||
"weeks": "тижні",
|
||||
"month": "місяць",
|
||||
"months": "місяці",
|
||||
"year": "рік",
|
||||
"years": "роки",
|
||||
"Period": "Період",
|
||||
"Fixed date": "Фіксована дата",
|
||||
"Indefinitely": "Безстроково",
|
||||
"Days": "Дні",
|
||||
"Weeks": "Тижні",
|
||||
"Months": "Місяці",
|
||||
"Years": "Роки",
|
||||
"Pick a date": "Виберіть дату",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "Максимум для цієї одиниці — {{max}} {{unit}}",
|
||||
"Never expires. Verifiers can re-verify at any time.": "Термін дії не спливає. Верифікатори можуть повторно перевірити будь-коли.",
|
||||
"Verified": "Перевірено",
|
||||
"Review needed": "Потрібен перегляд",
|
||||
"Verification expired": "Термін перевірки сплив",
|
||||
"Draft": "Чернетка",
|
||||
"In Approval": "На погодженні",
|
||||
"In approval": "На погодженні",
|
||||
"Approved": "Погоджено",
|
||||
"Obsolete": "Застаріло",
|
||||
"Expiring": "Термін дії спливає",
|
||||
"Set up verification": "Налаштувати перевірку",
|
||||
"Verify page": "Перевірити сторінку",
|
||||
"Page verification": "Перевірка сторінки",
|
||||
"Add verification": "Додати перевірку",
|
||||
"Edit verification": "Редагувати перевірку",
|
||||
"Search by title": "Пошук за назвою",
|
||||
"Choose how this page should stay accurate.": "Виберіть, як підтримувати актуальність цієї сторінки.",
|
||||
"Recurring verification": "Регулярна перевірка",
|
||||
"Verifiers re-confirm this page on a schedule.": "Верифікатори повторно підтверджують цю сторінку за розкладом.",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "Повторно перевіряти за розкладом (наприклад, кожні 30 днів)",
|
||||
"Page stays editable at all times": "Сторінка залишається доступною для редагування в будь-який час",
|
||||
"Best for runbooks, FAQs, living documentation": "Найкраще підходить для runbook-ів, FAQ і живої документації",
|
||||
"Approval workflow": "Процес погодження",
|
||||
"Formal document lifecycle with named approvers.": "Формальний життєвий цикл документа з призначеними погоджувачами.",
|
||||
"Draft → In approval → Approved → Obsolete": "Чернетка → На погодженні → Погоджено → Застаріло",
|
||||
"Locked once approved, with full history": "Після погодження блокується, із повною історією",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "Призначено для ISO 9001, ISO 13485 та FDA",
|
||||
"Best for SOPs and controlled documents": "Найкраще підходить для SOP і контрольованих документів",
|
||||
"Back": "Назад",
|
||||
"Quality management": "Управління якістю",
|
||||
"Recurring": "Регулярна",
|
||||
"Pages move through draft, approval, and approved stages.": "Сторінки проходять стадії чернетки, погодження та погодженого документа.",
|
||||
"Verifiers": "Верифікатори",
|
||||
"Add verifier": "Додати верифікатора",
|
||||
"I've reviewed this page for accuracy": "Я перевірив(ла) цю сторінку на точність",
|
||||
"Set up": "Налаштувати",
|
||||
"Remove verification": "Видалити перевірку",
|
||||
"Are you sure you want to remove verification from this page?": "Ви впевнені, що хочете видалити перевірку з цієї сторінки?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "Призначені верифікатори мають періодично повторно перевіряти цю сторінку.",
|
||||
"Last verified by {{name}} {{time}} (expired)": "Востаннє перевірив(-ла) {{name}} {{time}} (термін дії сплив)",
|
||||
"The fixed expiration date has passed.": "Фіксована дата завершення вже минула.",
|
||||
"Verified by {{name}} {{time}}": "Перевірено: {{name}} {{time}}",
|
||||
"Expires {{date}}": "Термін дії спливає {{date}}",
|
||||
"Expired {{date}}": "Термін дії сплив {{date}}",
|
||||
"Mark as obsolete": "Позначити як застаріле",
|
||||
"Mark obsolete": "Позначити як застаріле",
|
||||
"Returned by {{name}} {{time}}": "Повернуто: {{name}} {{time}}",
|
||||
"No approval has been requested yet.": "Запит на погодження ще не було надіслано.",
|
||||
"Submitted by {{name}} {{time}}": "Надіслано: {{name}} {{time}}",
|
||||
"Someone": "Хтось",
|
||||
"Approved by {{name}} {{time}}": "Погоджено: {{name}} {{time}}",
|
||||
"This document has been marked as obsolete.": "Цей документ позначено як застарілий.",
|
||||
"Rejection comment": "Коментар щодо відхилення",
|
||||
"Reason for returning this document...": "Причина повернення цього документа...",
|
||||
"Confirm rejection": "Підтвердити відхилення",
|
||||
"Submit for approval": "Надіслати на погодження",
|
||||
"Reject": "Відхилити",
|
||||
"Approve": "Погодити",
|
||||
"Re-submit for approval": "Повторно надіслати на погодження",
|
||||
"Verified until": "Перевірено до",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "Перевірені сторінки",
|
||||
"Search pages...": "Шукати сторінки...",
|
||||
"Filter by space": "Фільтрувати за простором",
|
||||
"Filter by type": "Фільтрувати за типом",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> перевірив(-ла) сторінку",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> надіслав(-ла) сторінку вам на погодження",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> повернув(-ла) сторінку на доопрацювання",
|
||||
"Page verification expires soon": "Термін перевірки сторінки скоро спливає",
|
||||
"Page verification has expired": "Термін перевірки сторінки сплив",
|
||||
"Verifying your email": "Перевірка вашої електронної пошти",
|
||||
"Please wait...": "Будь ласка, зачекайте...",
|
||||
"Verification failed. The link may have expired.": "Підтвердження не вдалося. Посилання могло втратити чинність.",
|
||||
"Check your email": "Перевірте свою електронну пошту",
|
||||
"We sent a verification link to {{email}}.": "Ми надіслали посилання для підтвердження на {{email}}.",
|
||||
"We sent a verification link to your email.": "Ми надіслали посилання для підтвердження на вашу електронну пошту.",
|
||||
"Click the link to verify your email and access your workspace.": "Клікніть на посилання, щоб підтвердити електронну пошту та отримати доступ до робочого простору.",
|
||||
"Resend verification email": "Повторно надіслати лист для підтвердження",
|
||||
"Verification email sent. Please check your inbox.": "Лист для підтвердження надіслано. Будь ласка, перевірте свою скриньку.",
|
||||
"Failed to resend verification email. Please try again.": "Не вдалося повторно надіслати лист для підтвердження. Будь ласка, спробуйте ще раз.",
|
||||
"We've sent you an email with your associated workspaces.": "Ми надіслали вам лист із переліком пов’язаних робочих просторів.",
|
||||
"Load more": "Завантажити більше",
|
||||
"Log out of all devices": "Вийти на всіх пристроях",
|
||||
"Log out of all sessions except this device": "Вийти з усіх сеансів, крім цього пристрою",
|
||||
"This Device": "Цей пристрій",
|
||||
"Unknown device": "Невідомий пристрій",
|
||||
"No active sessions": "Немає активних сеансів",
|
||||
"Session revoked": "Сеанс відкликано",
|
||||
"All other sessions revoked": "Усі інші сеанси відкликано",
|
||||
"Last used": "Останнє використання",
|
||||
"Created": "Створено",
|
||||
"Rename": "Перейменувати",
|
||||
"Publish": "Опублікувати",
|
||||
"Security": "Безпека",
|
||||
"Enforce SSO": "Зробити SSO обов’язковим",
|
||||
"Once enforced, members will not be able to login with email and password.": "Після ввімкнення учасники не зможуть входити за допомогою електронної пошти та пароля.",
|
||||
"AI-generated content may not be accurate.": "Вміст, згенерований ШІ, може бути неточним.",
|
||||
"AI Chat": "AI-чат",
|
||||
"Analyze for insights": "Проаналізувати для отримання висновків",
|
||||
"Ask anything...": "Запитайте що завгодно...",
|
||||
"Chat history": "Історія чатів",
|
||||
"Chat name": "Назва чату",
|
||||
"Close": "Закрити",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "Не вдалося завантажити чат. Сталася помилка.",
|
||||
"Failed to render this message.": "Не вдалося відобразити це повідомлення.",
|
||||
"How can I help you today?": "Чим я можу допомогти вам сьогодні?",
|
||||
"New chat": "Новий чат",
|
||||
"No chat history": "Немає історії чатів",
|
||||
"No chats found": "Чатів не знайдено",
|
||||
"No conversations yet": "Розмов поки немає",
|
||||
"Open full page": "Відкрити повну сторінку",
|
||||
"Previous 7 days": "Попередні 7 днів",
|
||||
"Previous 30 days": "Попередні 30 днів",
|
||||
"Search chats...": "Шукати чати...",
|
||||
"Start a new chat to see it here.": "Почніть новий чат, щоб побачити його тут.",
|
||||
"Summarize this page": "Підсумувати цю сторінку",
|
||||
"Toggle AI Chat": "Перемкнути AI-чат",
|
||||
"Translate this page": "Перекласти цю сторінку",
|
||||
"Try a different search term.": "Спробуйте інший пошуковий запит.",
|
||||
"Try again": "Спробувати ще раз",
|
||||
"Untitled chat": "Чат без назви",
|
||||
"What can I help you with?": "Чим я можу вам допомогти?"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Add members": "添加成员",
|
||||
"Add to groups": "添加到群组",
|
||||
"Add space members": "添加空间成员",
|
||||
"Add to favorites": "添加到收藏",
|
||||
"Admin": "管理员",
|
||||
"Are you sure you want to delete this group? Members will lose access to resources this group has access to.": "您确定要删除这个群组吗?成员将失去对该群组可访问资源的访问权限。",
|
||||
"Are you sure you want to delete this page?": "您确定要删除这个页面吗?",
|
||||
@@ -44,24 +45,24 @@
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "您确定要删除这个页面吗?这将删除其子页面和页面历史记录。此操作不可逆。",
|
||||
"Description": "描述",
|
||||
"Details": "详情",
|
||||
"e.g ACME": "例如:ACME",
|
||||
"e.g ACME Inc": "例如:ACME Inc",
|
||||
"e.g Developers": "例如:开发人员",
|
||||
"e.g Group for developers": "例如:开发人员群组",
|
||||
"e.g product": "例如:product",
|
||||
"e.g Product Team": "例如:产品团队",
|
||||
"e.g Sales": "例如:销售",
|
||||
"e.g Space for product team": "例如:产品团队的空间",
|
||||
"e.g Space for sales team to collaborate": "例如:销售团队协作的空间",
|
||||
"e.g ACME": "例如 ACME",
|
||||
"e.g ACME Inc": "例如 ACME Inc",
|
||||
"e.g Developers": "例如 Developers",
|
||||
"e.g Group for developers": "例如 开发者小组",
|
||||
"e.g product": "例如 product",
|
||||
"e.g Product Team": "例如 产品团队",
|
||||
"e.g Sales": "例如 销售",
|
||||
"e.g Space for product team": "例如 产品团队空间",
|
||||
"e.g Space for sales team to collaborate": "例如 供销售团队协作的空间",
|
||||
"Edit": "编辑",
|
||||
"Read": "阅读",
|
||||
"Read": "读取",
|
||||
"Edit group": "编辑群组",
|
||||
"Email": "电子邮箱",
|
||||
"Enter a strong password": "输入一个强密码",
|
||||
"Enter valid email addresses separated by comma or space max_50": "输入有效的电子邮箱地址,用逗号或空格分隔 [最多:50个]",
|
||||
"enter valid emails addresses": "输入有效的电子邮箱地址",
|
||||
"enter valid emails addresses": "请输入有效的电子邮箱地址",
|
||||
"Enter your current password": "输入您的当前密码",
|
||||
"enter your full name": "输入您的全名",
|
||||
"enter your full name": "请输入您的全名",
|
||||
"Enter your new password": "输入您的新密码",
|
||||
"Enter your new preferred email": "输入您新的首选电子邮箱",
|
||||
"Enter your password": "输入您的密码",
|
||||
@@ -74,6 +75,9 @@
|
||||
"Failed to import pages": "导入页面失败",
|
||||
"Failed to load page. An error occurred.": "页面加载失败。发生了一个错误。",
|
||||
"Failed to update data": "数据更新失败",
|
||||
"Favorite spaces": "收藏的空间",
|
||||
"Favorite spaces appear here": "收藏的空间会显示在这里",
|
||||
"Favorites": "收藏",
|
||||
"Full access": "完全访问",
|
||||
"Full page width": "全页宽度",
|
||||
"Full width": "全宽",
|
||||
@@ -92,6 +96,7 @@
|
||||
"Invite by email": "通过电子邮箱邀请",
|
||||
"Invite members": "邀请成员",
|
||||
"Invite new members": "邀请新成员",
|
||||
"Invite People": "邀请成员",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "尚未接受邀请的成员将显示在这里。",
|
||||
"Invited members will be granted access to spaces the groups can access": "被邀请的成员将被授予访问群组可以访问的空间的权限",
|
||||
"Join the workspace": "加入工作空间",
|
||||
@@ -116,6 +121,7 @@
|
||||
"No group found": "未找到群组",
|
||||
"No page history saved yet.": "尚未保存页面历史。",
|
||||
"No pages yet": "暂无页面",
|
||||
"No shared pages": "没有共享页面",
|
||||
"No results found...": "未找到结果...",
|
||||
"No user found": "未找到用户",
|
||||
"Overview": "概览",
|
||||
@@ -130,6 +136,7 @@
|
||||
"pages": "个页面",
|
||||
"Password": "密码",
|
||||
"Password changed successfully": "密码更改成功",
|
||||
"People": "人员",
|
||||
"Pending": "待定",
|
||||
"Please confirm your action": "请确认您的操作",
|
||||
"Preferences": "偏好设置",
|
||||
@@ -137,6 +144,7 @@
|
||||
"Profile": "个人资料",
|
||||
"Recently updated": "最近更新",
|
||||
"Remove": "移除",
|
||||
"Remove from favorites": "从收藏中移除",
|
||||
"Remove group member": "移除群组成员",
|
||||
"Remove space member": "移除空间成员",
|
||||
"Restore": "恢复",
|
||||
@@ -149,53 +157,54 @@
|
||||
"Search...": "搜索...",
|
||||
"Select language": "选择语言",
|
||||
"Select role": "选择角色",
|
||||
"Select role to assign to all invited members": "选择要分配给所有被邀请成员的角色",
|
||||
"Select role to assign to all invited members": "选择要分配给所有受邀成员的角色",
|
||||
"Select theme": "选择主题",
|
||||
"Send invitation": "发送邀请",
|
||||
"Invitation sent": "邀请邮件已发送",
|
||||
"Invitation sent": "邀请已发送",
|
||||
"Settings": "设置",
|
||||
"Setup workspace": "设置工作空间",
|
||||
"Setup workspace": "设置工作区",
|
||||
"Sign In": "登录",
|
||||
"Sign Up": "注册",
|
||||
"Slug": "短链接",
|
||||
"Slug": "标识符",
|
||||
"Space": "空间",
|
||||
"Space description": "空间描述",
|
||||
"Space menu": "空间菜单",
|
||||
"Space name": "空间名称",
|
||||
"Space settings": "空间设置",
|
||||
"Space slug": "空间短链接",
|
||||
"Space slug": "空间标识符",
|
||||
"Spaces": "空间",
|
||||
"Spaces you belong to": "您所属的空间",
|
||||
"No space found": "未找到空间",
|
||||
"Search for spaces": "搜索空间",
|
||||
"Start typing to search...": "开始输入以搜索...",
|
||||
"Status": "状态",
|
||||
"Successfully imported": "成功导入",
|
||||
"Successfully imported": "导入成功",
|
||||
"Successfully restored": "恢复成功",
|
||||
"System settings": "系统设置",
|
||||
"Templates": "模板",
|
||||
"Theme": "主题",
|
||||
"To change your email, you have to enter your password and new email.": "要更改您的电子邮箱,您需要输入密码和新的电子邮箱地址。",
|
||||
"Toggle full page width": "切换全页宽度",
|
||||
"Toggle full page width": "切换整页宽度",
|
||||
"Unable to import pages. Please try again.": "无法导入页面。请重试。",
|
||||
"untitled": "无标题",
|
||||
"Untitled": "无标题",
|
||||
"untitled": "未命名",
|
||||
"Untitled": "未命名",
|
||||
"Updated successfully": "更新成功",
|
||||
"User": "用户",
|
||||
"Workspace": "工作区",
|
||||
"Workspace Name": "工作空间名称",
|
||||
"Workspace Name": "工作区名称",
|
||||
"Workspace settings": "工作区设置",
|
||||
"You can change your password here.": "您可以在这里更改密码。",
|
||||
"Your Email": "您的电子邮箱",
|
||||
"Your Email": "您的邮箱",
|
||||
"Your import is complete.": "导入已完成。",
|
||||
"Your name": "您的姓名",
|
||||
"Your Name": "您的姓名",
|
||||
"Your password": "您的密码",
|
||||
"Your password must be a minimum of 8 characters.": "您的密码必须至少包含8个字符。",
|
||||
"Sidebar toggle": "切换侧边栏",
|
||||
"Sidebar toggle": "侧边栏切换",
|
||||
"Comments": "评论",
|
||||
"404 page not found": "404 页面未找到",
|
||||
"Sorry, we can't find the page you are looking for.": "抱歉,我们无法找到你所需要的页面",
|
||||
"Take me back to homepage": "回到主页",
|
||||
"Take me back to homepage": "返回首页",
|
||||
"Forgot password": "忘记密码",
|
||||
"Forgot your password?": "忘记密码了吗?",
|
||||
"A password reset link has been sent to your email. Please check your inbox.": "密码重置链接已经发送到您的邮箱,请检查收件箱",
|
||||
@@ -207,9 +216,14 @@
|
||||
"Reply...": "回复...",
|
||||
"Error loading comments.": "加载评论时出错",
|
||||
"No comments yet.": "目前还没有评论",
|
||||
"No open comments.": "没有未解决的评论。",
|
||||
"No resolved comments.": "没有已解决的评论。",
|
||||
"Add a comment...": "添加评论...",
|
||||
"Edit comment": "编辑评论",
|
||||
"Delete comment": "删除评论",
|
||||
"Are you sure you want to delete this comment?": "你确定要删除这条评论吗?",
|
||||
"Delete chat": "删除聊天",
|
||||
"Are you sure you want to delete '{{title}}'? This action cannot be undone.": "您确定要删除「{{title}}」吗?此操作无法撤销。",
|
||||
"Comment created successfully": "成功创建评论",
|
||||
"Error creating comment": "创建评论时出错",
|
||||
"Comment updated successfully": "评论更新成功",
|
||||
@@ -217,8 +231,8 @@
|
||||
"Comment deleted successfully": "成功删除评论",
|
||||
"Failed to delete comment": "删除评论失败",
|
||||
"Comment resolved successfully": "成功标记评论为解决",
|
||||
"Comment re-opened successfully": "成功重新打开评论",
|
||||
"Comment unresolved successfully": "成功标记评论为未解决",
|
||||
"Comment re-opened successfully": "评论重新打开成功",
|
||||
"Comment unresolved successfully": "评论已成功取消解决",
|
||||
"Failed to resolve comment": "标记评论为解决失败",
|
||||
"Resolve comment": "解决评论",
|
||||
"Unresolve comment": "取消解决评论",
|
||||
@@ -228,7 +242,6 @@
|
||||
"Are you sure you want to unresolve this comment thread?": "确定要取消解决此评论线程吗?",
|
||||
"Resolved": "已解决",
|
||||
"No active comments.": "没有活跃的评论。",
|
||||
"No resolved comments.": "没有已解决的评论。",
|
||||
"Revoke invitation": "撤回邀请",
|
||||
"Revoke": "撤销",
|
||||
"Don't": "不要",
|
||||
@@ -239,7 +252,7 @@
|
||||
"Copy": "复制",
|
||||
"Copy to space": "复制到空间",
|
||||
"Copied": "已复制",
|
||||
"Duplicate": "重复",
|
||||
"Duplicate": "复制",
|
||||
"Select a user": "选择一个用户",
|
||||
"Select a group": "选择一个组",
|
||||
"Export all pages and attachments in this space.": "导出当前空间的所有页面和附件",
|
||||
@@ -274,6 +287,7 @@
|
||||
"Add row below": "在下方插入行",
|
||||
"Delete table": "删除表格",
|
||||
"Info": "信息",
|
||||
"Note": "注意",
|
||||
"Success": "成功",
|
||||
"Warning": "警告",
|
||||
"Danger": "危险",
|
||||
@@ -284,6 +298,11 @@
|
||||
"Save & Exit": "保存并退出",
|
||||
"Double-click to edit Excalidraw diagram": "双击以编辑 Excalidraw 图表",
|
||||
"Paste link": "粘贴链接",
|
||||
"Paste link or search pages": "粘贴链接或搜索页面",
|
||||
"Link to web page": "链接到网页",
|
||||
"Recents": "最近使用",
|
||||
"Page or URL": "页面或网址",
|
||||
"Link title": "链接标题",
|
||||
"Edit link": "编辑链接",
|
||||
"Remove link": "移除链接",
|
||||
"Add link": "添加链接",
|
||||
@@ -331,6 +350,7 @@
|
||||
"Insert horizontal rule divider": "插入水平分割线",
|
||||
"Upload any image from your device.": "从设备上传任何图像",
|
||||
"Upload any video from your device.": "从设备上传任何视频",
|
||||
"Upload any audio from your device.": "从您的设备上传任意音频文件。",
|
||||
"Upload any file from your device.": "从设备上传任何文件",
|
||||
"Uploading {{name}}": "正在上传{{name}}",
|
||||
"Uploading file": "正在上传文件",
|
||||
@@ -340,10 +360,16 @@
|
||||
"Video": "视频",
|
||||
"Divider": "分割线",
|
||||
"Quote": "引用",
|
||||
"Image": "图像",
|
||||
"Image": "图片",
|
||||
"Audio": "音频",
|
||||
"Embed PDF": "嵌入 PDF",
|
||||
"Upload and embed a PDF file.": "上传并嵌入 PDF 文件。",
|
||||
"Embed as PDF": "作为 PDF 嵌入",
|
||||
"Failed to load PDF": "加载 PDF 失败",
|
||||
"Convert to attachment": "转换为附件",
|
||||
"File attachment": "文件附件",
|
||||
"Toggle block": "切换块",
|
||||
"Callout": "标注块",
|
||||
"Toggle block": "折叠块",
|
||||
"Callout": "提示块",
|
||||
"Insert callout notice.": "插入标注提示块",
|
||||
"Math inline": "行内公式",
|
||||
"Insert inline math equation.": "插入行内公式",
|
||||
@@ -351,35 +377,56 @@
|
||||
"Insert math equation": "插入数学公式",
|
||||
"Mermaid diagram": "Mermaid 图表",
|
||||
"Insert mermaid diagram": "插入 Mermaid 图表",
|
||||
"Insert and design Drawio diagrams": "插入并设计 Draw.io 图表",
|
||||
"Insert and design Drawio diagrams": "插入并设计 Drawio 图表",
|
||||
"Insert current date": "插入当前日期",
|
||||
"Draw and sketch excalidraw diagrams": "绘制 Excalidraw 图表",
|
||||
"Draw and sketch excalidraw diagrams": "绘制和草绘 Excalidraw 图表",
|
||||
"Multiple": "多个",
|
||||
"Heading {{level}}": "{{level}} 级标题",
|
||||
"Toggle title": "切换标题",
|
||||
"Write anything. Enter \"/\" for commands": "开始编写内容,输入 \"/\" 以使用指令",
|
||||
"Turn into": "变成",
|
||||
"Text align": "文本对齐",
|
||||
"This page may have been deleted, moved, or you may not have access.": "此页面可能已被删除、移动,或者您可能无权访问。{",
|
||||
"Go to homepage": "前往首页",
|
||||
"Pages you create will show up here.": "您创建的页面将显示在此处。",
|
||||
"Heading {{level}}": "标题 {{level}}",
|
||||
"Toggle title": "折叠标题",
|
||||
"Write anything. Enter \"/\" for commands": "输入任意内容。输入“/”查看命令",
|
||||
"Write...": "写点内容...",
|
||||
"Column count": "列数",
|
||||
"{{count}} Columns": "{{count}} 列",
|
||||
"Equal columns": "等宽列",
|
||||
"Left sidebar": "左侧边栏",
|
||||
"Right sidebar": "右侧边栏",
|
||||
"Wide center": "中间加宽",
|
||||
"Left wide": "左侧加宽",
|
||||
"Right wide": "右侧加宽",
|
||||
"Names do not match": "名称不匹配",
|
||||
"Today, {{time}}": "今天,{{time}}",
|
||||
"Yesterday, {{time}}": "昨天,{{time}}",
|
||||
"Space created successfully": "空间创建成功",
|
||||
"Space updated successfully": "空间更新成功",
|
||||
"Space deleted successfully": "空间已成功删除",
|
||||
"Space deleted successfully": "空间删除成功",
|
||||
"Members added successfully": "成员添加成功",
|
||||
"Member removed successfully": "成员移除成功",
|
||||
"Member role updated successfully": "成员角色更新成功",
|
||||
"Created by: <b>{{creatorName}}</b>": "创建者:<b>{{creatorName}}</b>",
|
||||
"Created at: {{time}}": "创建于:{{time}}",
|
||||
"Edited by {{name}} {{time}}": "由{{name}} 编辑于 {{time}}",
|
||||
"Edited by {{name}} {{time}}": "由 {{name}} 编辑于 {{time}}",
|
||||
"Word count: {{wordCount}}": "字数:{{wordCount}}",
|
||||
"Character count: {{characterCount}}": "字符数:{{characterCount}}",
|
||||
"New update": "新更新",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} 已经可以使用",
|
||||
"{{latestVersion}} is available": "{{latestVersion}} 可用",
|
||||
"Default page edit mode": "默认页面编辑模式",
|
||||
"Choose your preferred page edit mode. Avoid accidental edits.": "选择您偏好的页面编辑模式。避免意外编辑。",
|
||||
"Reading": "阅读",
|
||||
"Delete member": "删除成员",
|
||||
"Member deleted successfully": "成员删除成功",
|
||||
"Are you sure you want to delete this workspace member? This action is irreversible.": "您确定要删除此工作区成员吗?此操作不可逆。",
|
||||
"Deactivate member": "停用成员",
|
||||
"Activate member": "激活成员",
|
||||
"Are you sure you want to deactivate this workspace member? They will no longer be able to access this workspace.": "您确定要停用此工作区成员吗?该成员将无法再访问此工作区。",
|
||||
"Are you sure you want to activate this workspace member?": "您确定要激活此工作区成员吗?",
|
||||
"Deactivate": "停用",
|
||||
"Activate": "激活",
|
||||
"Deactivated": "已停用",
|
||||
"Move": "移动",
|
||||
"Move page": "移动页面",
|
||||
"Move page to a different space.": "将页面移动到不同的空间。",
|
||||
@@ -389,124 +436,144 @@
|
||||
"Share": "分享",
|
||||
"Public sharing": "公开分享",
|
||||
"Shared by": "分享者",
|
||||
"Shared at": "分享时间",
|
||||
"Inherits public sharing from": "继承自的公开分享",
|
||||
"Shared at": "分享于",
|
||||
"Inherits public sharing from": "继承公开分享自",
|
||||
"Share to web": "分享到网页",
|
||||
"Shared to web": "已分享到网页",
|
||||
"Anyone with the link can view this page": "任何有链接的人都可以查看此页面",
|
||||
"Make this page publicly accessible": "使此页面可公开访问",
|
||||
"Include sub-pages": "包括子页面",
|
||||
"Make sub-pages public too": "将子页面也设为公开",
|
||||
"Anyone with the link can view this page": "任何拥有链接的人都可以查看此页面",
|
||||
"Make this page publicly accessible": "将此页面设为公开可访问",
|
||||
"Include sub-pages": "包含子页面",
|
||||
"Make sub-pages public too": "同时将子页面设为公开",
|
||||
"Allow search engines to index page": "允许搜索引擎索引页面",
|
||||
"Open page": "打开页面",
|
||||
"Page": "页面",
|
||||
"Delete public share link": "删除公开分享链接",
|
||||
"Delete share": "删除分享",
|
||||
"Are you sure you want to delete this shared link?": "您确定要删除此分享链接吗?",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "您所在空间的公开共享页面会显示在此处",
|
||||
"Share deleted successfully": "分享已成功删除",
|
||||
"Publicly shared pages from spaces you are a member of will appear here": "您所属空间中公开分享的页面将显示在这里",
|
||||
"Share deleted successfully": "分享删除成功",
|
||||
"Share not found": "未找到分享",
|
||||
"Failed to share page": "页面分享失败",
|
||||
"Disable public sharing": "禁用公开分享",
|
||||
"Prevent members from sharing pages publicly.": "阻止成员公开分享页面。",
|
||||
"Toggle public sharing": "切换公开分享",
|
||||
"Toggle space public sharing": "切换空间公开分享",
|
||||
"Allow viewers to comment": "允许观众评论",
|
||||
"Allow viewers to add comments on pages in this space.": "允许观众在此空间的页面上添加评论。",
|
||||
"Toggle viewer comments": "切换观众评论",
|
||||
"Public sharing is disabled at the workspace level": "公开分享在工作区级别被禁用",
|
||||
"Prevent pages in this space from being shared publicly.": "阻止此空间中的页面被公开分享。",
|
||||
"Page permissions": "页面权限},{",
|
||||
"Control who can view and edit individual pages. Available with an enterprise license.": "控制谁可以查看和编辑单个页面。此功能在企业版许可下可用。",
|
||||
"Enable public sharing": "启用公开分享",
|
||||
"Are you sure you want to enable public sharing? Members will be able to share pages publicly.": "您确定要启用公开分享吗?成员将能够公开分享页面。",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this workspace will be deleted.": "您确定要禁用公开分享吗?此工作区中的所有现有共享链接都将被删除。",
|
||||
"Are you sure you want to enable public sharing for this space?": "您确定要为此空间启用公开分享吗?",
|
||||
"Are you sure you want to disable public sharing? All existing shared links in this space will be deleted.": "您确定要禁用公开分享吗?此空间中的所有现有共享链接都将被删除。",
|
||||
"Public sharing is disabled": "公开分享已被禁用",
|
||||
"Public sharing has been disabled at the workspace level.": "公开分享已在工作区级别被禁用。",
|
||||
"Public sharing has been disabled for this space.": "此空间的公开分享已被禁用。",
|
||||
"Copy page": "复制页面",
|
||||
"Copy page to a different space.": "将页面复制到不同的空间。",
|
||||
"Page copied successfully": "页面复制成功",
|
||||
"Page duplicated successfully": "页面复制成功",
|
||||
"Page duplicated successfully": "页面副本创建成功",
|
||||
"Find": "查找",
|
||||
"Not found": "未找到",
|
||||
"Previous Match (Shift+Enter)": "上一个匹配 (Shift+Enter)",
|
||||
"Next match (Enter)": "下一个匹配 (Enter)",
|
||||
"Match case (Alt+C)": "区分大小写 (Alt+C)",
|
||||
"Previous Match (Shift+Enter)": "上一个匹配项(Shift+Enter)",
|
||||
"Next match (Enter)": "下一个匹配项(Enter)",
|
||||
"Match case (Alt+C)": "区分大小写(Alt+C)",
|
||||
"Replace": "替换",
|
||||
"Close (Escape)": "关闭 (Escape)",
|
||||
"Replace (Enter)": "替换 (Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "全部替换 (Ctrl+Alt+Enter)",
|
||||
"Close (Escape)": "关闭(Escape)",
|
||||
"Replace (Enter)": "替换(Enter)",
|
||||
"Replace all (Ctrl+Alt+Enter)": "全部替换(Ctrl+Alt+Enter)",
|
||||
"Replace all": "全部替换",
|
||||
"View all": "查看全部",
|
||||
"View all spaces": "查看所有空间",
|
||||
"Error": "错误",
|
||||
"Failed to disable MFA": "停用 MFA 失败",
|
||||
"Disable two-factor authentication": "停用双因素认证",
|
||||
"Failed to disable MFA": "禁用 MFA 失败",
|
||||
"Disable two-factor authentication": "禁用双重身份验证",
|
||||
"Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "停用双因素认证会降低账户安全性。您只需密码即可登录。",
|
||||
"Please enter your password to disable two-factor authentication:": "请输入您的密码以停用双因素认证:",
|
||||
"Two-factor authentication has been enabled": "双因素认证已启用",
|
||||
"Two-factor authentication has been disabled": "双因素认证已停用",
|
||||
"Two-factor authentication has been enabled": "双重身份验证已启用",
|
||||
"Two-factor authentication has been disabled": "双重身份验证已禁用",
|
||||
"2-step verification": "两步验证",
|
||||
"Protect your account with an additional verification layer when signing in.": "通过额外的验证层保护您的账户安全。",
|
||||
"Two-factor authentication is active on your account.": "您的账户已激活双因素认证。",
|
||||
"Add 2FA method": "添加 2FA 方法",
|
||||
"Backup codes": "备份代码",
|
||||
"Disable": "停用",
|
||||
"Add 2FA method": "添加 2FA 方式",
|
||||
"Backup codes": "备用代码",
|
||||
"Disable": "禁用",
|
||||
"Invalid verification code": "无效的验证码",
|
||||
"New backup codes have been generated": "已生成新的备份代码",
|
||||
"Failed to regenerate backup codes": "重新生成备份代码失败",
|
||||
"About backup codes": "关于备份代码",
|
||||
"New backup codes have been generated": "新的备用代码已生成",
|
||||
"Failed to regenerate backup codes": "重新生成备用代码失败",
|
||||
"About backup codes": "关于备用代码",
|
||||
"Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "如果您无法访问身份验证器应用,可使用备份代码访问账户。每个代码仅可使用一次。",
|
||||
"You can regenerate new backup codes at any time. This will invalidate all existing codes.": "您可以随时重新生成新的备份代码。这将使所有现有代码失效。",
|
||||
"Confirm password": "确认密码",
|
||||
"Generate new backup codes": "生成新的备份代码",
|
||||
"Save your new backup codes": "保存您的新备份代码",
|
||||
"Generate new backup codes": "生成新的备用代码",
|
||||
"Save your new backup codes": "保存您的新备用代码",
|
||||
"Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "请确保将这些代码保存在安全的地方。您的旧备份代码不再有效。",
|
||||
"Your new backup codes": "您的新备份代码",
|
||||
"I've saved my backup codes": "我已经保存了我的备份代码",
|
||||
"Your new backup codes": "您的新备用代码",
|
||||
"I've saved my backup codes": "我已保存备用代码",
|
||||
"Failed to setup MFA": "设置 MFA 失败",
|
||||
"Setup & Verify": "设置并验证",
|
||||
"Add to authenticator": "添加到身份验证器",
|
||||
"1. Scan this QR code with your authenticator app": "1. 用身份验证器应用扫描此二维码",
|
||||
"1. Scan this QR code with your authenticator app": "1. 使用您的身份验证器应用扫描此二维码",
|
||||
"Can't scan the code?": "无法扫描代码?",
|
||||
"Enter this code manually in your authenticator app:": "在您的身份验证器应用中手动输入此代码:",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. 输入来自身份验证器的6位代码",
|
||||
"2. Enter the 6-digit code from your authenticator": "2. 输入您的身份验证器中的 6 位代码",
|
||||
"Verify and enable": "验证并启用",
|
||||
"Failed to generate QR code. Please try again.": "生成二维码失败。请重试。",
|
||||
"Backup": "备份",
|
||||
"Backup": "备用",
|
||||
"Save codes": "保存代码",
|
||||
"Save your backup codes": "保存您的备份代码",
|
||||
"Save your backup codes": "保存您的备用代码",
|
||||
"These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "如果无法访问身份验证器应用,可以使用这些代码访问账户。每个代码仅可使用一次。",
|
||||
"Print": "打印",
|
||||
"Two-factor authentication has been set up. Please log in again.": "双因素认证已设置。请重新登录。",
|
||||
"Two-Factor authentication required": "需要双因素认证",
|
||||
"Your workspace requires two-factor authentication for all users": "您的工作区要求所有用户启用双因素认证",
|
||||
"Two-Factor authentication required": "需要双重身份验证",
|
||||
"Your workspace requires two-factor authentication for all users": "您的工作区要求所有用户启用双重身份验证",
|
||||
"To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "要继续访问工作区,必须设置双因素认证。此操作为您的账户添加一层额外的安全保障。",
|
||||
"Set up two-factor authentication": "设置双因素认证",
|
||||
"Set up two-factor authentication": "设置双重身份验证",
|
||||
"Cancel and logout": "取消并退出登录",
|
||||
"Your workspace requires two-factor authentication. Please set it up to continue.": "您的工作区需要双因素认证。请设置以继续。",
|
||||
"This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "通过要求您的身份验证器应用提供验证码,此操作为您的账户增加了一层额外的安全保障。",
|
||||
"Password is required": "需要密码",
|
||||
"Password must be at least 8 characters": "密码必须至少包含8个字符",
|
||||
"Please enter a 6-digit code": "请输入6位代码",
|
||||
"Code must be exactly 6 digits": "代码必须正好是6位",
|
||||
"Enter the 6-digit code found in your authenticator app": "输入在您的身份验证器应用中找到的6位代码",
|
||||
"Password is required": "密码为必填项",
|
||||
"Password must be at least 8 characters": "密码长度至少为 8 个字符",
|
||||
"Please enter a 6-digit code": "请输入 6 位代码",
|
||||
"Code must be exactly 6 digits": "代码必须正好为 6 位",
|
||||
"Enter the 6-digit code found in your authenticator app": "输入您身份验证器应用中的 6 位代码",
|
||||
"Need help authenticating?": "需要帮助进行身份验证吗?",
|
||||
"MFA QR Code": "MFA二维码",
|
||||
"MFA QR Code": "MFA 二维码",
|
||||
"Account created successfully. Please log in to set up two-factor authentication.": "账户创建成功。请登录以设置双因素认证。",
|
||||
"Password reset successful. Please log in with your new password and complete two-factor authentication.": "密码重置成功。请使用新密码登录并完成双因素认证。",
|
||||
"Password reset successful. Please log in with your new password to set up two-factor authentication.": "密码重置成功。请使用新密码登录以设置双因素认证。",
|
||||
"Password reset was successful. Please log in with your new password.": "密码重置成功。请使用新密码登录。",
|
||||
"Two-factor authentication": "双因素认证",
|
||||
"Two-factor authentication": "双重身份验证",
|
||||
"Use authenticator app instead": "改用身份验证器应用",
|
||||
"Verify backup code": "验证备份代码",
|
||||
"Use backup code": "使用备份代码",
|
||||
"Enter one of your backup codes": "输入您的一个备份代码",
|
||||
"Backup code": "备份代码",
|
||||
"Verify backup code": "验证备用代码",
|
||||
"Use backup code": "使用备用代码",
|
||||
"Enter one of your backup codes": "输入您的一个备用代码",
|
||||
"Backup code": "备用代码",
|
||||
"Enter one of your backup codes. Each backup code can only be used once.": "输入您的一个备份代码。每个备份代码只能使用一次。",
|
||||
"Verify": "验证",
|
||||
"Trash": "垃圾箱",
|
||||
"Pages in trash will be permanently deleted after 30 days.": "垃圾箱中的页面将在30天后被永久删除。",
|
||||
"Trash": "回收站",
|
||||
"Pages in trash will be permanently deleted after {{count}} days.": "垃圾箱中的页面将在{{count}}天后被永久删除。",
|
||||
"Deleted": "已删除",
|
||||
"No pages in trash": "垃圾箱中没有页面",
|
||||
"No pages in trash": "回收站中没有页面",
|
||||
"Permanently delete page?": "永久删除页面?",
|
||||
"Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "确定要永久删除“{{title}}”吗?此操作无法撤销。",
|
||||
"Restore '{{title}}' and its sub-pages?": "恢复“{{title}}”及其子页面?",
|
||||
"Move to trash": "移至垃圾箱",
|
||||
"Move to trash": "移至回收站",
|
||||
"Move this page to trash?": "将此页面移至垃圾箱?",
|
||||
"Restore page": "恢复页面",
|
||||
"Page moved to trash": "页面已移至垃圾箱",
|
||||
"Page moved to trash": "页面已移至回收站",
|
||||
"Page restored successfully": "页面恢复成功",
|
||||
"Deleted by": "删除人",
|
||||
"Deleted at": "删除时间",
|
||||
"Deleted by": "删除者",
|
||||
"Deleted at": "删除于",
|
||||
"Preview": "预览",
|
||||
"Subpages": "子页面",
|
||||
"Failed to load subpages": "加载子页面失败",
|
||||
"No subpages": "没有子页面",
|
||||
"Subpages (Child pages)": "子页面(子页面)",
|
||||
"Subpages (Child pages)": "子页面(下级页面)",
|
||||
"List all subpages of the current page": "列出当前页面的所有子页面",
|
||||
"Attachments": "附件",
|
||||
"All spaces": "所有空间",
|
||||
@@ -514,23 +581,23 @@
|
||||
"Find a space": "查找空间",
|
||||
"Search in all your spaces": "在您的所有空间中搜索",
|
||||
"Type": "类型",
|
||||
"Enterprise": "企业",
|
||||
"Enterprise": "企业版",
|
||||
"Download attachment": "下载附件",
|
||||
"Allowed email domains": "允许的电子邮件域",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "只有来自这些域的电子邮件地址的用户才能通过SSO注册。",
|
||||
"Enter valid domain names separated by comma or space": "输入用逗号或空格分隔的有效域名",
|
||||
"Enforce two-factor authentication": "强制实施双因素认证",
|
||||
"Allowed email domains": "允许的邮箱域名",
|
||||
"Only users with email addresses from these domains can signup via SSO.": "只有使用这些域名邮箱地址的用户才能通过 SSO 注册。",
|
||||
"Enter valid domain names separated by comma or space": "请输入有效的域名,并用逗号或空格分隔",
|
||||
"Enforce two-factor authentication": "强制启用双重身份验证",
|
||||
"Once enforced, all members must enable two-factor authentication to access the workspace.": "一旦实施,所有成员必须启用双因素认证才能访问工作区。",
|
||||
"Toggle MFA enforcement": "切换多因素认证实施",
|
||||
"Toggle MFA enforcement": "切换 MFA 强制执行",
|
||||
"Display name": "显示名称",
|
||||
"Allow signup": "允许注册",
|
||||
"Enabled": "已启用",
|
||||
"Advanced Settings": "高级设置",
|
||||
"Enable TLS/SSL": "启用TLS/SSL",
|
||||
"Use secure connection to LDAP server": "使用安全连接到LDAP服务器",
|
||||
"Group sync": "组同步",
|
||||
"Enable TLS/SSL": "启用 TLS/SSL",
|
||||
"Use secure connection to LDAP server": "使用安全连接访问 LDAP 服务器",
|
||||
"Group sync": "群组同步",
|
||||
"No SSO providers found.": "未找到SSO提供商。",
|
||||
"Delete SSO provider": "删除SSO提供商",
|
||||
"Delete SSO provider": "删除 SSO 提供商",
|
||||
"Are you sure you want to delete this SSO provider?": "您确定要删除此SSO提供商吗?",
|
||||
"Action": "操作",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} 配置",
|
||||
@@ -561,19 +628,257 @@
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "此操作无法撤销。使用此API密钥的任何应用程序将停止工作。",
|
||||
"Update API key": "更新API密钥",
|
||||
"Manage API keys for all users in the workspace": "管理工作空间中所有用户的API密钥",
|
||||
"Restrict API key creation to admins": "仅限管理员创建 API 密钥。",
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "只有管理员和所有者可以创建新的 API 密钥。现有成员密钥将继续有效。",
|
||||
"Toggle restrict API keys to admins": "切换仅限管理员创建 API 密钥",
|
||||
"API key creation is restricted to admins by your workspace administrator.": "API 密钥的创建已被您的工作区管理员限制为仅管理员可用。",
|
||||
"AI settings": "AI设置",
|
||||
"AI search": "AI搜索",
|
||||
"AI Answer": "AI回答",
|
||||
"Ask AI": "询问AI",
|
||||
"AI is thinking...": "AI正在思考...",
|
||||
"Thinking": "思考中",
|
||||
"Ask a question...": "提问...",
|
||||
"AI-powered search (Ask AI)": "AI驱动的搜索(询问AI)",
|
||||
"AI Answers": "AI答案",
|
||||
"AI-powered search (AI Answers)": "AI驱动的搜索 (AI答案)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI搜索使用向量嵌入提供跨工作空间内容的语义搜索功能。",
|
||||
"Toggle AI search": "切换AI搜索",
|
||||
"Generative AI (Ask AI)": "生成型AI (询问AI)",
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "在编辑器中启用AI驱动的内容生成。允许用户生成、改进、翻译和转换文本。",
|
||||
"Toggle generative AI": "切换生成型AI",
|
||||
"Upgrade your plan": "升级您的方案",
|
||||
"Available with a paid license": "需付费许可才可用",
|
||||
"Upgrade your license tier.": "升级您的许可等级。",
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI 仅在 Docmost 企业版中提供。请联系 sales@docmost.com。",
|
||||
"AI & MCP": "AI 与 MCP",
|
||||
"AI": "AI",
|
||||
"MCP": "MCP",
|
||||
"Model Context Protocol (MCP)": "模型上下文协议(MCP)",
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "启用 MCP 服务器以允许 AI 助手和工具与您的工作区内容交互。",
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP 仅在 Docmost 企业版中提供。请联系 sales@docmost.com。",
|
||||
"MCP Server URL": "MCP 服务器 URL",
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.": "使用您的 API 密钥进行身份验证。您可以在账户设置中管理 API 密钥。",
|
||||
"Supported tools": "支持的工具",
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.": "您的工作区已启用 MCP。使用您的 API 密钥连接 AI 助手。",
|
||||
"MCP server URL:": "MCP 服务器 URL:",
|
||||
"Learn more": "了解更多",
|
||||
"Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "为工作区内所有用户管理 API 密钥。有关使用详情,请查阅<anchor>API 文档</anchor>。",
|
||||
"View the <anchor>API documentation</anchor> for usage details.": "有关使用详情,请查阅<anchor>API 文档</anchor>。",
|
||||
"View the <anchor>MCP documentation</anchor>.": "查看<anchor>MCP 文档</anchor>。",
|
||||
"Sources": "来源",
|
||||
"Ask AI not available for attachments": "附件不支持询问AI",
|
||||
"AI Answers not available for attachments": "AI答案不适用于附件",
|
||||
"No answer available": "无可用答案",
|
||||
"Background color": "背景颜色",
|
||||
"Highlight color": "突出显示颜色",
|
||||
"Remove color": "移除颜色"
|
||||
"Remove color": "移除颜色",
|
||||
"Notifications": "通知",
|
||||
"No notifications": "没有通知",
|
||||
"No unread notifications": "没有未读通知",
|
||||
"All notifications": "所有通知",
|
||||
"Unread only": "仅未读",
|
||||
"Mark all as read": "标记所有为已读",
|
||||
"Mark as read": "标记为已读",
|
||||
"More options": "更多选项",
|
||||
"<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold>在评论中提到你",
|
||||
"<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold>在页面上评论了",
|
||||
"<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> 解决了一条评论",
|
||||
"<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> 在某个页面中提到了您",
|
||||
"<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> 授予您某个页面的编辑权限",
|
||||
"<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> 授予您某个页面的查看权限",
|
||||
"<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> 更新了某个页面",
|
||||
"Watch page": "关注页面",
|
||||
"Stop watching": "取消关注",
|
||||
"Watch space": "关注空间",
|
||||
"Stop watching space": "取消关注空间",
|
||||
"Email notifications": "邮件通知",
|
||||
"Page updates": "页面更新",
|
||||
"Get notified when pages you watch are updated.": "当你关注的页面有更新时收到通知。",
|
||||
"Page mentions": "页面提及",
|
||||
"Get notified when someone mentions you on a page.": "当有人在页面上提到你时收到通知。",
|
||||
"Comment mentions": "评论提及",
|
||||
"Get notified when someone mentions you in a comment.": "当有人在评论中提到你时收到通知。",
|
||||
"New comments": "新评论",
|
||||
"Get notified about new comments on threads you participate in.": "当你参与的讨论有新评论时收到通知。",
|
||||
"Resolved comments": "已解决的评论",
|
||||
"Get notified when your comment is resolved.": "当你的评论被解决时收到通知。",
|
||||
"You are now watching this page": "你现在正在关注此页面",
|
||||
"You are no longer watching this page": "你已取消关注此页面",
|
||||
"You are now watching this space": "您现在正在关注此空间",
|
||||
"You are no longer watching this space": "您已不再关注此空间",
|
||||
"Direct": "直接",
|
||||
"Updates": "更新",
|
||||
"Today": "今天",
|
||||
"Yesterday": "昨天",
|
||||
"This week": "本周",
|
||||
"Older": "较早",
|
||||
"Restricted page": "受限页面",
|
||||
"Restricted pages cannot be shared publicly.": "受限页面不能公开共享。",
|
||||
"Restricted by parent": "受父页面限制",
|
||||
"Restricted": "受限",
|
||||
"Open": "公开",
|
||||
"Inherits restrictions from ancestor page": "继承自上级页面的限制",
|
||||
"Only people listed below can access this page": "只有下面列出的人可以访问此页面",
|
||||
"Everyone in this space can access": "此空间中的所有人均可访问",
|
||||
"No additional restrictions on this page": "此页面无额外限制",
|
||||
"Only specific people can access": "仅特定人员可访问",
|
||||
"Use only inherited restrictions": "仅使用继承的限制",
|
||||
"Add restrictions on top of inherited": "在继承的限制之上添加限制",
|
||||
"Inherited restriction": "继承的限制",
|
||||
"Access limited by": "访问受限于",
|
||||
"Restrict access to control who can view and edit this page": "限制访问以控制谁可以查看和编辑此页面",
|
||||
"Add additional restrictions specific to this page": "为此页面添加额外的特定限制",
|
||||
"Access": "访问",
|
||||
"People with access": "有访问权限的人员",
|
||||
"Remove all": "全部移除",
|
||||
"Remove access": "移除访问权限",
|
||||
"Remove all access": "移除所有访问权限",
|
||||
"Are you sure you want to remove this member's access to the page?": "您确定要移除此成员对该页面的访问权限吗?",
|
||||
"Are you sure you want to remove all specific access? This will make the page open to everyone in the space.": "您确定要删除所有特定访问权限吗?这将使该页面对该空间中的所有人开放。",
|
||||
"Trash retention": "垃圾箱保留期",
|
||||
"Pages in trash will be permanently deleted after this period.": "该期限结束后,垃圾箱中的页面将被永久删除。",
|
||||
"Trash retention updated": "垃圾箱保留期已更新",
|
||||
"Failed to update trash retention": "更新垃圾箱保留期失败",
|
||||
"Removed page restriction": "已移除页面限制",
|
||||
"Added page permission": "已添加页面权限",
|
||||
"Removed page permission": "已移除页面权限",
|
||||
"day": "天",
|
||||
"days": "天",
|
||||
"week": "周",
|
||||
"weeks": "周",
|
||||
"month": "个月",
|
||||
"months": "个月",
|
||||
"year": "年",
|
||||
"years": "年",
|
||||
"Period": "周期",
|
||||
"Fixed date": "固定日期",
|
||||
"Indefinitely": "无限期",
|
||||
"Days": "天",
|
||||
"Weeks": "周",
|
||||
"Months": "个月",
|
||||
"Years": "年",
|
||||
"Pick a date": "选择日期",
|
||||
"Maximum is {{max}} {{unit}} for this unit": "此单位的最大值为 {{max}} {{unit}}",
|
||||
"Never expires. Verifiers can re-verify at any time.": "永不过期。验证者可随时重新验证。",
|
||||
"Verified": "已验证",
|
||||
"Review needed": "需要审核",
|
||||
"Verification expired": "验证已过期",
|
||||
"Draft": "草稿",
|
||||
"In Approval": "审批中",
|
||||
"In approval": "审批中",
|
||||
"Approved": "已批准",
|
||||
"Obsolete": "已作废",
|
||||
"Expiring": "即将过期",
|
||||
"Set up verification": "设置验证",
|
||||
"Verify page": "验证页面",
|
||||
"Page verification": "页面验证",
|
||||
"Add verification": "添加验证",
|
||||
"Edit verification": "编辑验证",
|
||||
"Search by title": "按标题搜索",
|
||||
"Choose how this page should stay accurate.": "选择此页面保持准确的方式。",
|
||||
"Recurring verification": "定期验证",
|
||||
"Verifiers re-confirm this page on a schedule.": "验证者按计划重新确认此页面。",
|
||||
"Re-verify on a schedule (e.g every 30 days )": "按计划重新验证(例如每 30 天一次)",
|
||||
"Page stays editable at all times": "页面始终可编辑",
|
||||
"Best for runbooks, FAQs, living documentation": "最适合运行手册、常见问题和动态文档",
|
||||
"Approval workflow": "审批工作流",
|
||||
"Formal document lifecycle with named approvers.": "具有指定审批人的正式文档生命周期。",
|
||||
"Draft → In approval → Approved → Obsolete": "草稿 → 审批中 → 已批准 → 已作废",
|
||||
"Locked once approved, with full history": "批准后锁定,并保留完整历史记录",
|
||||
"Designed for ISO 9001, ISO 13485, and FDA": "专为 ISO 9001、ISO 13485 和 FDA 设计",
|
||||
"Best for SOPs and controlled documents": "最适合 SOP 和受控文档",
|
||||
"Back": "返回",
|
||||
"Quality management": "质量管理",
|
||||
"Recurring": "定期",
|
||||
"Pages move through draft, approval, and approved stages.": "页面会经历草稿、审批中和已批准阶段。",
|
||||
"Verifiers": "验证者",
|
||||
"Add verifier": "添加验证者",
|
||||
"I've reviewed this page for accuracy": "我已审核此页面的准确性",
|
||||
"Set up": "设置",
|
||||
"Remove verification": "移除验证",
|
||||
"Are you sure you want to remove verification from this page?": "确定要移除此页面的验证吗?",
|
||||
"Assigned verifiers must periodically re-verify this page.": "指定的验证者必须定期重新验证此页面。",
|
||||
"Last verified by {{name}} {{time}} (expired)": "最后由 {{name}} 于 {{time}} 验证(已过期)",
|
||||
"The fixed expiration date has passed.": "固定到期日已过。",
|
||||
"Verified by {{name}} {{time}}": "由 {{name}} 于 {{time}} 验证",
|
||||
"Expires {{date}}": "于 {{date}} 到期",
|
||||
"Expired {{date}}": "已于 {{date}} 过期",
|
||||
"Mark as obsolete": "标记为作废",
|
||||
"Mark obsolete": "标记作废",
|
||||
"Returned by {{name}} {{time}}": "由 {{name}} 于 {{time}} 退回",
|
||||
"No approval has been requested yet.": "尚未请求审批。",
|
||||
"Submitted by {{name}} {{time}}": "由 {{name}} 于 {{time}} 提交",
|
||||
"Someone": "某人",
|
||||
"Approved by {{name}} {{time}}": "由 {{name}} 于 {{time}} 批准",
|
||||
"This document has been marked as obsolete.": "此文档已被标记为作废。",
|
||||
"Rejection comment": "退回意见",
|
||||
"Reason for returning this document...": "退回此文档的原因...",
|
||||
"Confirm rejection": "确认退回",
|
||||
"Submit for approval": "提交审批",
|
||||
"Reject": "退回",
|
||||
"Approve": "批准",
|
||||
"Re-submit for approval": "重新提交审批",
|
||||
"Verified until": "验证有效期至",
|
||||
"QMS": "QMS",
|
||||
"Verified pages": "已验证页面",
|
||||
"Search pages...": "搜索页面...",
|
||||
"Filter by space": "按空间筛选",
|
||||
"Filter by type": "按类型筛选",
|
||||
"<bold>{{name}}</bold> verified a page": "<bold>{{name}}</bold> 验证了一个页面",
|
||||
"<bold>{{name}}</bold> submitted a page for your approval": "<bold>{{name}}</bold> 提交了一个页面供您审批",
|
||||
"<bold>{{name}}</bold> returned a page for revision": "<bold>{{name}}</bold> 退回了一个页面以供修改",
|
||||
"Page verification expires soon": "页面验证即将过期",
|
||||
"Page verification has expired": "页面验证已过期",
|
||||
"Verifying your email": "正在验证您的邮箱",
|
||||
"Please wait...": "请稍候……",
|
||||
"Verification failed. The link may have expired.": "验证失败。该链接可能已过期。",
|
||||
"Check your email": "检查您的邮箱",
|
||||
"We sent a verification link to {{email}}.": "我们已向{{email}}发送了一封验证邮件。",
|
||||
"We sent a verification link to your email.": "我们已向您的邮箱发送了一封验证邮件。",
|
||||
"Click the link to verify your email and access your workspace.": "请点击链接以验证邮箱并访问您的工作区。",
|
||||
"Resend verification email": "重新发送验证邮件",
|
||||
"Verification email sent. Please check your inbox.": "验证邮件已发送。请检查您的收件箱。",
|
||||
"Failed to resend verification email. Please try again.": "重新发送验证邮件失败。请重试。",
|
||||
"We've sent you an email with your associated workspaces.": "我们已向您发送包含关联工作区的邮件。",
|
||||
"Load more": "加载更多",
|
||||
"Log out of all devices": "退出所有设备登录",
|
||||
"Log out of all sessions except this device": "退出除当前设备外的所有会话",
|
||||
"This Device": "此设备",
|
||||
"Unknown device": "未知设备",
|
||||
"No active sessions": "没有活动会话",
|
||||
"Session revoked": "会话已撤销",
|
||||
"All other sessions revoked": "所有其他会话已撤销",
|
||||
"Last used": "上次使用",
|
||||
"Created": "创建时间",
|
||||
"Rename": "重命名",
|
||||
"Publish": "发布",
|
||||
"Security": "安全",
|
||||
"Enforce SSO": "强制使用 SSO",
|
||||
"Once enforced, members will not be able to login with email and password.": "启用后,成员将无法使用邮箱和密码登录。",
|
||||
"AI-generated content may not be accurate.": "AI 生成的内容可能并不准确。",
|
||||
"AI Chat": "AI 聊天",
|
||||
"Analyze for insights": "分析并获取洞察",
|
||||
"Ask anything...": "随便问点什么...",
|
||||
"Chat history": "聊天记录",
|
||||
"Chat name": "聊天名称",
|
||||
"Close": "关闭",
|
||||
"Docmost AI": "Docmost AI",
|
||||
"Failed to load chat. An error occurred.": "加载聊天失败。发生错误。",
|
||||
"Failed to render this message.": "渲染此消息失败。",
|
||||
"How can I help you today?": "今天我可以如何帮助您?",
|
||||
"New chat": "新聊天",
|
||||
"No chat history": "没有聊天记录",
|
||||
"No chats found": "未找到聊天",
|
||||
"No conversations yet": "暂无对话",
|
||||
"Open full page": "打开完整页面",
|
||||
"Previous 7 days": "前 7 天",
|
||||
"Previous 30 days": "前 30 天",
|
||||
"Search chats...": "搜索聊天...",
|
||||
"Start a new chat to see it here.": "开始新的聊天后会显示在这里。",
|
||||
"Summarize this page": "总结此页面",
|
||||
"Toggle AI Chat": "切换 AI 聊天",
|
||||
"Translate this page": "翻译此页面",
|
||||
"Try a different search term.": "请尝试其他搜索词。",
|
||||
"Try again": "重试",
|
||||
"Untitled chat": "未命名聊天",
|
||||
"What can I help you with?": "我能帮您做什么?"
|
||||
}
|
||||
|
||||
+22
-8
@@ -14,7 +14,6 @@ import AccountPreferences from "@/pages/settings/account/account-preferences.tsx
|
||||
import SpaceHome from "@/pages/space/space-home.tsx";
|
||||
import PageRedirect from "@/pages/page/page-redirect.tsx";
|
||||
import Layout from "@/components/layouts/global/layout.tsx";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import InviteSignup from "@/pages/auth/invite-signup.tsx";
|
||||
import ForgotPassword from "@/pages/auth/forgot-password.tsx";
|
||||
import PasswordReset from "./pages/auth/password-reset";
|
||||
@@ -27,6 +26,7 @@ import Security from "@/ee/security/pages/security.tsx";
|
||||
import License from "@/ee/licence/pages/license.tsx";
|
||||
import { useRedirectToCloudSelect } from "@/ee/hooks/use-redirect-to-cloud-select.tsx";
|
||||
import SharedPage from "@/pages/share/shared-page.tsx";
|
||||
import PdfRenderPage from "@/ee/pdf-export/pdf-render-page.tsx";
|
||||
import Shares from "@/pages/settings/shares/shares.tsx";
|
||||
import ShareLayout from "@/features/share/components/share-layout.tsx";
|
||||
import ShareRedirect from "@/pages/share/share-redirect.tsx";
|
||||
@@ -38,6 +38,13 @@ import SpaceTrash from "@/pages/space/space-trash.tsx";
|
||||
import UserApiKeys from "@/ee/api-key/pages/user-api-keys";
|
||||
import WorkspaceApiKeys from "@/ee/api-key/pages/workspace-api-keys";
|
||||
import AiSettings from "@/ee/ai/pages/ai-settings.tsx";
|
||||
import AuditLogs from "@/ee/audit/pages/audit-logs.tsx";
|
||||
import VerifiedPages from "@/ee/page-verification/pages/verified-pages.tsx";
|
||||
import TemplateList from "@/ee/template/pages/template-list";
|
||||
import TemplateEditor from "@/ee/template/pages/template-editor";
|
||||
import FavoritesPage from "@/pages/favorites/favorites-page";
|
||||
import AiChat from "@/ee/ai-chat/pages/ai-chat.tsx";
|
||||
import VerifyEmail from "@/ee/pages/verify-email.tsx";
|
||||
|
||||
export default function App() {
|
||||
const { t } = useTranslation();
|
||||
@@ -63,6 +70,7 @@ export default function App() {
|
||||
<>
|
||||
<Route path={"/create"} element={<CreateWorkspace />} />
|
||||
<Route path={"/select"} element={<CloudLogin />} />
|
||||
<Route path={"/verify-email"} element={<VerifyEmail />} />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -74,23 +82,26 @@ export default function App() {
|
||||
<Route path={"/share/p/:pageSlug"} element={<SharedPage />} />
|
||||
</Route>
|
||||
|
||||
<Route path={"/pdf-render/:pageId"} element={<PdfRenderPage />} />
|
||||
<Route path={"/share/:shareId"} element={<ShareRedirect />} />
|
||||
<Route path={"/p/:pageSlug"} element={<PageRedirect />} />
|
||||
|
||||
<Route element={<Layout />}>
|
||||
<Route path={"/home"} element={<Home />} />
|
||||
<Route path={"/ai"} element={<AiChat />} />
|
||||
<Route path={"/ai/chat/:chatId"} element={<AiChat />} />
|
||||
<Route path={"/spaces"} element={<SpacesPage />} />
|
||||
<Route path={"/favorites"} element={<FavoritesPage />} />
|
||||
<Route path={"/templates"} element={<TemplateList />} />
|
||||
<Route
|
||||
path={"/templates/:templateId"}
|
||||
element={<TemplateEditor />}
|
||||
/>
|
||||
<Route path={"/s/:spaceSlug"} element={<SpaceHome />} />
|
||||
<Route path={"/s/:spaceSlug/trash"} element={<SpaceTrash />} />
|
||||
<Route
|
||||
path={"/s/:spaceSlug/p/:pageSlug"}
|
||||
element={
|
||||
<ErrorBoundary
|
||||
fallback={<>{t("Failed to load page. An error occurred.")}</>}
|
||||
>
|
||||
<Page />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
element={<Page />}
|
||||
/>
|
||||
|
||||
<Route path={"/settings"}>
|
||||
@@ -109,6 +120,9 @@ export default function App() {
|
||||
<Route path={"sharing"} element={<Shares />} />
|
||||
<Route path={"security"} element={<Security />} />
|
||||
<Route path={"ai"} element={<AiSettings />} />
|
||||
<Route path={"ai/mcp"} element={<AiSettings />} />
|
||||
<Route path={"audit"} element={<AuditLogs />} />
|
||||
<Route path={"verifications"} element={<VerifiedPages />} />
|
||||
{!isCloud() && <Route path={"license"} element={<License />} />}
|
||||
{isCloud() && <Route path={"billing"} element={<Billing />} />}
|
||||
</Route>
|
||||
|
||||
@@ -130,7 +130,7 @@ export default function AvatarUploader({
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
zIndex: 1000,
|
||||
zIndex: 200,
|
||||
}}
|
||||
>
|
||||
<Loader size="sm" />
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Source: https://github.com/mantinedev/mantine/blob/master/packages/@mantine/core/src/components/CopyButton/CopyButton.tsx - MIT
|
||||
// modified to use the polyfilled clipboard api
|
||||
import React from "react";
|
||||
import { useClipboard } from "@/hooks/use-clipboard";
|
||||
import { useProps } from "@mantine/core";
|
||||
|
||||
interface CopyButtonProps {
|
||||
/** Children callback, provides current status and copy function as an argument */
|
||||
children: (payload: { copied: boolean; copy: () => void }) => React.ReactNode;
|
||||
|
||||
/** Value that is copied to the clipboard when the button is clicked */
|
||||
value: string;
|
||||
|
||||
/** Copied status timeout in ms @default `1000` */
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
timeout: 1000,
|
||||
} satisfies Partial<CopyButtonProps>;
|
||||
|
||||
export function CopyButton(props: CopyButtonProps) {
|
||||
const { children, timeout, value, ...others } = useProps(
|
||||
"CopyButton",
|
||||
defaultProps,
|
||||
props,
|
||||
);
|
||||
const clipboard = useClipboard({ timeout });
|
||||
const copy = () => clipboard.copy(value);
|
||||
return <>{children({ copy, copied: clipboard.copied, ...others })}</>;
|
||||
}
|
||||
|
||||
CopyButton.displayName = "@mantine/core/CopyButton";
|
||||
@@ -1,12 +1,15 @@
|
||||
import { ActionIcon, CopyButton, Tooltip } from "@mantine/core";
|
||||
import { ActionIcon, MantineColor, MantineSize, Tooltip } from "@mantine/core";
|
||||
import { CopyButton } from "@/components/common/copy-button";
|
||||
import { IconCheck, IconCopy } from "@tabler/icons-react";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface CopyProps {
|
||||
text: string;
|
||||
size?: MantineSize;
|
||||
color?: MantineColor;
|
||||
}
|
||||
export default function CopyTextButton({ text }: CopyProps) {
|
||||
export default function CopyTextButton({ text, size }: CopyProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -21,6 +24,7 @@ export default function CopyTextButton({ text }: CopyProps) {
|
||||
color={copied ? "teal" : "gray"}
|
||||
variant="subtle"
|
||||
onClick={copy}
|
||||
size={size}
|
||||
>
|
||||
{copied ? <IconCheck size={16} /> : <IconCopy size={16} />}
|
||||
</ActionIcon>
|
||||
|
||||
@@ -5,13 +5,15 @@ import {
|
||||
Badge,
|
||||
Table,
|
||||
ActionIcon,
|
||||
Button,
|
||||
} from "@mantine/core";
|
||||
import { Link } from "react-router-dom";
|
||||
import PageListSkeleton from "@/components/ui/page-list-skeleton.tsx";
|
||||
import { buildPageUrl } from "@/features/page/page.utils.ts";
|
||||
import { formattedDate } from "@/lib/time.ts";
|
||||
import { useRecentChangesQuery } from "@/features/page/queries/page-query.ts";
|
||||
import { IconFileDescription } from "@tabler/icons-react";
|
||||
import { IconFileDescription, IconFiles } from "@tabler/icons-react";
|
||||
import { EmptyState } from "@/components/ui/empty-state.tsx";
|
||||
import { getSpaceUrl } from "@/lib/config.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getInitialsColor } from "@/lib/get-initials-color.ts";
|
||||
@@ -22,7 +24,8 @@ interface Props {
|
||||
|
||||
export default function RecentChanges({ spaceId }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { data: pages, isLoading, isError } = useRecentChangesQuery(spaceId);
|
||||
const { data, isLoading, isError, hasNextPage, fetchNextPage, isFetchingNextPage } = useRecentChangesQuery(spaceId);
|
||||
const pages = data?.pages.flatMap((p) => p.items) ?? [];
|
||||
|
||||
if (isLoading) {
|
||||
return <PageListSkeleton />;
|
||||
@@ -32,61 +35,77 @@ export default function RecentChanges({ spaceId }: Props) {
|
||||
return <Text>{t("Failed to fetch recent pages")}</Text>;
|
||||
}
|
||||
|
||||
return pages && pages.items.length > 0 ? (
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Tbody>
|
||||
{pages.items.map((page) => (
|
||||
<Table.Tr key={page.id}>
|
||||
<Table.Td>
|
||||
<UnstyledButton
|
||||
component={Link}
|
||||
to={buildPageUrl(page?.space.slug, page.slugId, page.title)}
|
||||
>
|
||||
<Group wrap="nowrap">
|
||||
{page.icon || (
|
||||
<ActionIcon variant="transparent" color="gray" size={18}>
|
||||
<IconFileDescription size={18} />
|
||||
</ActionIcon>
|
||||
)}
|
||||
|
||||
<Text fw={500} size="md" lineClamp={1}>
|
||||
{page.title || t("Untitled")}
|
||||
</Text>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Table.Td>
|
||||
{!spaceId && (
|
||||
return pages.length > 0 ? (
|
||||
<>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Tbody>
|
||||
{pages.map((page) => (
|
||||
<Table.Tr key={page.id}>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
color={getInitialsColor(page?.space.name)}
|
||||
variant="light"
|
||||
<UnstyledButton
|
||||
component={Link}
|
||||
to={getSpaceUrl(page?.space.slug)}
|
||||
style={{ cursor: "pointer" }}
|
||||
to={buildPageUrl(page?.space.slug, page.slugId, page.title)}
|
||||
>
|
||||
{page?.space.name}
|
||||
</Badge>
|
||||
<Group wrap="nowrap">
|
||||
{page.icon || (
|
||||
<ActionIcon variant="transparent" color="gray" size={18}>
|
||||
<IconFileDescription size={18} />
|
||||
</ActionIcon>
|
||||
)}
|
||||
|
||||
<Text fw={500} size="md" lineClamp={1}>
|
||||
{page.title || t("Untitled")}
|
||||
</Text>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Table.Td>
|
||||
)}
|
||||
<Table.Td>
|
||||
<Text
|
||||
c="dimmed"
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
size="xs"
|
||||
fw={500}
|
||||
>
|
||||
{formattedDate(page.updatedAt)}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
{!spaceId && (
|
||||
<Table.Td>
|
||||
<Badge
|
||||
color={getInitialsColor(page?.space.name)}
|
||||
variant="light"
|
||||
component={Link}
|
||||
to={getSpaceUrl(page?.space.slug)}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{page?.space.name}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
)}
|
||||
<Table.Td>
|
||||
<Text
|
||||
c="dimmed"
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
size="xs"
|
||||
fw={500}
|
||||
>
|
||||
{formattedDate(page.updatedAt)}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
{hasNextPage && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
fullWidth
|
||||
mt="sm"
|
||||
mb="xl"
|
||||
onClick={() => fetchNextPage()}
|
||||
loading={isFetchingNextPage}
|
||||
>
|
||||
{t("Load more")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text size="md" ta="center">
|
||||
{t("No pages yet")}
|
||||
</Text>
|
||||
<EmptyState
|
||||
icon={IconFiles}
|
||||
title={t("No pages yet")}
|
||||
description={t("Pages you create will show up here.")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { rem } from "@mantine/core";
|
||||
|
||||
type Props = {
|
||||
size?: number | string;
|
||||
stroke?: number;
|
||||
};
|
||||
|
||||
export function IconColumns4({ size = 24, stroke = 2 }: Props) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={rem(size)}
|
||||
height={rem(size)}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={stroke}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M3 4a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v16a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-16" />
|
||||
<path d="M7.5 3v18" />
|
||||
<path d="M12 3v18" />
|
||||
<path d="M16.5 3v18" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { rem } from "@mantine/core";
|
||||
|
||||
type Props = {
|
||||
size?: number | string;
|
||||
stroke?: number;
|
||||
};
|
||||
|
||||
export function IconColumns5({ size = 24, stroke = 2 }: Props) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={rem(size)}
|
||||
height={rem(size)}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={stroke}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M3 4a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v16a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-16" />
|
||||
<path d="M6.6 3v18" />
|
||||
<path d="M10.2 3v18" />
|
||||
<path d="M13.8 3v18" />
|
||||
<path d="M17.4 3v18" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,19 @@
|
||||
padding-right: var(--mantine-spacing-md);
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.brandIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: block;
|
||||
line-height: 1;
|
||||
@@ -16,6 +29,9 @@
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import { Badge, Group, Text, Tooltip } from "@mantine/core";
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
Box,
|
||||
Group,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import classes from "./app-header.module.css";
|
||||
import React from "react";
|
||||
import TopMenu from "@/components/layouts/global/top-menu.tsx";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { IconSparkles } from "@tabler/icons-react";
|
||||
import useToggleAside from "@/hooks/use-toggle-aside.tsx";
|
||||
import APP_ROUTE from "@/lib/app-route.ts";
|
||||
import { useAtom } from "jotai";
|
||||
import {
|
||||
@@ -22,8 +32,12 @@ import {
|
||||
searchSpotlight,
|
||||
shareSearchSpotlight,
|
||||
} from "@/features/search/constants.ts";
|
||||
import { NotificationPopover } from "@/features/notification/components/notification-popover.tsx";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
|
||||
const links = [{ link: APP_ROUTE.HOME, label: "Home" }];
|
||||
const links = [
|
||||
{ link: APP_ROUTE.HOME, label: "Home" },
|
||||
];
|
||||
|
||||
export function AppHeader() {
|
||||
const { t } = useTranslation();
|
||||
@@ -33,10 +47,12 @@ export function AppHeader() {
|
||||
const [desktopOpened] = useAtom(desktopSidebarAtom);
|
||||
const toggleDesktop = useToggleSidebar(desktopSidebarAtom);
|
||||
const { isTrial, trialDaysLeft } = useTrial();
|
||||
const location = useLocation();
|
||||
const toggleAside = useToggleAside();
|
||||
const [workspace] = useAtom(workspaceAtom);
|
||||
const aiChatEnabled = workspace?.settings?.ai?.chat === true;
|
||||
|
||||
const isHomeRoute = location.pathname.startsWith("/home");
|
||||
const isSpacesRoute = location.pathname === "/spaces";
|
||||
const hideSidebar = isHomeRoute || isSpacesRoute;
|
||||
const isPageRoute = location.pathname.includes("/p/");
|
||||
|
||||
const items = links.map((link) => (
|
||||
<Link key={link.label} to={link.link} className={classes.link}>
|
||||
@@ -48,39 +64,44 @@ export function AppHeader() {
|
||||
<>
|
||||
<Group h="100%" px="md" justify="space-between" wrap={"nowrap"}>
|
||||
<Group wrap="nowrap">
|
||||
{!hideSidebar && (
|
||||
<>
|
||||
<Tooltip label={t("Sidebar toggle")}>
|
||||
<SidebarToggle
|
||||
aria-label={t("Sidebar toggle")}
|
||||
opened={mobileOpened}
|
||||
onClick={toggleMobile}
|
||||
hiddenFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("Sidebar toggle")}>
|
||||
<SidebarToggle
|
||||
aria-label={t("Sidebar toggle")}
|
||||
opened={mobileOpened}
|
||||
onClick={toggleMobile}
|
||||
hiddenFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label={t("Sidebar toggle")}>
|
||||
<SidebarToggle
|
||||
aria-label={t("Sidebar toggle")}
|
||||
opened={desktopOpened}
|
||||
onClick={toggleDesktop}
|
||||
visibleFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
<Tooltip label={t("Sidebar toggle")}>
|
||||
<SidebarToggle
|
||||
aria-label={t("Sidebar toggle")}
|
||||
opened={desktopOpened}
|
||||
onClick={toggleDesktop}
|
||||
visibleFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Text
|
||||
size="lg"
|
||||
fw={600}
|
||||
style={{ cursor: "pointer", userSelect: "none" }}
|
||||
component={Link}
|
||||
to="/home"
|
||||
>
|
||||
Docmost
|
||||
</Text>
|
||||
<Link to="/home" className={classes.brand} aria-label="Docmost">
|
||||
<Box hiddenFrom="sm" className={classes.brandIcon}>
|
||||
<img
|
||||
src="/icons/favicon-32x32.png"
|
||||
alt="Docmost"
|
||||
width={22}
|
||||
height={22}
|
||||
/>
|
||||
</Box>
|
||||
<Text
|
||||
size="lg"
|
||||
fw={600}
|
||||
style={{ userSelect: "none" }}
|
||||
visibleFrom="sm"
|
||||
>
|
||||
Docmost
|
||||
</Text>
|
||||
</Link>
|
||||
|
||||
<Group ml={50} gap={5} className={classes.links} visibleFrom="sm">
|
||||
{items}
|
||||
@@ -97,6 +118,50 @@ export function AppHeader() {
|
||||
</div>
|
||||
|
||||
<Group px={"xl"} wrap="nowrap">
|
||||
{aiChatEnabled && (
|
||||
<>
|
||||
<UnstyledButton
|
||||
component={Link}
|
||||
to="/ai"
|
||||
className={classes.link}
|
||||
visibleFrom="sm"
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) {
|
||||
return;
|
||||
}
|
||||
if (isPageRoute) {
|
||||
e.preventDefault();
|
||||
toggleAside("chat");
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t("AI Chat")}
|
||||
</UnstyledButton>
|
||||
<Tooltip label={t("AI Chat")} openDelay={250} withArrow>
|
||||
<ActionIcon
|
||||
component={Link}
|
||||
to="/ai"
|
||||
variant="subtle"
|
||||
color="dark"
|
||||
size="sm"
|
||||
hiddenFrom="sm"
|
||||
aria-label={t("AI Chat")}
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) {
|
||||
return;
|
||||
}
|
||||
if (isPageRoute) {
|
||||
e.preventDefault();
|
||||
toggleAside("chat");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconSparkles size={20} stroke={2} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
<NotificationPopover />
|
||||
{isCloud() && isTrial && trialDaysLeft !== 0 && (
|
||||
<Badge
|
||||
variant="light"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { TableOfContents } from "@/features/editor/components/table-of-contents/table-of-contents.tsx";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { pageEditorAtom } from "@/features/editor/atoms/editor-atoms.ts";
|
||||
import AsideChatPanel from "@/ee/ai-chat/components/aside-chat-panel";
|
||||
|
||||
export default function Aside() {
|
||||
const [{ tab }] = useAtom(asideStateAtom);
|
||||
@@ -25,21 +26,27 @@ export default function Aside() {
|
||||
component = <TableOfContents editor={pageEditor} />;
|
||||
title = "Table of contents";
|
||||
break;
|
||||
case "chat":
|
||||
component = <AsideChatPanel />;
|
||||
title = "AI Chat";
|
||||
break;
|
||||
default:
|
||||
component = null;
|
||||
title = null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box p="md">
|
||||
<Box p="md" style={{ height: "100%", display: "flex", flexDirection: "column" }}>
|
||||
{component && (
|
||||
<>
|
||||
<Text mb="md" fw={500}>
|
||||
{t(title)}
|
||||
</Text>
|
||||
{tab !== "chat" && (
|
||||
<Text mb="md" fw={500}>
|
||||
{t(title)}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{tab === "comments" ? (
|
||||
<CommentListWithTabs />
|
||||
{tab === "comments" || tab === "chat" ? (
|
||||
component
|
||||
) : (
|
||||
<ScrollArea
|
||||
style={{ height: "85vh" }}
|
||||
|
||||
@@ -10,11 +10,13 @@ import {
|
||||
sidebarWidthAtom,
|
||||
} from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts";
|
||||
import { SpaceSidebar } from "@/features/space/components/sidebar/space-sidebar.tsx";
|
||||
import AiChatSidebar from "@/ee/ai-chat/components/ai-chat-sidebar.tsx";
|
||||
import { AppHeader } from "@/components/layouts/global/app-header.tsx";
|
||||
import Aside from "@/components/layouts/global/aside.tsx";
|
||||
import classes from "./app-shell.module.css";
|
||||
import { useTrialEndAction } from "@/ee/hooks/use-trial-end-action.tsx";
|
||||
import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts";
|
||||
import GlobalSidebar from "@/components/layouts/global/global-sidebar.tsx";
|
||||
|
||||
export default function GlobalAppShell({
|
||||
children,
|
||||
@@ -72,24 +74,21 @@ export default function GlobalAppShell({
|
||||
const location = useLocation();
|
||||
const isSettingsRoute = location.pathname.startsWith("/settings");
|
||||
const isSpaceRoute = location.pathname.startsWith("/s/");
|
||||
const isHomeRoute = location.pathname.startsWith("/home");
|
||||
const isSpacesRoute = location.pathname === "/spaces";
|
||||
const isAiRoute = location.pathname.startsWith("/ai");
|
||||
const isPageRoute = location.pathname.includes("/p/");
|
||||
const hideSidebar = isHomeRoute || isSpacesRoute;
|
||||
const showGlobalSidebar = !isSpaceRoute && !isSettingsRoute && !isAiRoute;
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
header={{ height: 45 }}
|
||||
navbar={
|
||||
!hideSidebar && {
|
||||
width: isSpaceRoute ? sidebarWidth : 300,
|
||||
breakpoint: "sm",
|
||||
collapsed: {
|
||||
mobile: !mobileOpened,
|
||||
desktop: !desktopOpened,
|
||||
},
|
||||
}
|
||||
}
|
||||
navbar={{
|
||||
width: isSpaceRoute ? sidebarWidth : 300,
|
||||
breakpoint: "sm",
|
||||
collapsed: {
|
||||
mobile: !mobileOpened,
|
||||
desktop: !desktopOpened,
|
||||
},
|
||||
}}
|
||||
aside={
|
||||
isPageRoute && {
|
||||
width: 350,
|
||||
@@ -102,20 +101,22 @@ export default function GlobalAppShell({
|
||||
<AppShell.Header px="md" className={classes.header}>
|
||||
<AppHeader />
|
||||
</AppShell.Header>
|
||||
{!hideSidebar && (
|
||||
<AppShell.Navbar
|
||||
className={classes.navbar}
|
||||
withBorder={false}
|
||||
ref={sidebarRef}
|
||||
>
|
||||
<AppShell.Navbar
|
||||
className={classes.navbar}
|
||||
withBorder={false}
|
||||
ref={sidebarRef}
|
||||
>
|
||||
{isSpaceRoute && (
|
||||
<div className={classes.resizeHandle} onMouseDown={startResizing} />
|
||||
{isSpaceRoute && <SpaceSidebar />}
|
||||
{isSettingsRoute && <SettingsSidebar />}
|
||||
</AppShell.Navbar>
|
||||
)}
|
||||
)}
|
||||
{isSpaceRoute && <SpaceSidebar />}
|
||||
{isSettingsRoute && <SettingsSidebar />}
|
||||
{isAiRoute && <AiChatSidebar />}
|
||||
{showGlobalSidebar && <GlobalSidebar />}
|
||||
</AppShell.Navbar>
|
||||
<AppShell.Main>
|
||||
{isSettingsRoute ? (
|
||||
<Container size={850}>{children}</Container>
|
||||
<Container size={900}>{children}</Container>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
.navbar {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: var(--mantine-spacing-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding-bottom: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.link {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
padding-left: var(--mantine-spacing-xs);
|
||||
min-height: 30px;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-1),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
&,
|
||||
& :hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.linkIcon {
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
margin-right: var(--mantine-spacing-sm);
|
||||
width: rem(16px);
|
||||
height: rem(16px);
|
||||
}
|
||||
|
||||
.sectionHeader {
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-3));
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bottomSection {
|
||||
padding-top: var(--mantine-spacing-xs);
|
||||
border-top: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
|
||||
.spaceItem {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--mantine-spacing-sm);
|
||||
text-decoration: none;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
padding-left: var(--mantine-spacing-xs);
|
||||
min-height: 30px;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-1),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { ScrollArea, Text, Divider, Modal } from "@mantine/core";
|
||||
import {
|
||||
IconHome,
|
||||
IconClock,
|
||||
IconStar,
|
||||
IconLayoutGrid,
|
||||
IconSettings,
|
||||
IconUserPlus,
|
||||
} from "@tabler/icons-react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import classes from "./global-sidebar.module.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAtom } from "jotai";
|
||||
import { mobileSidebarAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom";
|
||||
import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar";
|
||||
import { useFavoritesQuery } from "@/features/favorite/queries/favorite-query";
|
||||
import { getSpaceUrl } from "@/lib/config";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { WorkspaceInviteForm } from "@/features/workspace/components/members/components/workspace-invite-form";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar";
|
||||
import { AvatarIconType } from "@/features/attachments/types/attachment.types";
|
||||
|
||||
const mainNavItems = [
|
||||
{ label: "Home", icon: IconHome, path: "/home" },
|
||||
{ label: "Favorites", icon: IconStar, path: "/favorites" },
|
||||
{ label: "Spaces", icon: IconLayoutGrid, path: "/spaces" },
|
||||
];
|
||||
|
||||
export default function GlobalSidebar() {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const [active, setActive] = useState(location.pathname);
|
||||
const [mobileSidebarOpened] = useAtom(mobileSidebarAtom);
|
||||
const toggleMobileSidebar = useToggleSidebar(mobileSidebarAtom);
|
||||
const { data: favoriteSpacesData, isPending: isFavoritesPending } = useFavoritesQuery("space");
|
||||
const favoriteSpaces = favoriteSpacesData?.pages.flatMap((p) => p.items) ?? [];
|
||||
const sortedFavoriteSpaces = [...favoriteSpaces]
|
||||
.filter((fav) => fav.space)
|
||||
.sort((a, b) => {
|
||||
const cmp = (a.space!.name ?? "").localeCompare(b.space!.name ?? "", undefined, { sensitivity: "base" });
|
||||
return cmp !== 0 ? cmp : a.id.localeCompare(b.id);
|
||||
});
|
||||
const [inviteOpened, { open: openInvite, close: closeInvite }] =
|
||||
useDisclosure(false);
|
||||
|
||||
useEffect(() => {
|
||||
setActive(location.pathname);
|
||||
}, [location.pathname]);
|
||||
|
||||
const handleNavClick = () => {
|
||||
if (mobileSidebarOpened) {
|
||||
toggleMobileSidebar();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.navbar}>
|
||||
<ScrollArea w="100%" style={{ flex: 1 }}>
|
||||
<div className={classes.section}>
|
||||
{mainNavItems.map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
className={classes.link}
|
||||
data-active={active === item.path || undefined}
|
||||
to={item.path}
|
||||
onClick={handleNavClick}
|
||||
>
|
||||
<item.icon className={classes.linkIcon} stroke={2} />
|
||||
<span>{t(item.label)}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Divider my="xs" />
|
||||
<div className={classes.section}>
|
||||
<Text className={classes.sectionHeader}>{t("Favorite spaces")}</Text>
|
||||
{!isFavoritesPending && sortedFavoriteSpaces.length === 0 ? (
|
||||
<Text size="xs" c="dimmed" pl="xs" py={4}>
|
||||
{t("Favorite spaces appear here")}
|
||||
</Text>
|
||||
) : (
|
||||
<>
|
||||
{sortedFavoriteSpaces.slice(0, 10).map((fav) => (
|
||||
<Link
|
||||
key={fav.id}
|
||||
className={classes.spaceItem}
|
||||
to={getSpaceUrl(fav.space!.slug)}
|
||||
onClick={handleNavClick}
|
||||
>
|
||||
<CustomAvatar
|
||||
name={fav.space!.name}
|
||||
avatarUrl={fav.space!.logo}
|
||||
type={AvatarIconType.SPACE_ICON}
|
||||
color="initials"
|
||||
variant="filled"
|
||||
size={20}
|
||||
/>
|
||||
<Text size="sm" fw={500} lineClamp={1}>
|
||||
{fav.space!.name}
|
||||
</Text>
|
||||
</Link>
|
||||
))}
|
||||
{sortedFavoriteSpaces.length > 10 && (
|
||||
<Link
|
||||
className={classes.spaceItem}
|
||||
to="/spaces"
|
||||
onClick={handleNavClick}
|
||||
>
|
||||
<Text size="xs" c="dimmed">
|
||||
{t("View all")}
|
||||
</Text>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</ScrollArea>
|
||||
|
||||
<div className={classes.bottomSection}>
|
||||
<a
|
||||
className={classes.link}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
openInvite();
|
||||
}}
|
||||
href="#"
|
||||
>
|
||||
<IconUserPlus className={classes.linkIcon} stroke={2} />
|
||||
<span>{t("Invite People")}</span>
|
||||
</a>
|
||||
<Link
|
||||
className={classes.link}
|
||||
data-active={active.startsWith("/settings") || undefined}
|
||||
to="/settings/account/profile"
|
||||
onClick={handleNavClick}
|
||||
>
|
||||
<IconSettings className={classes.linkIcon} stroke={2} />
|
||||
<span>{t("Settings")}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
size="550"
|
||||
opened={inviteOpened}
|
||||
onClose={closeInvite}
|
||||
title={t("Invite new members")}
|
||||
centered
|
||||
>
|
||||
<Divider size="xs" mb="xs" />
|
||||
<ScrollArea h="80%">
|
||||
<WorkspaceInviteForm onClose={closeInvite} />
|
||||
</ScrollArea>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import { getLicenseInfo } from "@/ee/licence/services/license-service.ts";
|
||||
import { getSsoProviders } from "@/ee/security/services/security-service.ts";
|
||||
import { getShares } from "@/features/share/services/share-service.ts";
|
||||
import { getApiKeys } from "@/ee/api-key";
|
||||
import { getAuditLogs } from "@/ee/audit/services/audit-service";
|
||||
import { getVerificationList } from "@/ee/page-verification/services/page-verification-service";
|
||||
|
||||
export const prefetchWorkspaceMembers = () => {
|
||||
const params: QueryParams = { limit: 100, query: "" };
|
||||
@@ -80,3 +82,19 @@ export const prefetchApiKeyManagement = () => {
|
||||
queryFn: () => getApiKeys({ adminView: true }),
|
||||
});
|
||||
};
|
||||
|
||||
export const prefetchAuditLogs = () => {
|
||||
const params = { limit: 50 };
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ["audit-logs", params],
|
||||
queryFn: () => getAuditLogs(params),
|
||||
});
|
||||
};
|
||||
|
||||
export const prefetchVerifiedPages = () => {
|
||||
const params = { limit: 50 };
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ["verification-list", params],
|
||||
queryFn: () => getVerificationList(params),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
IconKey,
|
||||
IconWorld,
|
||||
IconSparkles,
|
||||
IconHistory,
|
||||
IconShieldCheck,
|
||||
} from "@tabler/icons-react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import classes from "./settings.module.css";
|
||||
@@ -20,7 +22,9 @@ import { useTranslation } from "react-i18next";
|
||||
import { isCloud } from "@/lib/config.ts";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import { entitlementAtom } from "@/ee/entitlement/entitlement-atom";
|
||||
import { Feature } from "@/ee/features";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
|
||||
import {
|
||||
prefetchApiKeyManagement,
|
||||
prefetchApiKeys,
|
||||
@@ -31,27 +35,27 @@ import {
|
||||
prefetchSpaces,
|
||||
prefetchSsoProviders,
|
||||
prefetchWorkspaceMembers,
|
||||
prefetchAuditLogs,
|
||||
prefetchVerifiedPages,
|
||||
} from "@/components/settings/settings-queries.tsx";
|
||||
import AppVersion from "@/components/settings/app-version.tsx";
|
||||
import { mobileSidebarAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts";
|
||||
import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts";
|
||||
import { useSettingsNavigation } from "@/hooks/use-settings-navigation";
|
||||
|
||||
interface DataItem {
|
||||
type DataItem = {
|
||||
label: string;
|
||||
icon: React.ElementType;
|
||||
path: string;
|
||||
isCloud?: boolean;
|
||||
isEnterprise?: boolean;
|
||||
isAdmin?: boolean;
|
||||
isSelfhosted?: boolean;
|
||||
showDisabledInNonEE?: boolean;
|
||||
}
|
||||
feature?: string;
|
||||
role?: "admin" | "owner";
|
||||
env?: "cloud" | "selfhosted";
|
||||
};
|
||||
|
||||
interface DataGroup {
|
||||
type DataGroup = {
|
||||
heading: string;
|
||||
items: DataItem[];
|
||||
}
|
||||
};
|
||||
|
||||
const groupedData: DataGroup[] = [
|
||||
{
|
||||
@@ -67,9 +71,7 @@ const groupedData: DataGroup[] = [
|
||||
label: "API keys",
|
||||
icon: IconKey,
|
||||
path: "/settings/account/api-keys",
|
||||
isCloud: true,
|
||||
isEnterprise: true,
|
||||
showDisabledInNonEE: true,
|
||||
feature: Feature.API_KEYS,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -77,45 +79,50 @@ const groupedData: DataGroup[] = [
|
||||
heading: "Workspace",
|
||||
items: [
|
||||
{ label: "General", icon: IconSettings, path: "/settings/workspace" },
|
||||
{
|
||||
label: "Members",
|
||||
icon: IconUsers,
|
||||
path: "/settings/members",
|
||||
},
|
||||
{ label: "Members", icon: IconUsers, path: "/settings/members" },
|
||||
{
|
||||
label: "Billing",
|
||||
icon: IconCoin,
|
||||
path: "/settings/billing",
|
||||
isCloud: true,
|
||||
isAdmin: true,
|
||||
role: "admin",
|
||||
env: "cloud",
|
||||
},
|
||||
{
|
||||
label: "Security & SSO",
|
||||
icon: IconLock,
|
||||
path: "/settings/security",
|
||||
isCloud: true,
|
||||
isEnterprise: true,
|
||||
isAdmin: true,
|
||||
showDisabledInNonEE: true,
|
||||
feature: Feature.SECURITY_SETTINGS,
|
||||
role: "admin",
|
||||
},
|
||||
{ label: "Groups", icon: IconUsersGroup, path: "/settings/groups" },
|
||||
{ label: "Spaces", icon: IconSpaces, path: "/settings/spaces" },
|
||||
{ label: "Public sharing", icon: IconWorld, path: "/settings/sharing" },
|
||||
{
|
||||
label: "Verified pages",
|
||||
icon: IconShieldCheck,
|
||||
path: "/settings/verifications",
|
||||
feature: Feature.PAGE_VERIFICATION,
|
||||
},
|
||||
{
|
||||
label: "API management",
|
||||
icon: IconKey,
|
||||
path: "/settings/api-keys",
|
||||
isCloud: true,
|
||||
isEnterprise: true,
|
||||
isAdmin: true,
|
||||
showDisabledInNonEE: true,
|
||||
feature: Feature.API_KEYS,
|
||||
role: "admin",
|
||||
},
|
||||
{
|
||||
label: "AI settings",
|
||||
icon: IconSparkles,
|
||||
path: "/settings/ai",
|
||||
isAdmin: true,
|
||||
isSelfhosted: true,
|
||||
role: "admin",
|
||||
},
|
||||
{
|
||||
label: "Audit log",
|
||||
icon: IconHistory,
|
||||
path: "/settings/audit",
|
||||
feature: Feature.AUDIT_LOGS,
|
||||
role: "owner",
|
||||
env: "selfhosted",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -136,8 +143,9 @@ export default function SettingsSidebar() {
|
||||
const location = useLocation();
|
||||
const [active, setActive] = useState(location.pathname);
|
||||
const { goBack } = useSettingsNavigation();
|
||||
const { isAdmin } = useUserRole();
|
||||
const [workspace] = useAtom(workspaceAtom);
|
||||
const { isAdmin, isOwner } = useUserRole();
|
||||
const [entitlements] = useAtom(entitlementAtom);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
const [mobileSidebarOpened] = useAtom(mobileSidebarAtom);
|
||||
const toggleMobileSidebar = useToggleSidebar(mobileSidebarAtom);
|
||||
|
||||
@@ -145,41 +153,20 @@ export default function SettingsSidebar() {
|
||||
setActive(location.pathname);
|
||||
}, [location.pathname]);
|
||||
|
||||
const hasFeature = (f: string) =>
|
||||
entitlements?.features?.includes(f) ?? false;
|
||||
|
||||
const canShowItem = (item: DataItem) => {
|
||||
if (item.showDisabledInNonEE && item.isEnterprise) {
|
||||
// Check admin permission regardless of license
|
||||
return item.isAdmin ? isAdmin : true;
|
||||
}
|
||||
|
||||
if (item.isCloud && item.isEnterprise) {
|
||||
if (!(isCloud() || workspace?.hasLicenseKey)) return false;
|
||||
return item.isAdmin ? isAdmin : true;
|
||||
}
|
||||
|
||||
if (item.isCloud) {
|
||||
return isCloud() ? (item.isAdmin ? isAdmin : true) : false;
|
||||
}
|
||||
|
||||
if (item.isSelfhosted) {
|
||||
return !isCloud() ? (item.isAdmin ? isAdmin : true) : false;
|
||||
}
|
||||
|
||||
if (item.isEnterprise) {
|
||||
return workspace?.hasLicenseKey ? (item.isAdmin ? isAdmin : true) : false;
|
||||
}
|
||||
|
||||
if (item.isAdmin) {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
if (item.env === "cloud" && !isCloud()) return false;
|
||||
if (item.env === "selfhosted" && isCloud()) return false;
|
||||
if (item.role === "admin" && !isAdmin) return false;
|
||||
if (item.role === "owner" && !isOwner) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const isItemDisabled = (item: DataItem) => {
|
||||
if (item.showDisabledInNonEE && item.isEnterprise) {
|
||||
return !(isCloud() || workspace?.hasLicenseKey);
|
||||
}
|
||||
return false;
|
||||
if (!item.feature) return false;
|
||||
return !hasFeature(item.feature);
|
||||
};
|
||||
|
||||
const menuItems = groupedData.map((group) => {
|
||||
@@ -212,7 +199,7 @@ export default function SettingsSidebar() {
|
||||
prefetchHandler = prefetchBilling;
|
||||
break;
|
||||
case "License & Edition":
|
||||
if (workspace?.hasLicenseKey) {
|
||||
if (entitlements?.tier !== "free") {
|
||||
prefetchHandler = prefetchLicense;
|
||||
}
|
||||
break;
|
||||
@@ -228,6 +215,12 @@ export default function SettingsSidebar() {
|
||||
case "API management":
|
||||
prefetchHandler = prefetchApiKeyManagement;
|
||||
break;
|
||||
case "Audit log":
|
||||
prefetchHandler = prefetchAuditLogs;
|
||||
break;
|
||||
case "Verified pages":
|
||||
prefetchHandler = prefetchVerifiedPages;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -264,7 +257,7 @@ export default function SettingsSidebar() {
|
||||
return (
|
||||
<Tooltip
|
||||
key={item.label}
|
||||
label={t("Available in enterprise edition")}
|
||||
label={upgradeLabel}
|
||||
position="right"
|
||||
withArrow
|
||||
>
|
||||
|
||||
@@ -34,6 +34,7 @@ export function AutoTooltipText({
|
||||
disabled={!isTruncated || !label}
|
||||
multiline
|
||||
withArrow
|
||||
withinPortal={false}
|
||||
{...tooltipProps}
|
||||
>
|
||||
<Text
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
.root {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.track {
|
||||
display: flex;
|
||||
gap: var(--mantine-spacing-md);
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
scroll-behavior: smooth;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
padding: 2px;
|
||||
margin: -2px;
|
||||
}
|
||||
|
||||
.track::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.track > * {
|
||||
scroll-snap-align: start;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 120ms ease, background-color 120ms ease, transform 120ms ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.root:hover .arrow.visible,
|
||||
.arrow.visible:focus-visible {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.arrow:hover {
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5));
|
||||
}
|
||||
|
||||
.arrow:active {
|
||||
transform: translateY(-50%) scale(0.95);
|
||||
}
|
||||
|
||||
.arrowLeft {
|
||||
left: -14px;
|
||||
}
|
||||
|
||||
.arrowRight {
|
||||
right: -14px;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classes from "./card-carousel.module.css";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
ariaLabel?: string;
|
||||
};
|
||||
|
||||
export default function CardCarousel({ children, ariaLabel }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const trackRef = useRef<HTMLDivElement>(null);
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(false);
|
||||
|
||||
const updateScrollState = useCallback(() => {
|
||||
const el = trackRef.current;
|
||||
if (!el) return;
|
||||
const maxScroll = el.scrollWidth - el.clientWidth;
|
||||
setCanScrollLeft(el.scrollLeft > 1);
|
||||
setCanScrollRight(el.scrollLeft < maxScroll - 1);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
updateScrollState();
|
||||
const el = trackRef.current;
|
||||
if (!el) return;
|
||||
|
||||
const observer = new ResizeObserver(updateScrollState);
|
||||
observer.observe(el);
|
||||
for (const child of Array.from(el.children)) {
|
||||
observer.observe(child);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [updateScrollState, children]);
|
||||
|
||||
const scrollBy = (direction: 1 | -1) => {
|
||||
const el = trackRef.current;
|
||||
if (!el) return;
|
||||
el.scrollBy({ left: direction * el.clientWidth * 0.85, behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<div
|
||||
ref={trackRef}
|
||||
className={classes.track}
|
||||
onScroll={updateScrollState}
|
||||
{...(ariaLabel ? { role: "region", "aria-label": ariaLabel } : {})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`${classes.arrow} ${classes.arrowLeft} ${canScrollLeft ? classes.visible : ""}`}
|
||||
onClick={() => scrollBy(-1)}
|
||||
aria-label={t("Scroll left")}
|
||||
tabIndex={canScrollLeft ? 0 : -1}
|
||||
>
|
||||
<IconChevronLeft size={18} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`${classes.arrow} ${classes.arrowRight} ${canScrollRight ? classes.visible : ""}`}
|
||||
onClick={() => scrollBy(1)}
|
||||
aria-label={t("Scroll right")}
|
||||
tabIndex={canScrollRight ? 0 : -1}
|
||||
>
|
||||
<IconChevronRight size={18} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { getAvatarUrl } from "@/lib/config.ts";
|
||||
import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts";
|
||||
|
||||
interface CustomAvatarProps {
|
||||
avatarUrl: string;
|
||||
avatarUrl?: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
size?: string | number;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Modal, Button, Group } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DestinationPicker } from "./destination-picker";
|
||||
import {
|
||||
DestinationPickerModalProps,
|
||||
DestinationSelection,
|
||||
} from "./destination-picker.types";
|
||||
|
||||
export function DestinationPickerModal({
|
||||
opened,
|
||||
onClose,
|
||||
title,
|
||||
actionLabel,
|
||||
onSelect,
|
||||
loading,
|
||||
excludePageId,
|
||||
pageLimit,
|
||||
}: DestinationPickerModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [selection, setSelection] = useState<DestinationSelection | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!opened) {
|
||||
setSelection(null);
|
||||
}
|
||||
}, [opened]);
|
||||
|
||||
return (
|
||||
<Modal.Root
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
size={550}
|
||||
padding="lg"
|
||||
yOffset="10vh"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Modal.Overlay />
|
||||
<Modal.Content>
|
||||
<Modal.Header py={0}>
|
||||
<Modal.Title fw={500}>{title}</Modal.Title>
|
||||
<Modal.CloseButton />
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<DestinationPicker
|
||||
onSelectionChange={setSelection}
|
||||
excludePageId={excludePageId}
|
||||
pageLimit={pageLimit}
|
||||
/>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button variant="default" onClick={onClose}>
|
||||
{t("Close")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => selection && onSelect(selection)}
|
||||
disabled={!selection}
|
||||
loading={loading}
|
||||
>
|
||||
{actionLabel}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal.Body>
|
||||
</Modal.Content>
|
||||
</Modal.Root>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
.searchInput {
|
||||
margin-bottom: var(--mantine-spacing-sm);
|
||||
}
|
||||
|
||||
.scrollArea {
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 6px 12px;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: background-color 150ms ease;
|
||||
user-select: none;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-0),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-blue-0),
|
||||
var(--mantine-color-dark-5)
|
||||
);
|
||||
border-left: 2px solid var(--mantine-primary-color-filled);
|
||||
}
|
||||
|
||||
.spaceRow {
|
||||
composes: row;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pageRow {
|
||||
composes: row;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
flex-shrink: 0;
|
||||
transition: transform 150ms ease;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-1),
|
||||
var(--mantine-color-dark-5)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.chevronExpanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.loadMore {
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
cursor: pointer;
|
||||
|
||||
@mixin hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.selectedIndicator {
|
||||
padding: 8px 12px;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
|
||||
margin-top: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
}
|
||||
|
||||
.searchResult {
|
||||
composes: row;
|
||||
}
|
||||
|
||||
.pageTitle {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
}
|
||||
|
||||
.spaceName {
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { TextInput, ScrollArea, Loader } from "@mantine/core";
|
||||
import { useDebouncedValue } from "@mantine/hooks";
|
||||
import { IconSearch, IconFile } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useGetSpacesQuery } from "@/features/space/queries/space-query";
|
||||
import { useSearchSuggestionsQuery } from "@/features/search/queries/search-query";
|
||||
import { ISpace } from "@/features/space/types/space.types";
|
||||
import { IPage } from "@/features/page/types/page.types";
|
||||
import { DestinationSelection } from "./destination-picker.types";
|
||||
import { SpaceRow } from "./space-row";
|
||||
import classes from "./destination-picker.module.css";
|
||||
|
||||
type DestinationPickerProps = {
|
||||
onSelectionChange: (selection: DestinationSelection | null) => void;
|
||||
excludePageId?: string;
|
||||
pageLimit?: number;
|
||||
};
|
||||
|
||||
export function DestinationPicker({
|
||||
onSelectionChange,
|
||||
excludePageId,
|
||||
pageLimit = 15,
|
||||
}: DestinationPickerProps) {
|
||||
const { t } = useTranslation();
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [selection, setSelection] = useState<DestinationSelection | null>(null);
|
||||
const [debouncedQuery] = useDebouncedValue(searchQuery, 300);
|
||||
|
||||
const { data: spacesData, isLoading: spacesLoading } = useGetSpacesQuery({
|
||||
limit: 100,
|
||||
});
|
||||
|
||||
const searchEnabled = debouncedQuery && debouncedQuery.length >= 2;
|
||||
|
||||
const { data: searchData, isLoading: searchLoading } =
|
||||
useSearchSuggestionsQuery({
|
||||
query: searchEnabled ? debouncedQuery : "",
|
||||
includePages: true,
|
||||
limit: 20,
|
||||
});
|
||||
|
||||
const isSearching = !!searchEnabled;
|
||||
|
||||
const selectedId =
|
||||
selection?.type === "space" ? selection.spaceId : selection?.pageId ?? null;
|
||||
|
||||
const updateSelection = useCallback(
|
||||
(next: DestinationSelection | null) => {
|
||||
setSelection(next);
|
||||
onSelectionChange(next);
|
||||
},
|
||||
[onSelectionChange],
|
||||
);
|
||||
|
||||
const handleSearchResultClick = (page: Partial<IPage>) => {
|
||||
if (!page.space || !page.id) return;
|
||||
|
||||
updateSelection({
|
||||
type: "page",
|
||||
spaceId: page.space.id,
|
||||
pageId: page.id,
|
||||
page,
|
||||
space: page.space,
|
||||
});
|
||||
setSearchQuery("");
|
||||
};
|
||||
|
||||
const handleSelectSpace = useCallback(
|
||||
(space: ISpace) => {
|
||||
updateSelection({ type: "space", spaceId: space.id, space });
|
||||
},
|
||||
[updateSelection],
|
||||
);
|
||||
|
||||
const handleSelectPage = useCallback(
|
||||
(page: Partial<IPage>, space: ISpace) => {
|
||||
if (!page.id) return;
|
||||
updateSelection({
|
||||
type: "page",
|
||||
spaceId: page.spaceId ?? space.id,
|
||||
pageId: page.id,
|
||||
page,
|
||||
space,
|
||||
});
|
||||
},
|
||||
[updateSelection],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
leftSection={<IconSearch size={16} />}
|
||||
placeholder={t("Search pages and spaces...")}
|
||||
variant="filled"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.currentTarget.value)}
|
||||
className={classes.searchInput}
|
||||
/>
|
||||
|
||||
<ScrollArea h="50vh" offsetScrollbars className={classes.scrollArea}>
|
||||
{isSearching ? (
|
||||
searchLoading ? (
|
||||
<div className={classes.emptyState}>
|
||||
<Loader size="xs" />
|
||||
</div>
|
||||
) : searchData?.pages && searchData.pages.length > 0 ? (
|
||||
searchData.pages.map(
|
||||
(page) =>
|
||||
page && (
|
||||
<div
|
||||
key={page.id}
|
||||
className={classes.searchResult}
|
||||
onClick={() => handleSearchResultClick(page)}
|
||||
>
|
||||
<div className={classes.iconWrapper}>
|
||||
{page.icon ? (
|
||||
page.icon
|
||||
) : (
|
||||
<IconFile
|
||||
size={16}
|
||||
color="var(--mantine-color-gray-5)"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.pageTitle}>
|
||||
{page.title || t("Untitled")}
|
||||
</div>
|
||||
{page.space && (
|
||||
<div className={classes.spaceName}>
|
||||
{page.space.name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
)
|
||||
) : (
|
||||
<div className={classes.emptyState}>{t("No results found")}</div>
|
||||
)
|
||||
) : spacesLoading ? (
|
||||
<div className={classes.emptyState}>
|
||||
<Loader size="xs" />
|
||||
</div>
|
||||
) : (
|
||||
spacesData?.items?.map((space) => (
|
||||
<SpaceRow
|
||||
key={space.id}
|
||||
space={space}
|
||||
limit={pageLimit}
|
||||
selectedId={selectedId}
|
||||
excludePageId={excludePageId}
|
||||
onSelectSpace={handleSelectSpace}
|
||||
onSelectPage={handleSelectPage}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ScrollArea>
|
||||
|
||||
{selection && (
|
||||
<div className={classes.selectedIndicator}>
|
||||
{selection.type === "space"
|
||||
? selection.space.name
|
||||
: `${selection.space.name} / ${selection.page.title || t("Untitled")}`}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ISpace } from "@/features/space/types/space.types";
|
||||
import { IPage } from "@/features/page/types/page.types";
|
||||
|
||||
export type DestinationSelection =
|
||||
| { type: "space"; spaceId: string; space: ISpace }
|
||||
| {
|
||||
type: "page";
|
||||
spaceId: string;
|
||||
pageId: string;
|
||||
page: Partial<IPage>;
|
||||
space: Partial<ISpace>;
|
||||
};
|
||||
|
||||
export type DestinationPickerModalProps = {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
actionLabel: string;
|
||||
onSelect: (selection: DestinationSelection) => void | Promise<void>;
|
||||
loading?: boolean;
|
||||
excludePageId?: string;
|
||||
pageLimit?: number;
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { Loader } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getSidebarPages } from "@/features/page/services/page-service";
|
||||
import { IPage } from "@/features/page/types/page.types";
|
||||
import { IPagination } from "@/lib/types";
|
||||
import { PageRow } from "./page-row";
|
||||
import classes from "./destination-picker.module.css";
|
||||
|
||||
type PageChildrenProps = {
|
||||
spaceId: string;
|
||||
pageId?: string;
|
||||
depth: number;
|
||||
limit: number;
|
||||
selectedId: string | null;
|
||||
excludePageId?: string;
|
||||
onSelectPage: (page: Partial<IPage>) => void;
|
||||
};
|
||||
|
||||
export function PageChildren({
|
||||
spaceId,
|
||||
pageId,
|
||||
depth,
|
||||
limit,
|
||||
selectedId,
|
||||
excludePageId,
|
||||
onSelectPage,
|
||||
}: PageChildrenProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data, isLoading, hasNextPage, fetchNextPage } = useInfiniteQuery({
|
||||
queryKey: ["destination-pages", spaceId, pageId ?? "root"],
|
||||
queryFn: ({ pageParam }) =>
|
||||
getSidebarPages({
|
||||
spaceId,
|
||||
pageId,
|
||||
limit,
|
||||
cursor: pageParam,
|
||||
}),
|
||||
initialPageParam: undefined as string | undefined,
|
||||
getNextPageParam: (lastPage: IPagination<IPage>) =>
|
||||
lastPage.meta?.nextCursor ?? undefined,
|
||||
});
|
||||
|
||||
const pages = data?.pages.flatMap((page) => page.items) ?? [];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={classes.emptyState}>
|
||||
<Loader size="xs" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (pages.length === 0) {
|
||||
return (
|
||||
<div className={classes.emptyState}>
|
||||
{pageId ? t("No pages inside") : t("No pages in this space")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{pages.map((page) => (
|
||||
<PageRow
|
||||
key={page.id}
|
||||
page={page}
|
||||
depth={depth}
|
||||
limit={limit}
|
||||
selectedId={selectedId}
|
||||
excludePageId={excludePageId}
|
||||
onSelect={onSelectPage}
|
||||
/>
|
||||
))}
|
||||
{hasNextPage && (
|
||||
<div className={classes.loadMore} onClick={() => fetchNextPage()}>
|
||||
{t("Load more")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useState } from "react";
|
||||
import { IconChevronRight, IconFile } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IPage } from "@/features/page/types/page.types";
|
||||
import { PageChildren } from "./page-children";
|
||||
import classes from "./destination-picker.module.css";
|
||||
|
||||
type PageRowProps = {
|
||||
page: Partial<IPage>;
|
||||
depth: number;
|
||||
limit: number;
|
||||
selectedId: string | null;
|
||||
excludePageId?: string;
|
||||
onSelect: (page: Partial<IPage>) => void;
|
||||
};
|
||||
|
||||
export function PageRow({
|
||||
page,
|
||||
depth,
|
||||
limit,
|
||||
selectedId,
|
||||
excludePageId,
|
||||
onSelect,
|
||||
}: PageRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const isExcluded = page.id === excludePageId;
|
||||
const isSelected = page.id === selectedId;
|
||||
|
||||
const rowClasses = [
|
||||
classes.pageRow,
|
||||
isSelected && classes.selected,
|
||||
isExcluded && classes.disabled,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={rowClasses}
|
||||
style={{ paddingLeft: depth * 20 + 12 }}
|
||||
onClick={() => !isExcluded && onSelect(page)}
|
||||
>
|
||||
{page.hasChildren ? (
|
||||
<div
|
||||
className={`${classes.chevron} ${expanded ? classes.chevronExpanded : ""}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setExpanded(!expanded);
|
||||
}}
|
||||
>
|
||||
<IconChevronRight size={14} />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ width: 20, flexShrink: 0 }} />
|
||||
)}
|
||||
|
||||
<div className={classes.iconWrapper}>
|
||||
{page.icon ? (
|
||||
page.icon
|
||||
) : (
|
||||
<IconFile
|
||||
size={16}
|
||||
color="var(--mantine-color-gray-5)"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={classes.pageTitle}>
|
||||
{page.title || t("Untitled")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{expanded && page.hasChildren && (
|
||||
<PageChildren
|
||||
spaceId={page.spaceId}
|
||||
pageId={page.id}
|
||||
depth={depth + 1}
|
||||
limit={limit}
|
||||
selectedId={selectedId}
|
||||
excludePageId={excludePageId}
|
||||
onSelectPage={onSelect}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { useState } from "react";
|
||||
import { Tooltip } from "@mantine/core";
|
||||
import { IconChevronRight, IconLock } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ISpace } from "@/features/space/types/space.types";
|
||||
import { IPage } from "@/features/page/types/page.types";
|
||||
import { SpaceRole } from "@/lib/types";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar";
|
||||
import { AvatarIconType } from "@/features/attachments/types/attachment.types";
|
||||
import { PageChildren } from "./page-children";
|
||||
import classes from "./destination-picker.module.css";
|
||||
|
||||
type SpaceRowProps = {
|
||||
space: ISpace;
|
||||
limit: number;
|
||||
selectedId: string | null;
|
||||
excludePageId?: string;
|
||||
onSelectSpace: (space: ISpace) => void;
|
||||
onSelectPage: (page: Partial<IPage>, space: ISpace) => void;
|
||||
};
|
||||
|
||||
export function SpaceRow({
|
||||
space,
|
||||
limit,
|
||||
selectedId,
|
||||
excludePageId,
|
||||
onSelectSpace,
|
||||
onSelectPage,
|
||||
}: SpaceRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const writable =
|
||||
!!space.membership?.role && space.membership.role !== SpaceRole.READER;
|
||||
const isSelected = space.id === selectedId;
|
||||
|
||||
const rowClasses = [
|
||||
classes.spaceRow,
|
||||
isSelected && classes.selected,
|
||||
!writable && classes.disabled,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
const rowContent = (
|
||||
<div
|
||||
className={rowClasses}
|
||||
onClick={() => writable && onSelectSpace(space)}
|
||||
>
|
||||
{writable ? (
|
||||
<div
|
||||
className={`${classes.chevron} ${expanded ? classes.chevronExpanded : ""}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setExpanded(!expanded);
|
||||
}}
|
||||
>
|
||||
<IconChevronRight size={14} />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ width: 20, flexShrink: 0 }} />
|
||||
)}
|
||||
|
||||
<CustomAvatar
|
||||
name={space.name}
|
||||
avatarUrl={space.logo}
|
||||
type={AvatarIconType.SPACE_ICON}
|
||||
size={22}
|
||||
/>
|
||||
|
||||
<div className={classes.pageTitle}>{space.name}</div>
|
||||
|
||||
{!writable && (
|
||||
<IconLock
|
||||
size={14}
|
||||
color="var(--mantine-color-gray-5)"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{writable ? (
|
||||
rowContent
|
||||
) : (
|
||||
<Tooltip
|
||||
label={t("You don't have permission to create pages here")}
|
||||
position="right"
|
||||
withArrow
|
||||
>
|
||||
<div>{rowContent}</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{expanded && writable && (
|
||||
<PageChildren
|
||||
spaceId={space.id}
|
||||
depth={1}
|
||||
limit={limit}
|
||||
selectedId={selectedId}
|
||||
excludePageId={excludePageId}
|
||||
onSelectPage={(page) => onSelectPage(page, space)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Stack, Text } from "@mantine/core";
|
||||
import { type TablerIcon } from "@tabler/icons-react";
|
||||
import { ReactNode } from "react";
|
||||
import classes from "./empty-state.module.css";
|
||||
|
||||
type EmptyStateProps = {
|
||||
icon: TablerIcon;
|
||||
title: string;
|
||||
description?: string;
|
||||
action?: ReactNode;
|
||||
};
|
||||
|
||||
export function EmptyState({ icon: Icon, title, description, action }: EmptyStateProps) {
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Stack align="center" gap="xs">
|
||||
<Icon size={40} stroke={1.5} color="var(--mantine-color-dimmed)" />
|
||||
<Text size="lg" fw={500}>
|
||||
{title}
|
||||
</Text>
|
||||
{description && (
|
||||
<Text size="sm" c="dimmed" maw={350}>
|
||||
{description}
|
||||
</Text>
|
||||
)}
|
||||
{action}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import { useChatInfoQuery } from "../queries/ai-chat-query";
|
||||
import { useChatStream } from "../hooks/use-chat-stream";
|
||||
import ChatMessageList from "./chat-message-list";
|
||||
import ChatEmptyState from "./chat-empty-state";
|
||||
import ChatInput from "./chat-input";
|
||||
import type { HomeAiPromptInitialState } from "@/features/home/components/home-ai-prompt";
|
||||
import classes from "../styles/ai-chat.module.css";
|
||||
|
||||
export default function AiChatLayout() {
|
||||
const { chatId } = useParams<{ chatId: string }>();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const chatInfoQuery = useChatInfoQuery(chatId);
|
||||
|
||||
// If the URL points at a chat the user does not own, the info fetch 404s.
|
||||
// Bounce them back to /ai so they cannot interact with any chat UI (including
|
||||
// kicking off orphan uploads) tied to a chat they have no access to.
|
||||
useEffect(() => {
|
||||
if (chatId && chatInfoQuery.isError) {
|
||||
navigate("/ai", { replace: true });
|
||||
}
|
||||
}, [chatId, chatInfoQuery.isError, navigate]);
|
||||
const {
|
||||
messages,
|
||||
streamingContent,
|
||||
streamingToolCalls,
|
||||
isStreaming,
|
||||
error,
|
||||
sendMessage,
|
||||
stopGeneration,
|
||||
hydrateFromServer,
|
||||
} = useChatStream(chatId);
|
||||
|
||||
const autoSentRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatInfoQuery.data?.messages) {
|
||||
hydrateFromServer(chatInfoQuery.data.messages);
|
||||
}
|
||||
}, [chatInfoQuery.data, hydrateFromServer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (autoSentRef.current || chatId) return;
|
||||
const state = location.state as HomeAiPromptInitialState | null;
|
||||
if (!state?.initialContent && !state?.initialAttachments?.length) return;
|
||||
|
||||
autoSentRef.current = true;
|
||||
sendMessage(
|
||||
state.initialContent ?? "",
|
||||
state.initialMentions ?? [],
|
||||
state.initialAttachments ?? [],
|
||||
);
|
||||
navigate(location.pathname, { replace: true, state: null });
|
||||
}, [chatId, location, navigate, sendMessage]);
|
||||
|
||||
const hasMessages = messages.length > 0 || isStreaming || !!chatId;
|
||||
|
||||
// While the redirect effect is running (or if the user is still on this
|
||||
// component for any reason) never render the chat UI for a forbidden chat.
|
||||
if (chatId && chatInfoQuery.isError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
{hasMessages ? (
|
||||
<>
|
||||
<ChatMessageList
|
||||
messages={messages}
|
||||
isStreaming={isStreaming}
|
||||
streamingContent={streamingContent}
|
||||
streamingToolCalls={streamingToolCalls}
|
||||
/>
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
padding: "var(--mantine-spacing-sm) var(--mantine-spacing-lg)",
|
||||
color: "var(--mantine-color-red-6)",
|
||||
fontSize: "var(--mantine-font-size-sm)",
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.inputArea}>
|
||||
<ChatInput
|
||||
isStreaming={isStreaming}
|
||||
onSend={sendMessage}
|
||||
onStop={stopGeneration}
|
||||
chatId={chatId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<ChatEmptyState
|
||||
isStreaming={isStreaming}
|
||||
onSend={sendMessage}
|
||||
onStop={stopGeneration}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { useState, useRef, useEffect, useMemo, useCallback } from "react";
|
||||
import { ActionIcon, Menu, TextInput } from "@mantine/core";
|
||||
import { IconDots, IconTrash, IconEdit } from "@tabler/icons-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { AiChat } from "../types/ai-chat.types";
|
||||
import classes from "../styles/chat-sidebar.module.css";
|
||||
|
||||
type Props = {
|
||||
chat: AiChat;
|
||||
isActive: boolean;
|
||||
onDelete: (chatId: string, title: string | null) => void;
|
||||
onRename: (chatId: string, title: string) => void;
|
||||
};
|
||||
|
||||
function formatChatDate(
|
||||
isoString: string | Date,
|
||||
locale: string | undefined,
|
||||
): string {
|
||||
const date = new Date(isoString);
|
||||
if (Number.isNaN(date.getTime())) return "";
|
||||
|
||||
const now = new Date();
|
||||
const startOfToday = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
).getTime();
|
||||
const ts = date.getTime();
|
||||
const sameYear = date.getFullYear() === now.getFullYear();
|
||||
|
||||
if (ts >= startOfToday) {
|
||||
return date.toLocaleTimeString(locale, {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
if (sameYear) {
|
||||
return date.toLocaleDateString(locale, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
return date.toLocaleDateString(locale, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
export default function AiChatSidebarItem({
|
||||
chat,
|
||||
isActive,
|
||||
onDelete,
|
||||
onRename,
|
||||
}: Props) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [renaming, setRenaming] = useState(false);
|
||||
const [renameValue, setRenameValue] = useState("");
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const formattedDate = useMemo(
|
||||
() => formatChatDate(chat.updatedAt, i18n.language),
|
||||
[chat.updatedAt, i18n.language],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (renaming) {
|
||||
// Wait for the input to be mounted before selecting.
|
||||
const id = window.setTimeout(() => inputRef.current?.select(), 0);
|
||||
return () => window.clearTimeout(id);
|
||||
}
|
||||
}, [renaming]);
|
||||
|
||||
const startRename = useCallback(() => {
|
||||
setRenameValue(chat.title || "");
|
||||
setRenaming(true);
|
||||
}, [chat.title]);
|
||||
|
||||
const submitRename = useCallback(() => {
|
||||
const trimmed = renameValue.trim();
|
||||
if (trimmed && trimmed !== chat.title) {
|
||||
onRename(chat.id, trimmed);
|
||||
}
|
||||
setRenaming(false);
|
||||
}, [renameValue, chat.id, chat.title, onRename]);
|
||||
|
||||
if (renaming) {
|
||||
return (
|
||||
<div className={classes.chatItem} data-active={isActive || undefined}>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
size="xs"
|
||||
variant="unstyled"
|
||||
placeholder={t("Chat name")}
|
||||
value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.currentTarget.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submitRename();
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
setRenaming(false);
|
||||
}
|
||||
}}
|
||||
onBlur={submitRename}
|
||||
classNames={{ input: classes.chatItemRenameInput }}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={`/ai/chat/${chat.id}`}
|
||||
className={classes.chatItem}
|
||||
data-active={isActive || undefined}
|
||||
>
|
||||
<span className={classes.chatItemTitle}>
|
||||
{chat.title || t("Untitled chat")}
|
||||
</span>
|
||||
<span className={classes.chatItemDate}>{formattedDate}</span>
|
||||
<div className={classes.chatItemActions}>
|
||||
<Menu position="bottom-end" withinPortal>
|
||||
<Menu.Target>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
size="xs"
|
||||
color="gray"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<IconDots size={14} />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
leftSection={<IconEdit size={14} />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
startRename();
|
||||
}}
|
||||
>
|
||||
{t("Rename")}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<IconTrash size={14} />}
|
||||
color="red"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onDelete(chat.id, chat.title);
|
||||
}}
|
||||
>
|
||||
{t("Delete")}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
ActionIcon,
|
||||
Center,
|
||||
Text,
|
||||
TextInput,
|
||||
Loader,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useDebouncedValue } from "@mantine/hooks";
|
||||
import { IconPlus, IconSearch, IconMessageCircle2 } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useChatsQuery,
|
||||
useDeleteChatMutation,
|
||||
useUpdateChatTitleMutation,
|
||||
useSearchChatsQuery,
|
||||
} from "../queries/ai-chat-query";
|
||||
import AiChatSidebarItem from "./ai-chat-sidebar-item";
|
||||
import { groupChatsByAge } from "../utils/group-chats-by-age";
|
||||
import classes from "../styles/chat-sidebar.module.css";
|
||||
|
||||
export default function AiChatSidebar() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { chatId } = useParams<{ chatId: string }>();
|
||||
const [search, setSearch] = useState("");
|
||||
const [debouncedSearch] = useDebouncedValue(search, 300);
|
||||
const chatsQuery = useChatsQuery();
|
||||
const searchQuery = useSearchChatsQuery(debouncedSearch);
|
||||
const deleteMutation = useDeleteChatMutation();
|
||||
const renameMutation = useUpdateChatTitleMutation();
|
||||
|
||||
const chats = useMemo(() => {
|
||||
if (debouncedSearch) {
|
||||
return searchQuery.data || [];
|
||||
}
|
||||
return chatsQuery.data?.pages.flatMap((p) => p.items) || [];
|
||||
}, [debouncedSearch, searchQuery.data, chatsQuery.data]);
|
||||
|
||||
const groupedChats = useMemo(() => groupChatsByAge(chats, t), [chats, t]);
|
||||
|
||||
const sentinelRef = useRef<HTMLDivElement>(null);
|
||||
const { hasNextPage, fetchNextPage, isFetchingNextPage } = chatsQuery;
|
||||
const isSearching = Boolean(debouncedSearch);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSearching) return;
|
||||
const sentinel = sentinelRef.current;
|
||||
if (!sentinel) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
|
||||
fetchNextPage();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.1 },
|
||||
);
|
||||
|
||||
observer.observe(sentinel);
|
||||
return () => observer.disconnect();
|
||||
}, [isSearching, hasNextPage, isFetchingNextPage, fetchNextPage]);
|
||||
|
||||
const handleNewChat = useCallback(
|
||||
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (
|
||||
event.button !== 0 ||
|
||||
event.ctrlKey ||
|
||||
event.metaKey ||
|
||||
event.shiftKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
navigate("/ai");
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(id: string, title: string | null) => {
|
||||
modals.openConfirmModal({
|
||||
title: t("Delete chat"),
|
||||
centered: true,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
{t("Are you sure you want to delete '{{title}}'? This action cannot be undone.", {
|
||||
title: title || t("Untitled"),
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: t("Delete"), cancel: t("Cancel") },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => {
|
||||
deleteMutation.mutate(id, {
|
||||
onSuccess: () => {
|
||||
if (chatId === id) {
|
||||
navigate("/ai");
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
[deleteMutation, chatId, navigate, t],
|
||||
);
|
||||
|
||||
const handleRename = useCallback(
|
||||
(chatId: string, title: string) => {
|
||||
renameMutation.mutate({ chatId, title });
|
||||
},
|
||||
[renameMutation],
|
||||
);
|
||||
|
||||
const isLoading = chatsQuery.isLoading || searchQuery.isLoading;
|
||||
|
||||
return (
|
||||
<div className={classes.sidebar}>
|
||||
<div className={classes.header}>
|
||||
<span className={classes.title}>{t("AI Chat")}</span>
|
||||
<Tooltip label={t("New chat")} openDelay={250} withArrow>
|
||||
<ActionIcon
|
||||
component={Link}
|
||||
to="/ai"
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
onClick={handleNewChat}
|
||||
aria-label={t("New chat")}
|
||||
>
|
||||
<IconPlus size={18} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<TextInput
|
||||
className={classes.searchInput}
|
||||
placeholder="Search chats..."
|
||||
leftSection={<IconSearch size={14} />}
|
||||
size="xs"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<div className={classes.chatList}>
|
||||
{isLoading && <Loader size="xs" mx="auto" mt="md" />}
|
||||
{!isLoading && chats.length === 0 && (
|
||||
<div className={classes.chatListEmpty}>
|
||||
<IconMessageCircle2
|
||||
size={28}
|
||||
stroke={1.5}
|
||||
className={classes.chatListEmptyIcon}
|
||||
/>
|
||||
<div className={classes.chatListEmptyTitle}>
|
||||
{isSearching ? t("No chats found") : t("No conversations yet")}
|
||||
</div>
|
||||
<div className={classes.chatListEmptyHint}>
|
||||
{isSearching
|
||||
? t("Try a different search term.")
|
||||
: t("Start a new chat to see it here.")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isSearching
|
||||
? chats.map((chat) => (
|
||||
<AiChatSidebarItem
|
||||
key={chat.id}
|
||||
chat={chat}
|
||||
isActive={chat.id === chatId}
|
||||
onDelete={handleDelete}
|
||||
onRename={handleRename}
|
||||
/>
|
||||
))
|
||||
: groupedChats.map((group) => (
|
||||
<div key={group.key} className={classes.chatGroup}>
|
||||
<div className={classes.chatGroupLabel}>{group.label}</div>
|
||||
{group.chats.map((chat) => (
|
||||
<AiChatSidebarItem
|
||||
key={chat.id}
|
||||
chat={chat}
|
||||
isActive={chat.id === chatId}
|
||||
onDelete={handleDelete}
|
||||
onRename={handleRename}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
{!isSearching && (
|
||||
<>
|
||||
<div ref={sentinelRef} style={{ height: 1 }} />
|
||||
{isFetchingNextPage && (
|
||||
<Center py="xs">
|
||||
<Loader size="xs" />
|
||||
</Center>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { useState } from "react";
|
||||
import { TextInput, Loader, Text, ScrollArea } from "@mantine/core";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
import { useChatsQuery, useSearchChatsQuery } from "../queries/ai-chat-query";
|
||||
import { useDebouncedValue } from "@mantine/hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classes from "../styles/aside-chat-panel.module.css";
|
||||
|
||||
type Props = {
|
||||
activeChatId: string | undefined;
|
||||
onSelect: (chatId: string) => void;
|
||||
};
|
||||
|
||||
export default function AsideChatHistory({ activeChatId, onSelect }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [debouncedSearch] = useDebouncedValue(searchValue, 300);
|
||||
|
||||
const chatsQuery = useChatsQuery();
|
||||
const searchQuery = useSearchChatsQuery(debouncedSearch);
|
||||
|
||||
const isSearching = debouncedSearch.length > 0;
|
||||
const chats = isSearching
|
||||
? (searchQuery.data ?? [])
|
||||
: (chatsQuery.data?.pages.flatMap((p) => p.items) ?? []);
|
||||
const isLoading = isSearching ? searchQuery.isLoading : chatsQuery.isLoading;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextInput
|
||||
placeholder={t("Search chats...")}
|
||||
leftSection={<IconSearch size={14} />}
|
||||
size="xs"
|
||||
mb="xs"
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
{isLoading ? (
|
||||
<div style={{ display: "flex", justifyContent: "center", padding: 16 }}>
|
||||
<Loader size="sm" />
|
||||
</div>
|
||||
) : chats.length === 0 ? (
|
||||
<Text size="sm" c="dimmed" ta="center" py="md">
|
||||
{isSearching ? t("No chats found") : t("No chat history")}
|
||||
</Text>
|
||||
) : (
|
||||
<ScrollArea.Autosize mah={300} scrollbars="y">
|
||||
<div className={classes.historyList}>
|
||||
{chats.map((chat) => (
|
||||
<div
|
||||
key={chat.id}
|
||||
className={classes.historyItem}
|
||||
data-active={chat.id === activeChatId || undefined}
|
||||
onClick={() => onSelect(chat.id)}
|
||||
>
|
||||
<span className={classes.historyItemTitle}>
|
||||
{chat.title || t("Untitled chat")}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea.Autosize>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { ActionIcon, Popover, Tooltip, UnstyledButton } from "@mantine/core";
|
||||
import {
|
||||
IconPlus,
|
||||
IconChevronDown,
|
||||
IconArrowsDiagonal,
|
||||
IconX,
|
||||
IconSparkles,
|
||||
IconFileText,
|
||||
IconLanguage,
|
||||
IconSearch,
|
||||
} from "@tabler/icons-react";
|
||||
import { useAtom } from "jotai";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { asideStateAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom";
|
||||
import { usePageQuery } from "@/features/page/queries/page-query";
|
||||
import { extractPageSlugId } from "@/lib";
|
||||
import { useChatStream } from "../hooks/use-chat-stream";
|
||||
import { useChatInfoQuery } from "../queries/ai-chat-query";
|
||||
import ChatMessageList from "./chat-message-list";
|
||||
import ChatInput from "./chat-input";
|
||||
import AsideChatHistory from "./aside-chat-history";
|
||||
import type { ChatAttachment, PageMention } from "../types/ai-chat.types";
|
||||
import classes from "../styles/aside-chat-panel.module.css";
|
||||
|
||||
type QuickAction = {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
prompt: string;
|
||||
};
|
||||
|
||||
export default function AsideChatPanel() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [, setAsideState] = useAtom(asideStateAtom);
|
||||
const [chatId, setChatId] = useState<string | undefined>(undefined);
|
||||
const [historyOpen, setHistoryOpen] = useState(false);
|
||||
const [contextPages, setContextPages] = useState<PageMention[]>([]);
|
||||
const { pageSlug } = useParams();
|
||||
const slugId = extractPageSlugId(pageSlug);
|
||||
const { data: page } = usePageQuery({ pageId: slugId });
|
||||
|
||||
const chatInfoQuery = useChatInfoQuery(chatId);
|
||||
const {
|
||||
messages,
|
||||
streamingContent,
|
||||
streamingToolCalls,
|
||||
isStreaming,
|
||||
error,
|
||||
sendMessage,
|
||||
stopGeneration,
|
||||
hydrateFromServer,
|
||||
} = useChatStream(chatId, {
|
||||
onChatCreated: (newChatId) => {
|
||||
setChatId(newChatId);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (page && !chatId) {
|
||||
setContextPages([{ id: page.id, title: page.title || "", slugId: page.slugId }]);
|
||||
}
|
||||
}, [page, chatId]);
|
||||
|
||||
const handleRemoveContextPage = useCallback((pageId: string) => {
|
||||
setContextPages((prev) => prev.filter((p) => p.id !== pageId));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatInfoQuery.data?.messages) {
|
||||
hydrateFromServer(chatInfoQuery.data.messages);
|
||||
}
|
||||
}, [chatInfoQuery.data, hydrateFromServer]);
|
||||
|
||||
// Drop the open chatId if the current user lost access to it (404/403 on
|
||||
// the info fetch). Reverts the panel to a fresh chat instead of presenting
|
||||
// an input tied to a chat the user does not own.
|
||||
useEffect(() => {
|
||||
if (chatId && chatInfoQuery.isError) {
|
||||
setChatId(undefined);
|
||||
}
|
||||
}, [chatId, chatInfoQuery.isError]);
|
||||
|
||||
const handleNewChat = useCallback(
|
||||
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (
|
||||
event.button !== 0 ||
|
||||
event.ctrlKey ||
|
||||
event.metaKey ||
|
||||
event.shiftKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
setChatId(undefined);
|
||||
if (page) {
|
||||
setContextPages([
|
||||
{ id: page.id, title: page.title || "", slugId: page.slugId },
|
||||
]);
|
||||
}
|
||||
},
|
||||
[page],
|
||||
);
|
||||
|
||||
const handleSelectChat = useCallback((selectedChatId: string) => {
|
||||
setChatId(selectedChatId);
|
||||
setHistoryOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleExpand = useCallback(() => {
|
||||
if (chatId) {
|
||||
navigate(`/ai/chat/${chatId}`);
|
||||
} else {
|
||||
navigate("/ai");
|
||||
}
|
||||
setAsideState({ tab: "", isAsideOpen: false });
|
||||
}, [chatId, navigate, setAsideState]);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setAsideState({ tab: "", isAsideOpen: false });
|
||||
}, [setAsideState]);
|
||||
|
||||
const handleSend = useCallback(
|
||||
(content: string, mentions: PageMention[], attachments: ChatAttachment[]) => {
|
||||
const contextPageId = contextPages.length > 0 ? contextPages[0].id : undefined;
|
||||
sendMessage(content, mentions, attachments, contextPageId);
|
||||
},
|
||||
[sendMessage, contextPages],
|
||||
);
|
||||
|
||||
const handleQuickAction = useCallback(
|
||||
(prompt: string) => {
|
||||
handleSend(prompt, [], []);
|
||||
},
|
||||
[handleSend],
|
||||
);
|
||||
|
||||
const hasMessages = messages.length > 0 || isStreaming;
|
||||
|
||||
const quickActions: QuickAction[] = [
|
||||
{ icon: <IconFileText size={16} />, label: t("Summarize this page"), prompt: "Summarize this page" },
|
||||
{ icon: <IconLanguage size={16} />, label: t("Translate this page"), prompt: "Translate this page" },
|
||||
{ icon: <IconSearch size={16} />, label: t("Analyze for insights"), prompt: "Analyze this page for insights" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={classes.panel}>
|
||||
<div className={classes.toolbar}>
|
||||
<Popover
|
||||
opened={historyOpen}
|
||||
onChange={setHistoryOpen}
|
||||
position="bottom-start"
|
||||
width={280}
|
||||
shadow="md"
|
||||
>
|
||||
<Popover.Target>
|
||||
<UnstyledButton
|
||||
className={classes.titleButton}
|
||||
onClick={() => setHistoryOpen((o) => !o)}
|
||||
>
|
||||
<span className={classes.titleText}>
|
||||
{chatInfoQuery.data?.chat?.title || t("New chat")}
|
||||
</span>
|
||||
<IconChevronDown size={16} stroke={1.75} />
|
||||
</UnstyledButton>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
<AsideChatHistory activeChatId={chatId} onSelect={handleSelectChat} />
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
|
||||
<div className={classes.toolbarSpacer} />
|
||||
|
||||
<Tooltip label={t("New chat")} openDelay={250}>
|
||||
<ActionIcon
|
||||
component="a"
|
||||
href="/ai"
|
||||
variant="subtle"
|
||||
color="dark"
|
||||
onClick={handleNewChat}
|
||||
>
|
||||
<IconPlus size={20} stroke={1.75} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label={t("Open full page")} openDelay={250}>
|
||||
<ActionIcon variant="subtle" color="dark" onClick={handleExpand}>
|
||||
<IconArrowsDiagonal size={18} stroke={1.5} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label={t("Close")} openDelay={250}>
|
||||
<ActionIcon variant="subtle" color="dark" onClick={handleClose}>
|
||||
<IconX size={20} stroke={1.75} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
padding: "var(--mantine-spacing-xs) var(--mantine-spacing-sm)",
|
||||
color: "var(--mantine-color-red-6)",
|
||||
fontSize: "var(--mantine-font-size-xs)",
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasMessages ? (
|
||||
<>
|
||||
<div className={classes.messages} data-aside-chat>
|
||||
<ChatMessageList
|
||||
messages={messages}
|
||||
isStreaming={isStreaming}
|
||||
streamingContent={streamingContent}
|
||||
streamingToolCalls={streamingToolCalls}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className={classes.emptyState}>
|
||||
<IconSparkles size={36} stroke={1.5} className={classes.emptyStateIcon} />
|
||||
<div className={classes.emptyStateTitle}>{t("How can I help you today?")}</div>
|
||||
<div className={classes.quickActions}>
|
||||
{quickActions.map((action) => (
|
||||
<button
|
||||
key={action.label}
|
||||
type="button"
|
||||
className={classes.quickAction}
|
||||
onClick={() => handleQuickAction(action.prompt)}
|
||||
>
|
||||
<span className={classes.quickActionIcon}>{action.icon}</span>
|
||||
{action.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={classes.inputArea}>
|
||||
<ChatInput
|
||||
isStreaming={isStreaming}
|
||||
onSend={handleSend}
|
||||
onStop={stopGeneration}
|
||||
placeholder={t("Ask anything...")}
|
||||
autofocus={false}
|
||||
contextPages={contextPages}
|
||||
onRemoveContextPage={handleRemoveContextPage}
|
||||
variant="flat"
|
||||
chatId={chatId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
IconSparkles,
|
||||
IconSearch,
|
||||
IconFilePlus,
|
||||
IconEdit,
|
||||
IconFileText,
|
||||
} from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ChatInput from "./chat-input";
|
||||
import type { ChatAttachment, PageMention } from "../types/ai-chat.types";
|
||||
import classes from "../styles/ai-chat.module.css";
|
||||
|
||||
type Suggestion = {
|
||||
icon: React.ReactNode;
|
||||
text: string;
|
||||
prompt: string;
|
||||
};
|
||||
|
||||
const SUGGESTIONS: Suggestion[] = [
|
||||
{
|
||||
icon: <IconSearch size={16} />,
|
||||
text: "Search across all pages",
|
||||
prompt: "Search for pages about ",
|
||||
},
|
||||
{
|
||||
icon: <IconFilePlus size={16} />,
|
||||
text: "Create a new page",
|
||||
prompt: "Create a new page titled ",
|
||||
},
|
||||
{
|
||||
icon: <IconFileText size={16} />,
|
||||
text: "Summarize a page",
|
||||
prompt: "Summarize the page @",
|
||||
},
|
||||
{
|
||||
icon: <IconEdit size={16} />,
|
||||
text: "Update page content",
|
||||
prompt: "Update the page @",
|
||||
},
|
||||
];
|
||||
|
||||
type Props = {
|
||||
isStreaming: boolean;
|
||||
onSend: (content: string, mentions: PageMention[], attachments: ChatAttachment[]) => void;
|
||||
onStop: () => void;
|
||||
};
|
||||
|
||||
export default function ChatEmptyState({ isStreaming, onSend, onStop }: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSuggestionClick = (prompt: string) => {
|
||||
onSend(prompt, [], []);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.emptyState}>
|
||||
<IconSparkles size={48} stroke={1.5} className={classes.emptyStateIcon} />
|
||||
<div className={classes.emptyStateBrand}>{t("Docmost AI")}</div>
|
||||
<div className={classes.emptyStateTitle}>
|
||||
{t("What can I help you with?")}
|
||||
</div>
|
||||
|
||||
<div className={classes.emptyStateInput}>
|
||||
<ChatInput
|
||||
isStreaming={isStreaming}
|
||||
onSend={onSend}
|
||||
onStop={onStop}
|
||||
placeholder="Ask anything... Use @ to mention pages"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={classes.suggestionsSection}>
|
||||
<div className={classes.suggestionsLabel}>Get started</div>
|
||||
<div className={classes.suggestionsGrid}>
|
||||
{SUGGESTIONS.map((s) => (
|
||||
<button
|
||||
key={s.text}
|
||||
type="button"
|
||||
className={classes.suggestionCard}
|
||||
onClick={() => handleSuggestionClick(s.prompt)}
|
||||
>
|
||||
<span className={classes.suggestionIcon}>{s.icon}</span>
|
||||
<span className={classes.suggestionText}>{s.text}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
import { useCallback, useRef, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconArrowUp, IconPaperclip, IconPlayerStopFilled, IconX, IconFile, IconPhoto, IconPlus, IconAt, IconFileText } from "@tabler/icons-react";
|
||||
import { Popover } from "@mantine/core";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { EditorContent, ReactNodeViewRenderer, useEditor } from "@tiptap/react";
|
||||
import { Placeholder } from "@tiptap/extension-placeholder";
|
||||
import { CharacterCount } from "@tiptap/extensions";
|
||||
import { StarterKit } from "@tiptap/starter-kit";
|
||||
import { Mention, LinkExtension } from "@docmost/editor-ext";
|
||||
import EmojiCommand from "@/features/editor/extensions/emoji-command";
|
||||
import mentionRenderItems from "@/features/editor/components/mention/mention-suggestion";
|
||||
import MentionView from "@/features/editor/components/mention/mention-view";
|
||||
import { uploadChatFile } from "../services/ai-chat-service";
|
||||
import type { ChatAttachment, PageMention } from "../types/ai-chat.types";
|
||||
import classes from "../styles/chat-input.module.css";
|
||||
|
||||
type PendingAttachment = ChatAttachment & { uploading: boolean };
|
||||
|
||||
const IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"];
|
||||
const ACCEPTED_FILE_TYPES = ".pdf,.docx,.txt,.csv,.md,.png,.jpg,.jpeg,.webp";
|
||||
// Kept in sync with MAX_ATTACHMENTS_PER_MESSAGE in apps/server/src/ee/ai-chat/ai-chat-limits.ts
|
||||
const MAX_ATTACHMENTS_PER_MESSAGE = 5;
|
||||
|
||||
type Props = {
|
||||
isStreaming: boolean;
|
||||
onSend: (content: string, mentions: PageMention[], attachments: ChatAttachment[]) => void;
|
||||
onStop: () => void;
|
||||
placeholder?: string;
|
||||
autofocus?: boolean;
|
||||
contextPages?: PageMention[];
|
||||
onRemoveContextPage?: (pageId: string) => void;
|
||||
variant?: "card" | "flat";
|
||||
showDisclaimer?: boolean;
|
||||
chatId?: string;
|
||||
};
|
||||
|
||||
function extractMentions(json: any): PageMention[] {
|
||||
const mentions: PageMention[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
function walk(node: any) {
|
||||
if (node.type === "mention" && node.attrs?.entityType === "page" && node.attrs?.entityId) {
|
||||
if (!seen.has(node.attrs.entityId)) {
|
||||
seen.add(node.attrs.entityId);
|
||||
mentions.push({
|
||||
id: node.attrs.entityId,
|
||||
title: node.attrs.label || "",
|
||||
slugId: node.attrs.slugId || "",
|
||||
});
|
||||
}
|
||||
}
|
||||
if (node.content) {
|
||||
for (const child of node.content) {
|
||||
walk(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(json);
|
||||
return mentions;
|
||||
}
|
||||
|
||||
function editorJsonToText(json: any): string {
|
||||
let text = "";
|
||||
|
||||
function walk(node: any) {
|
||||
if (node.type === "text") {
|
||||
text += node.text || "";
|
||||
} else if (node.type === "mention") {
|
||||
text += `@${node.attrs?.label || ""}`;
|
||||
} else if (node.type === "paragraph") {
|
||||
if (text.length > 0) text += "\n";
|
||||
if (node.content) {
|
||||
for (const child of node.content) {
|
||||
walk(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (node.content) {
|
||||
for (const child of node.content) {
|
||||
walk(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(json);
|
||||
return text;
|
||||
}
|
||||
|
||||
export default function ChatInput({
|
||||
isStreaming,
|
||||
onSend,
|
||||
onStop,
|
||||
placeholder,
|
||||
autofocus = true,
|
||||
contextPages,
|
||||
onRemoveContextPage,
|
||||
variant = "card",
|
||||
showDisclaimer = true,
|
||||
chatId,
|
||||
}: Props) {
|
||||
const chatIdRef = useRef(chatId);
|
||||
chatIdRef.current = chatId;
|
||||
const { t } = useTranslation();
|
||||
const [isEmpty, setIsEmpty] = useState(true);
|
||||
const [pendingAttachments, setPendingAttachments] = useState<PendingAttachment[]>([]);
|
||||
const [plusMenuOpen, setPlusMenuOpen] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const onSendRef = useRef(onSend);
|
||||
onSendRef.current = onSend;
|
||||
|
||||
const handleFileSelect = useCallback(async (files: FileList | null) => {
|
||||
if (!files?.length) return;
|
||||
|
||||
const room = MAX_ATTACHMENTS_PER_MESSAGE - pendingAttachments.length;
|
||||
if (room <= 0) {
|
||||
notifications.show({
|
||||
color: "yellow",
|
||||
message: t("You can attach up to {{max}} files per message.", {
|
||||
max: MAX_ATTACHMENTS_PER_MESSAGE,
|
||||
}),
|
||||
});
|
||||
if (fileInputRef.current) fileInputRef.current.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const incoming = Array.from(files);
|
||||
const accepted = incoming.slice(0, room);
|
||||
|
||||
if (incoming.length > accepted.length) {
|
||||
notifications.show({
|
||||
color: "yellow",
|
||||
message: t(
|
||||
"Only the first {{n}} file(s) were added (max {{max}} per message).",
|
||||
{ n: accepted.length, max: MAX_ATTACHMENTS_PER_MESSAGE },
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
for (const file of accepted) {
|
||||
const tempId = `uploading-${Date.now()}-${Math.random()}`;
|
||||
const ext = file.name.split(".").pop()?.toLowerCase() || "";
|
||||
|
||||
const placeholder: PendingAttachment = {
|
||||
id: tempId,
|
||||
fileName: file.name,
|
||||
fileExt: ext,
|
||||
fileSize: file.size,
|
||||
mimeType: file.type,
|
||||
uploading: true,
|
||||
};
|
||||
|
||||
setPendingAttachments((prev) => [...prev, placeholder]);
|
||||
|
||||
try {
|
||||
const uploaded = await uploadChatFile(file, chatIdRef.current);
|
||||
setPendingAttachments((prev) =>
|
||||
prev.map((a) =>
|
||||
a.id === tempId ? { ...uploaded, uploading: false } : a,
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
setPendingAttachments((prev) => prev.filter((a) => a.id !== tempId));
|
||||
}
|
||||
}
|
||||
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = "";
|
||||
}
|
||||
}, [pendingAttachments.length, t]);
|
||||
|
||||
const removeAttachment = useCallback((id: string) => {
|
||||
setPendingAttachments((prev) => prev.filter((a) => a.id !== id));
|
||||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
if (!editor || isStreaming) return;
|
||||
const json = editor.getJSON();
|
||||
const text = editorJsonToText(json).trim();
|
||||
const readyAttachments = pendingAttachments.filter((a) => !a.uploading);
|
||||
if (!text && readyAttachments.length === 0) return;
|
||||
|
||||
const mentions = extractMentions(json);
|
||||
onSendRef.current(text, mentions, readyAttachments);
|
||||
editor.commands.clearContent();
|
||||
editor.commands.focus();
|
||||
setPendingAttachments([]);
|
||||
}, [isStreaming, pendingAttachments]);
|
||||
|
||||
const handleSubmitRef = useRef(handleSubmit);
|
||||
handleSubmitRef.current = handleSubmit;
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
gapcursor: false,
|
||||
dropcursor: false,
|
||||
link: false,
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: placeholder || "Ask anything... Use @ to mention pages",
|
||||
}),
|
||||
CharacterCount.configure({
|
||||
limit: 50000,
|
||||
}),
|
||||
LinkExtension,
|
||||
EmojiCommand,
|
||||
Mention.configure({
|
||||
suggestion: {
|
||||
allowSpaces: true,
|
||||
items: () => [],
|
||||
// @ts-ignore
|
||||
render: mentionRenderItems,
|
||||
},
|
||||
HTMLAttributes: {
|
||||
class: "mention",
|
||||
},
|
||||
}).extend({
|
||||
addNodeView() {
|
||||
this.editor.isInitialized = true;
|
||||
return ReactNodeViewRenderer(MentionView);
|
||||
},
|
||||
}),
|
||||
],
|
||||
editorProps: {
|
||||
handleDOMEvents: {
|
||||
keydown: (_view, event) => {
|
||||
if (
|
||||
["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Enter"].includes(
|
||||
event.key,
|
||||
)
|
||||
) {
|
||||
const emojiCommand = document.querySelector("#emoji-command");
|
||||
const mentionPopup = document.querySelector("#mention");
|
||||
if (emojiCommand || mentionPopup) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
handleSubmitRef.current();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
content: "",
|
||||
editable: true,
|
||||
immediatelyRender: true,
|
||||
shouldRerenderOnTransaction: false,
|
||||
autofocus: autofocus ? "end" : false,
|
||||
onUpdate: ({ editor: e }) => {
|
||||
setIsEmpty(!e.getText().trim());
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editor && autofocus) {
|
||||
editor.commands.focus();
|
||||
}
|
||||
}, [editor]);
|
||||
|
||||
const hasContent = !isEmpty || pendingAttachments.some((a) => !a.uploading) || (contextPages?.length ?? 0) > 0;
|
||||
|
||||
const wrapperClass = variant === "flat" ? classes.inputWrapperFlat : classes.inputWrapper;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={wrapperClass} data-chat-input>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept={ACCEPTED_FILE_TYPES}
|
||||
multiple
|
||||
style={{ display: "none" }}
|
||||
onChange={(e) => handleFileSelect(e.target.files)}
|
||||
/>
|
||||
|
||||
{((contextPages?.length ?? 0) > 0 || pendingAttachments.length > 0) && (
|
||||
<div className={classes.attachmentChips}>
|
||||
{contextPages?.map((page) => (
|
||||
<div key={page.id} className={classes.attachmentChip}>
|
||||
<IconFileText size={14} />
|
||||
<span className={classes.attachmentChipName}>
|
||||
{page.title || "Untitled"}
|
||||
</span>
|
||||
{onRemoveContextPage && (
|
||||
<button
|
||||
type="button"
|
||||
className={classes.attachmentChipRemove}
|
||||
onClick={() => onRemoveContextPage(page.id)}
|
||||
aria-label={`Remove ${page.title}`}
|
||||
>
|
||||
<IconX size={12} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{pendingAttachments.map((attachment) => (
|
||||
<div
|
||||
key={attachment.id}
|
||||
className={`${classes.attachmentChip} ${attachment.uploading ? classes.attachmentChipUploading : ""}`}
|
||||
>
|
||||
{IMAGE_EXTENSIONS.includes(attachment.fileExt) ? (
|
||||
<IconPhoto size={14} />
|
||||
) : (
|
||||
<IconFile size={14} />
|
||||
)}
|
||||
<span className={classes.attachmentChipName}>
|
||||
{attachment.fileName}
|
||||
</span>
|
||||
{!attachment.uploading && (
|
||||
<button
|
||||
type="button"
|
||||
className={classes.attachmentChipRemove}
|
||||
onClick={() => removeAttachment(attachment.id)}
|
||||
aria-label={`Remove ${attachment.fileName}`}
|
||||
>
|
||||
<IconX size={12} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<EditorContent editor={editor} className={classes.editorContent} />
|
||||
<div className={classes.actions}>
|
||||
<Popover opened={plusMenuOpen} onChange={setPlusMenuOpen} position="top-start" width={220} shadow="md">
|
||||
<Popover.Target>
|
||||
<button
|
||||
type="button"
|
||||
className={classes.plusButton}
|
||||
onClick={() => setPlusMenuOpen((o) => !o)}
|
||||
aria-label="Add content"
|
||||
>
|
||||
<IconPlus size={14} />
|
||||
</button>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown p={4}>
|
||||
<button
|
||||
type="button"
|
||||
className={classes.plusMenuItem}
|
||||
onClick={() => {
|
||||
fileInputRef.current?.click();
|
||||
setPlusMenuOpen(false);
|
||||
}}
|
||||
disabled={pendingAttachments.length >= MAX_ATTACHMENTS_PER_MESSAGE}
|
||||
title={
|
||||
pendingAttachments.length >= MAX_ATTACHMENTS_PER_MESSAGE
|
||||
? t("Max {{max}} files per message", {
|
||||
max: MAX_ATTACHMENTS_PER_MESSAGE,
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<IconPaperclip size={16} className={classes.plusMenuIcon} />
|
||||
{t("Add files")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={classes.plusMenuItem}
|
||||
onClick={() => {
|
||||
editor?.commands.insertContent("@");
|
||||
editor?.commands.focus();
|
||||
setPlusMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<IconAt size={16} className={classes.plusMenuIcon} />
|
||||
Mention a page
|
||||
</button>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
|
||||
<div style={{ flex: 1 }} />
|
||||
|
||||
{isStreaming ? (
|
||||
<button
|
||||
type="button"
|
||||
className={classes.stopButton}
|
||||
onClick={onStop}
|
||||
aria-label="Stop generation"
|
||||
>
|
||||
<IconPlayerStopFilled size={14} />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={classes.sendButton}
|
||||
onClick={handleSubmit}
|
||||
disabled={!hasContent}
|
||||
aria-label="Send message"
|
||||
>
|
||||
<IconArrowUp size={16} stroke={2.5} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{showDisclaimer && (
|
||||
<div className={classes.disclaimer}>
|
||||
{t("AI-generated content may not be accurate.")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { useEffect, useRef, useCallback, useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { IconArrowDown, IconAlertTriangle } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { AiChatMessage, AiChatToolCall } from "../types/ai-chat.types";
|
||||
import ChatMessage from "./chat-message";
|
||||
import classes from "../styles/ai-chat.module.css";
|
||||
|
||||
function ChatMessageErrorFallback() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={classes.messageErrorFallback}>
|
||||
<IconAlertTriangle size={14} />
|
||||
<span>{t("Failed to render this message.")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type Props = {
|
||||
messages: AiChatMessage[];
|
||||
isStreaming: boolean;
|
||||
streamingContent: string;
|
||||
streamingToolCalls: AiChatToolCall[];
|
||||
};
|
||||
|
||||
const BOTTOM_THRESHOLD_PX = 32;
|
||||
const SCROLL_UP_THRESHOLD_PX = 5;
|
||||
const SMOOTH_SCROLL_SETTLE_MS = 600;
|
||||
|
||||
export default function ChatMessageList({
|
||||
messages,
|
||||
isStreaming,
|
||||
streamingContent,
|
||||
streamingToolCalls,
|
||||
}: Props) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
const isAtBottomRef = useRef(true);
|
||||
const isAutoScrollingRef = useRef(false);
|
||||
const prevScrollTopRef = useRef(0);
|
||||
const [showScrollButton, setShowScrollButton] = useState(false);
|
||||
|
||||
const scrollToBottom = useCallback((behavior: ScrollBehavior = "smooth") => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
isAutoScrollingRef.current = true;
|
||||
const target = container.scrollHeight - container.clientHeight;
|
||||
container.scrollTo({ top: target, behavior });
|
||||
prevScrollTopRef.current = target;
|
||||
isAtBottomRef.current = true;
|
||||
setShowScrollButton(false);
|
||||
|
||||
if (behavior === "smooth") {
|
||||
setTimeout(() => {
|
||||
isAutoScrollingRef.current = false;
|
||||
if (containerRef.current) {
|
||||
prevScrollTopRef.current = containerRef.current.scrollTop;
|
||||
}
|
||||
}, SMOOTH_SCROLL_SETTLE_MS);
|
||||
} else {
|
||||
isAutoScrollingRef.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleScroll = useCallback(() => {
|
||||
if (isAutoScrollingRef.current) return;
|
||||
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const currentScrollTop = container.scrollTop;
|
||||
const scrolledUp =
|
||||
currentScrollTop < prevScrollTopRef.current - SCROLL_UP_THRESHOLD_PX;
|
||||
prevScrollTopRef.current = currentScrollTop;
|
||||
|
||||
const distanceFromBottom =
|
||||
container.scrollHeight - currentScrollTop - container.clientHeight;
|
||||
const atBottom = distanceFromBottom <= BOTTOM_THRESHOLD_PX;
|
||||
|
||||
if (scrolledUp) {
|
||||
isAtBottomRef.current = atBottom;
|
||||
} else if (atBottom) {
|
||||
isAtBottomRef.current = true;
|
||||
}
|
||||
|
||||
setShowScrollButton(!atBottom);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
container.addEventListener("scroll", handleScroll, { passive: true });
|
||||
return () => container.removeEventListener("scroll", handleScroll);
|
||||
}, [handleScroll]);
|
||||
|
||||
// Instant scroll during streaming to keep up with rapid updates
|
||||
useEffect(() => {
|
||||
if (isAtBottomRef.current) {
|
||||
scrollToBottom("instant");
|
||||
}
|
||||
}, [streamingContent, streamingToolCalls.length, scrollToBottom]);
|
||||
|
||||
// Smooth scroll for new messages. Always force-scroll when the latest
|
||||
// message is from the user (they just sent it), even if they were reading
|
||||
// scrollback.
|
||||
useEffect(() => {
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
const lastIsUser = lastMessage?.role === "user";
|
||||
if (lastIsUser || isAtBottomRef.current) {
|
||||
scrollToBottom("smooth");
|
||||
return;
|
||||
}
|
||||
|
||||
// No auto-scroll: recompute from actual layout so that chat switches to
|
||||
// content that doesn't overflow correctly hide the button even when no
|
||||
// scroll event fires.
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
const distanceFromBottom =
|
||||
container.scrollHeight - container.scrollTop - container.clientHeight;
|
||||
const atBottom = distanceFromBottom <= BOTTOM_THRESHOLD_PX;
|
||||
isAtBottomRef.current = atBottom;
|
||||
setShowScrollButton(!atBottom);
|
||||
}, [messages, scrollToBottom]);
|
||||
|
||||
return (
|
||||
<div className={classes.messageListWrapper}>
|
||||
<div ref={containerRef} className={classes.messageList}>
|
||||
{messages.map((msg) => (
|
||||
<ErrorBoundary
|
||||
key={msg.id}
|
||||
fallback={<ChatMessageErrorFallback />}
|
||||
>
|
||||
<ChatMessage message={msg} />
|
||||
</ErrorBoundary>
|
||||
))}
|
||||
{isStreaming && (
|
||||
<ErrorBoundary
|
||||
resetKeys={[streamingContent, streamingToolCalls.length]}
|
||||
fallback={<ChatMessageErrorFallback />}
|
||||
>
|
||||
<ChatMessage
|
||||
message={{
|
||||
id: "streaming",
|
||||
chatId: "",
|
||||
role: "assistant",
|
||||
content: null,
|
||||
toolCalls: null,
|
||||
metadata: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
}}
|
||||
isStreaming
|
||||
streamingContent={streamingContent}
|
||||
streamingToolCalls={streamingToolCalls}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
)}
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
{showScrollButton && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Scroll to bottom"
|
||||
className={classes.scrollToBottomButton}
|
||||
onClick={() => scrollToBottom("smooth")}
|
||||
>
|
||||
<IconArrowDown size={16} stroke={2} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { useCallback } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import DOMPurify from "dompurify";
|
||||
import { ActionIcon, Tooltip } from "@mantine/core";
|
||||
import {
|
||||
IconCheck,
|
||||
IconCopy,
|
||||
IconFile,
|
||||
IconLoader2,
|
||||
IconPhoto,
|
||||
} from "@tabler/icons-react";
|
||||
import { markdownToHtml } from "@docmost/editor-ext";
|
||||
import { CopyButton } from "@/components/common/copy-button";
|
||||
import type { AiChatMessage, AiChatToolCall } from "../types/ai-chat.types";
|
||||
import ChatToolGroup from "./chat-tool-group";
|
||||
import classes from "../styles/chat-message.module.css";
|
||||
import CopyTextButton from "@/components/common/copy.tsx";
|
||||
|
||||
const chatSanitizer = DOMPurify();
|
||||
chatSanitizer.addHook("afterSanitizeAttributes", (node) => {
|
||||
if (node.tagName === "A") {
|
||||
const href = node.getAttribute("href") || "";
|
||||
if (href.startsWith("http://") || href.startsWith("https://")) {
|
||||
node.setAttribute("target", "_blank");
|
||||
node.setAttribute("rel", "noopener noreferrer");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"];
|
||||
|
||||
type Props = {
|
||||
message: AiChatMessage;
|
||||
isStreaming?: boolean;
|
||||
streamingContent?: string;
|
||||
streamingToolCalls?: AiChatToolCall[];
|
||||
};
|
||||
|
||||
export default function ChatMessage({
|
||||
message,
|
||||
isStreaming,
|
||||
streamingContent,
|
||||
streamingToolCalls,
|
||||
}: Props) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleContentClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const anchor = target.closest("a");
|
||||
if (!anchor) return;
|
||||
|
||||
const href = anchor.getAttribute("href");
|
||||
if (href && (href.startsWith("/s/") || href.startsWith("/p/"))) {
|
||||
e.preventDefault();
|
||||
navigate(href);
|
||||
}
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
if (message.role === "tool") return null;
|
||||
|
||||
const isUser = message.role === "user";
|
||||
const content = isStreaming ? streamingContent : message.content;
|
||||
const toolCalls = isStreaming ? streamingToolCalls : message.toolCalls;
|
||||
|
||||
if (isUser) {
|
||||
const displayContent = (content || "").replace(
|
||||
/\n\n<referenced_pages>[\s\S]*<\/referenced_pages>$/,
|
||||
"",
|
||||
);
|
||||
const attachments =
|
||||
(message.metadata?.attachments as {
|
||||
id: string;
|
||||
fileName: string;
|
||||
fileExt: string;
|
||||
}[]) || [];
|
||||
|
||||
return (
|
||||
<div className={classes.userMessage}>
|
||||
<div className={classes.userBubble}>
|
||||
{attachments.length > 0 && (
|
||||
<div className={classes.messageAttachments}>
|
||||
{attachments.map((a) => (
|
||||
<span key={a.id} className={classes.messageAttachmentChip}>
|
||||
{IMAGE_EXTENSIONS.includes(a.fileExt) ? (
|
||||
<IconPhoto size={13} />
|
||||
) : (
|
||||
<IconFile size={13} />
|
||||
)}
|
||||
{a.fileName}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{displayContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.assistantMessage}>
|
||||
<div className={classes.messageContent}>
|
||||
{toolCalls && toolCalls.length > 0 && (
|
||||
<ChatToolGroup toolCalls={toolCalls} isStreaming={isStreaming} />
|
||||
)}
|
||||
{content && (
|
||||
<div
|
||||
onClick={handleContentClick}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: chatSanitizer.sanitize(
|
||||
markdownToHtml(content) as string,
|
||||
{ ADD_ATTR: ["target", "rel"] },
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isStreaming && (
|
||||
<>
|
||||
{!content && (
|
||||
<span className={classes.processingIndicator}>
|
||||
<IconLoader2 size={16} className={classes.processingSpinner} />
|
||||
Thinking
|
||||
</span>
|
||||
)}
|
||||
<span className={classes.streamingCursor} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!isStreaming && message.content && (
|
||||
<div className={classes.messageActions}>
|
||||
<CopyTextButton text={message?.content} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
IconChevronRight,
|
||||
IconChevronDown,
|
||||
IconLoader2,
|
||||
} from "@tabler/icons-react";
|
||||
import type { AiChatToolCall } from "../types/ai-chat.types";
|
||||
import ChatToolResult, { TOOL_LABELS } from "./chat-tool-result";
|
||||
import classes from "../styles/chat-message.module.css";
|
||||
|
||||
type Props = {
|
||||
toolCalls: AiChatToolCall[];
|
||||
isStreaming?: boolean;
|
||||
};
|
||||
|
||||
export default function ChatToolGroup({ toolCalls, isStreaming }: Props) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
if (!toolCalls || toolCalls.length === 0) return null;
|
||||
|
||||
const activeCall =
|
||||
isStreaming && toolCalls.length > 0
|
||||
? [...toolCalls].reverse().find((tc) => tc.result === undefined)
|
||||
: undefined;
|
||||
|
||||
const activeLabel = activeCall
|
||||
? TOOL_LABELS[activeCall.name] || activeCall.name
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={classes.toolGroup}>
|
||||
<div
|
||||
className={classes.toolGroupHeader}
|
||||
onClick={() => setExpanded((prev) => !prev)}
|
||||
>
|
||||
{activeLabel ? (
|
||||
<IconLoader2 size={12} className={classes.processingSpinner} />
|
||||
) : expanded ? (
|
||||
<IconChevronDown size={12} />
|
||||
) : (
|
||||
<IconChevronRight size={12} />
|
||||
)}
|
||||
<span className={classes.toolGroupLabel}>
|
||||
{activeLabel ? `${activeLabel}…` : `Steps ${toolCalls.length}`}
|
||||
</span>
|
||||
</div>
|
||||
{expanded && (
|
||||
<div className={classes.toolGroupSteps}>
|
||||
{toolCalls.map((tc) => (
|
||||
<ChatToolResult key={tc.id} toolCall={tc} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useState } from "react";
|
||||
import { IconChevronRight, IconChevronDown } from "@tabler/icons-react";
|
||||
import type { AiChatToolCall } from "../types/ai-chat.types";
|
||||
import classes from "../styles/chat-message.module.css";
|
||||
|
||||
export const TOOL_LABELS: Record<string, string> = {
|
||||
list_spaces: "Listed spaces",
|
||||
search_pages: "Searched pages",
|
||||
get_page: "Read page",
|
||||
create_page: "Created page",
|
||||
update_page: "Updated page",
|
||||
};
|
||||
|
||||
type Props = {
|
||||
toolCall: AiChatToolCall;
|
||||
};
|
||||
|
||||
export default function ChatToolResult({ toolCall }: Props) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const label = TOOL_LABELS[toolCall.name] || toolCall.name;
|
||||
|
||||
return (
|
||||
<div className={classes.toolStep}>
|
||||
<div
|
||||
className={classes.toolStepRow}
|
||||
onClick={() => setExpanded((prev) => !prev)}
|
||||
>
|
||||
<span className={classes.toolStepBullet}>·</span>
|
||||
{expanded ? (
|
||||
<IconChevronDown size={12} />
|
||||
) : (
|
||||
<IconChevronRight size={12} />
|
||||
)}
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
{expanded && (
|
||||
<div className={classes.toolStepDetails}>
|
||||
<pre style={{ margin: 0, whiteSpace: "pre-wrap" }}>
|
||||
{JSON.stringify(
|
||||
{ args: toolCall.args, result: toolCall.result },
|
||||
null,
|
||||
2,
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Badge, Group, Text, Switch, Tooltip } from "@mantine/core";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature";
|
||||
import { Feature } from "@/ee/features";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
|
||||
|
||||
export default function EnableAiChat() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
||||
<div>
|
||||
<Group gap="xs" align="center">
|
||||
<Text size="md">{t("AI Chat")}</Text>
|
||||
<Badge color="gray" variant="light" size="sm" radius="sm">
|
||||
{t("Beta")}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t(
|
||||
"Enable AI Chat to allow users to have multi-turn conversations with AI about your workspace content.",
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<AiChatToggle />
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
function AiChatToggle() {
|
||||
const { t } = useTranslation();
|
||||
const [workspace, setWorkspace] = useAtom(workspaceAtom);
|
||||
const [checked, setChecked] = useState(workspace?.settings?.ai?.chat);
|
||||
const hasAccess = useHasFeature(Feature.AI);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.checked;
|
||||
try {
|
||||
const updatedWorkspace = await updateWorkspace({ aiChat: value } as any);
|
||||
setChecked(value);
|
||||
setWorkspace(updatedWorkspace);
|
||||
} catch (err: any) {
|
||||
notifications.show({
|
||||
message: err?.response?.data?.message,
|
||||
color: "red",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip label={upgradeLabel} disabled={hasAccess} refProp="rootRef">
|
||||
<Switch
|
||||
defaultChecked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={!hasAccess}
|
||||
aria-label={t("Toggle AI Chat")}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sendChatMessage } from "../services/ai-chat-service";
|
||||
import type {
|
||||
AiChatMessage,
|
||||
AiChatStreamEvent,
|
||||
AiChatToolCall,
|
||||
ChatAttachment,
|
||||
PageMention,
|
||||
} from "../types/ai-chat.types";
|
||||
|
||||
type ChatStreamOptions = {
|
||||
onChatCreated?: (chatId: string) => void;
|
||||
};
|
||||
|
||||
export function useChatStream(
|
||||
chatId: string | undefined,
|
||||
options?: ChatStreamOptions,
|
||||
) {
|
||||
const [messages, setMessages] = useState<AiChatMessage[]>([]);
|
||||
const [streamingContent, setStreamingContent] = useState("");
|
||||
const [streamingToolCalls, setStreamingToolCalls] = useState<AiChatToolCall[]>(
|
||||
[],
|
||||
);
|
||||
const [isStreaming, setIsStreaming] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [errorCode, setErrorCode] = useState<string | null>(null);
|
||||
const [isRetryable, setIsRetryable] = useState(false);
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const currentChatIdRef = useRef(chatId);
|
||||
currentChatIdRef.current = chatId;
|
||||
// Tracks which chatId the local `messages` state currently represents.
|
||||
// Set when we seed from a server fetch AND when we optimistically own a
|
||||
// freshly-created chat after `chat_created`. This is the single authority
|
||||
// marker that keeps server-state effects from clobbering in-flight streams.
|
||||
const hydratedChatIdRef = useRef<string | undefined>(undefined);
|
||||
|
||||
// Reset local state when the consumer switches to a different chat.
|
||||
// Skip the reset if the new chatId is one the hook itself already claimed
|
||||
// during a new-chat flow — in that case our optimistic state is the truth.
|
||||
useEffect(() => {
|
||||
if (chatId && chatId === hydratedChatIdRef.current) return;
|
||||
hydratedChatIdRef.current = undefined;
|
||||
setMessages([]);
|
||||
setError(null);
|
||||
setErrorCode(null);
|
||||
setIsRetryable(false);
|
||||
}, [chatId]);
|
||||
|
||||
const hydrateFromServer = useCallback((msgs: AiChatMessage[]) => {
|
||||
const forId = currentChatIdRef.current;
|
||||
if (!forId) return;
|
||||
if (hydratedChatIdRef.current === forId) return;
|
||||
hydratedChatIdRef.current = forId;
|
||||
setMessages(msgs);
|
||||
}, []);
|
||||
|
||||
const sendMessage = useCallback(
|
||||
(content: string, mentions: PageMention[] = [], attachments: ChatAttachment[] = [], contextPageId?: string) => {
|
||||
if (isStreaming || (!content.trim() && attachments.length === 0)) return;
|
||||
|
||||
setError(null);
|
||||
setErrorCode(null);
|
||||
setIsRetryable(false);
|
||||
setIsStreaming(true);
|
||||
setStreamingContent("");
|
||||
setStreamingToolCalls([]);
|
||||
|
||||
const metadata: Record<string, unknown> = {};
|
||||
if (mentions.length) {
|
||||
metadata.mentionedPageIds = mentions.map((m) => m.id);
|
||||
}
|
||||
if (attachments.length) {
|
||||
metadata.attachments = attachments.map((a) => ({
|
||||
id: a.id,
|
||||
fileName: a.fileName,
|
||||
fileExt: a.fileExt,
|
||||
}));
|
||||
}
|
||||
|
||||
const userMessage: AiChatMessage = {
|
||||
id: `temp-${Date.now()}`,
|
||||
chatId: currentChatIdRef.current || "",
|
||||
role: "user",
|
||||
content,
|
||||
toolCalls: null,
|
||||
metadata: Object.keys(metadata).length ? metadata : null,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
setMessages((prev) => [...prev, userMessage]);
|
||||
|
||||
const attachmentIds = attachments.map((a) => a.id);
|
||||
|
||||
const abortController = sendChatMessage(
|
||||
{
|
||||
chatId: currentChatIdRef.current,
|
||||
content,
|
||||
mentionedPageIds: mentions.map((m) => m.id),
|
||||
...(contextPageId && { contextPageId }),
|
||||
...(attachmentIds.length && { attachmentIds }),
|
||||
},
|
||||
(event: AiChatStreamEvent) => {
|
||||
switch (event.type) {
|
||||
case "chat_created":
|
||||
currentChatIdRef.current = event.chatId;
|
||||
// Claim authority over this new chatId so when the consumer's
|
||||
// prop catches up via navigation/onChatCreated, the reset effect
|
||||
// sees a match and preserves our optimistic messages.
|
||||
hydratedChatIdRef.current = event.chatId;
|
||||
if (options?.onChatCreated) {
|
||||
options.onChatCreated(event.chatId);
|
||||
} else {
|
||||
navigate(`/ai/chat/${event.chatId}`, { replace: true });
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ["ai-chats"] });
|
||||
break;
|
||||
case "content":
|
||||
setStreamingContent((prev) => prev + event.text);
|
||||
break;
|
||||
case "tool_call":
|
||||
setStreamingToolCalls((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: event.id,
|
||||
name: event.name,
|
||||
args: event.args,
|
||||
},
|
||||
]);
|
||||
break;
|
||||
case "tool_result":
|
||||
setStreamingToolCalls((prev) =>
|
||||
prev.map((tc) =>
|
||||
tc.id === event.id ? { ...tc, result: event.result } : tc,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case "done": {
|
||||
setStreamingContent((currentContent) => {
|
||||
setStreamingToolCalls((currentToolCalls) => {
|
||||
const assistantMessage: AiChatMessage = {
|
||||
id: event.messageId,
|
||||
chatId: currentChatIdRef.current || "",
|
||||
role: "assistant",
|
||||
content: currentContent || null,
|
||||
toolCalls: currentToolCalls.length
|
||||
? currentToolCalls
|
||||
: null,
|
||||
metadata: event.usage ? { tokenUsage: event.usage } : null,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
setMessages((prev) => [...prev, assistantMessage]);
|
||||
return [];
|
||||
});
|
||||
return "";
|
||||
});
|
||||
setIsStreaming(false);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["ai-chat", currentChatIdRef.current],
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "error":
|
||||
setError(event.message);
|
||||
setErrorCode(event.code || null);
|
||||
setIsRetryable(event.retryable || false);
|
||||
setIsStreaming(false);
|
||||
break;
|
||||
}
|
||||
},
|
||||
(errorMsg) => {
|
||||
setError(errorMsg);
|
||||
setIsStreaming(false);
|
||||
},
|
||||
() => {
|
||||
setIsStreaming(false);
|
||||
},
|
||||
);
|
||||
|
||||
abortRef.current = abortController;
|
||||
},
|
||||
[isStreaming, navigate, queryClient],
|
||||
);
|
||||
|
||||
const stopGeneration = useCallback(() => {
|
||||
abortRef.current?.abort();
|
||||
abortRef.current = null;
|
||||
|
||||
setStreamingContent((currentContent) => {
|
||||
setStreamingToolCalls((currentToolCalls) => {
|
||||
if (currentContent || currentToolCalls.length > 0) {
|
||||
const partialMessage: AiChatMessage = {
|
||||
id: `stopped-${Date.now()}`,
|
||||
chatId: currentChatIdRef.current || "",
|
||||
role: "assistant",
|
||||
content: currentContent || null,
|
||||
toolCalls: currentToolCalls.length ? currentToolCalls : null,
|
||||
metadata: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
setMessages((prev) => [...prev, partialMessage]);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return "";
|
||||
});
|
||||
|
||||
setIsStreaming(false);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
messages,
|
||||
streamingContent,
|
||||
streamingToolCalls,
|
||||
isStreaming,
|
||||
error,
|
||||
errorCode,
|
||||
isRetryable,
|
||||
sendMessage,
|
||||
stopGeneration,
|
||||
hydrateFromServer,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { Button } from "@mantine/core";
|
||||
import { IconAlertTriangle } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AiChatLayout from "../components/ai-chat-layout";
|
||||
import { EmptyState } from "@/components/ui/empty-state.tsx";
|
||||
import classes from "../styles/ai-chat.module.css";
|
||||
|
||||
export default function AiChat() {
|
||||
const { t } = useTranslation();
|
||||
const { chatId } = useParams<{ chatId: string }>();
|
||||
|
||||
return (
|
||||
<div className={classes.layout}>
|
||||
<ErrorBoundary
|
||||
resetKeys={[chatId]}
|
||||
fallbackRender={({ resetErrorBoundary }) => (
|
||||
<EmptyState
|
||||
icon={IconAlertTriangle}
|
||||
title={t("Failed to load chat. An error occurred.")}
|
||||
action={
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
mt="xs"
|
||||
onClick={resetErrorBoundary}
|
||||
>
|
||||
{t("Try again")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<AiChatLayout />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
useQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
useInfiniteQuery,
|
||||
} from "@tanstack/react-query";
|
||||
import {
|
||||
listChats,
|
||||
getChatInfo,
|
||||
deleteChat,
|
||||
updateChatTitle,
|
||||
searchChats,
|
||||
} from "../services/ai-chat-service";
|
||||
|
||||
export function useChatsQuery() {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ["ai-chats"],
|
||||
queryFn: ({ pageParam }) =>
|
||||
listChats({ cursor: pageParam, limit: 30 }),
|
||||
initialPageParam: undefined as string | undefined,
|
||||
getNextPageParam: (lastPage) =>
|
||||
lastPage.meta.hasNextPage ? lastPage.meta.nextCursor : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export function useChatInfoQuery(chatId: string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: ["ai-chat", chatId],
|
||||
queryFn: () => getChatInfo(chatId!),
|
||||
enabled: !!chatId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteChatMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (chatId: string) => deleteChat(chatId),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["ai-chats"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateChatTitleMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ chatId, title }: { chatId: string; title: string }) =>
|
||||
updateChatTitle(chatId, title),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["ai-chats"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSearchChatsQuery(query: string) {
|
||||
return useQuery({
|
||||
queryKey: ["ai-chats-search", query],
|
||||
queryFn: () => searchChats(query),
|
||||
enabled: query.length > 0,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import api from "@/lib/api-client.ts";
|
||||
import type {
|
||||
AiChat,
|
||||
AiChatMessage,
|
||||
AiChatStreamEvent,
|
||||
ChatAttachment,
|
||||
} from "../types/ai-chat.types";
|
||||
import { IPagination } from "@/lib/types.ts";
|
||||
|
||||
export async function createChat(): Promise<AiChat> {
|
||||
const req = await api.post<AiChat>("/ai/chats/create");
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function listChats(params?: {
|
||||
limit?: number;
|
||||
cursor?: string;
|
||||
}): Promise<IPagination<AiChat>> {
|
||||
const req = await api.post("/ai/chats", params);
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function getChatInfo(
|
||||
chatId: string,
|
||||
): Promise<{ chat: AiChat; messages: AiChatMessage[] }> {
|
||||
const req = await api.post("/ai/chats/info", { chatId });
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function deleteChat(chatId: string): Promise<void> {
|
||||
await api.post("/ai/chats/delete", { chatId });
|
||||
}
|
||||
|
||||
export async function updateChatTitle(
|
||||
chatId: string,
|
||||
title: string,
|
||||
): Promise<void> {
|
||||
await api.post("/ai/chats/update", { chatId, title });
|
||||
}
|
||||
|
||||
export async function searchChats(query: string): Promise<AiChat[]> {
|
||||
const req = await api.post("/ai/chats/search", { query });
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function uploadChatFile(
|
||||
file: File,
|
||||
chatId?: string,
|
||||
): Promise<ChatAttachment> {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
if (chatId) {
|
||||
formData.append("chatId", chatId);
|
||||
}
|
||||
return await api.post("/ai/chats/upload", formData, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
}
|
||||
|
||||
export function sendChatMessage(
|
||||
params: {
|
||||
chatId?: string;
|
||||
content: string;
|
||||
mentionedPageIds?: string[];
|
||||
contextPageId?: string;
|
||||
attachmentIds?: string[];
|
||||
},
|
||||
onEvent: (event: AiChatStreamEvent) => void,
|
||||
onError?: (error: string) => void,
|
||||
onComplete?: () => void,
|
||||
): AbortController {
|
||||
const abortController = new AbortController();
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const response = await fetch("/api/ai/chats/send", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorBody = await response.text();
|
||||
let errorMessage = `HTTP error ${response.status}`;
|
||||
try {
|
||||
const parsed = JSON.parse(errorBody);
|
||||
errorMessage = parsed.message || errorMessage;
|
||||
} catch {
|
||||
// use default
|
||||
}
|
||||
onError?.(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
if (!reader) {
|
||||
onError?.("Response body is not readable");
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer = "";
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || "";
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const data = line.slice(6);
|
||||
if (data === "[DONE]") {
|
||||
onComplete?.();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(data) as AiChatStreamEvent;
|
||||
onEvent(parsed);
|
||||
} catch {
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
|
||||
onComplete?.();
|
||||
} catch (error: any) {
|
||||
if (error.name !== "AbortError") {
|
||||
onError?.(error.message);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return abortController;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
.layout {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 45px - 2 * var(--mantine-spacing-md));
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.messageListWrapper {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.messageList {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: var(--mantine-spacing-md) var(--mantine-spacing-lg);
|
||||
}
|
||||
|
||||
.messageErrorFallback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: var(--mantine-spacing-lg);
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
}
|
||||
|
||||
.scrollToBottomButton {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.scrollToBottomButton:hover {
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5));
|
||||
border-color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-3));
|
||||
}
|
||||
|
||||
.scrollToBottomButton:active {
|
||||
transform: translateX(-50%) scale(0.95);
|
||||
}
|
||||
|
||||
.inputArea {
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-lg) var(--mantine-spacing-lg);
|
||||
}
|
||||
|
||||
/* Empty state - Notion AI style centered layout */
|
||||
.emptyState {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--mantine-spacing-xl) var(--mantine-spacing-lg);
|
||||
}
|
||||
|
||||
.emptyStateIcon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: var(--mantine-spacing-sm);
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
}
|
||||
|
||||
.emptyStateBrand {
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
margin-bottom: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.emptyStateTitle {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0));
|
||||
margin-bottom: var(--mantine-spacing-xl);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.emptyStateInput {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin-bottom: var(--mantine-spacing-xl);
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.suggestionsSection {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.suggestionsLabel {
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
font-weight: 500;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: var(--mantine-spacing-sm);
|
||||
}
|
||||
|
||||
.suggestionsGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--mantine-spacing-sm);
|
||||
}
|
||||
|
||||
.suggestionCard {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--mantine-spacing-sm);
|
||||
padding: var(--mantine-spacing-sm) var(--mantine-spacing-md);
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
border-radius: var(--mantine-radius-md);
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
transition: background-color 150ms, border-color 150ms;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
|
||||
@mixin hover {
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
}
|
||||
|
||||
.suggestionIcon {
|
||||
flex-shrink: 0;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.suggestionText {
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
line-height: 1.4;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0 0 var(--mantine-spacing-sm) 0;
|
||||
border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
}
|
||||
|
||||
.toolbarSpacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.titleButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
font-weight: 500;
|
||||
color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0));
|
||||
max-width: 60%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.titleButton:hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6));
|
||||
}
|
||||
|
||||
.titleText {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: var(--mantine-spacing-sm) 0;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.inputArea {
|
||||
padding-top: var(--mantine-spacing-sm);
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--mantine-spacing-md);
|
||||
padding: var(--mantine-spacing-xl) var(--mantine-spacing-sm);
|
||||
}
|
||||
|
||||
.emptyStateIcon {
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
}
|
||||
|
||||
.emptyStateTitle {
|
||||
font-size: var(--mantine-font-size-lg);
|
||||
font-weight: 600;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quickActions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.quickAction {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--mantine-spacing-sm);
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
border-radius: var(--mantine-radius-md);
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
transition: background-color 150ms, border-color 150ms;
|
||||
|
||||
@mixin hover {
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
}
|
||||
|
||||
.quickActionIcon {
|
||||
flex-shrink: 0;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
}
|
||||
|
||||
.historyList {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.historyItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
|
||||
cursor: pointer;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
transition: background-color 150ms;
|
||||
|
||||
@mixin hover {
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||
}
|
||||
}
|
||||
|
||||
.historyItemTitle {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
.inputWrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid
|
||||
light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
border-radius: 16px;
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
|
||||
box-shadow: light-dark(
|
||||
0 2px 40px 4px rgba(0, 0, 0, 0.07),
|
||||
0 2px 40px 4px rgba(0, 0, 0, 0.5)
|
||||
);
|
||||
transition:
|
||||
border-color 150ms,
|
||||
box-shadow 150ms;
|
||||
|
||||
&:focus-within {
|
||||
border-color: light-dark(
|
||||
var(--mantine-color-gray-3),
|
||||
var(--mantine-color-dark-4)
|
||||
);
|
||||
box-shadow: light-dark(
|
||||
0 4px 48px 6px rgba(0, 0, 0, 0.09),
|
||||
0 4px 48px 6px rgba(0, 0, 0, 0.6)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.inputWrapperFlat {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
border-radius: 12px;
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
|
||||
box-shadow: none;
|
||||
transition: border-color 150ms;
|
||||
|
||||
&:focus-within {
|
||||
border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
margin-top: 6px;
|
||||
text-align: center;
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
}
|
||||
|
||||
.attachmentChips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
padding: 10px 14px 0;
|
||||
}
|
||||
|
||||
.attachmentChip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.attachmentChipUploading {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.attachmentChipName {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.attachmentChipRemove {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin-left: 2px;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
border-radius: 50%;
|
||||
|
||||
@mixin hover {
|
||||
color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0));
|
||||
}
|
||||
}
|
||||
|
||||
.editorContent {
|
||||
overflow: hidden;
|
||||
|
||||
:global(.ProseMirror) {
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 14px 18px 8px;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
min-height: 24px;
|
||||
color: light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0));
|
||||
}
|
||||
|
||||
:global(.ProseMirror p) {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
:global(.ProseMirror p.is-editor-empty:first-child::before) {
|
||||
color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-3));
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 4px 12px 10px;
|
||||
gap: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.sendButton {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 150ms, opacity 150ms;
|
||||
background: light-dark(var(--mantine-color-dark-9), var(--mantine-color-gray-0));
|
||||
color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-9));
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.25;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@mixin hover {
|
||||
&:not(:disabled) {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attachButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
transition: color 150ms;
|
||||
|
||||
@mixin hover {
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
}
|
||||
}
|
||||
|
||||
.plusButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
transition: color 150ms, background-color 150ms;
|
||||
|
||||
@mixin hover {
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
}
|
||||
}
|
||||
|
||||
.plusMenuItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--mantine-spacing-sm);
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
transition: background-color 150ms;
|
||||
|
||||
@mixin hover {
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.plusMenuIcon {
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
}
|
||||
|
||||
.stopButton {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
cursor: pointer;
|
||||
transition: background-color 150ms;
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
|
||||
@mixin hover {
|
||||
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
.message {
|
||||
margin-bottom: var(--mantine-spacing-lg);
|
||||
}
|
||||
|
||||
.userMessage {
|
||||
composes: message;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.userBubble {
|
||||
max-width: 75%;
|
||||
padding: 10px 16px;
|
||||
border-radius: 18px;
|
||||
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0));
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-aside-chat] .userBubble {
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
}
|
||||
|
||||
.userBubble p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.messageAttachments {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.messageAttachmentChip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
background: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-2));
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
max-width: 180px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.assistantMessage {
|
||||
composes: message;
|
||||
}
|
||||
|
||||
.messageContent {
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-1));
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.messageContent p {
|
||||
margin: 0 0 0.75em 0;
|
||||
}
|
||||
|
||||
.messageContent p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.messageContent ul,
|
||||
.messageContent ol {
|
||||
margin: 0.5em 0 0.75em 0;
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
.messageContent li {
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.messageContent h1,
|
||||
.messageContent h2,
|
||||
.messageContent h3 {
|
||||
margin: 1em 0 0.5em 0;
|
||||
font-weight: 600;
|
||||
color: light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0));
|
||||
}
|
||||
|
||||
.messageContent h1 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.messageContent h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.messageContent h3 {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.messageContent pre {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
|
||||
padding: var(--mantine-spacing-sm) var(--mantine-spacing-md);
|
||||
border-radius: var(--mantine-radius-md);
|
||||
overflow-x: auto;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
|
||||
.messageContent code {
|
||||
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6));
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.88em;
|
||||
}
|
||||
|
||||
.messageContent pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.messageContent blockquote {
|
||||
border-left: 3px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
padding-left: var(--mantine-spacing-md);
|
||||
margin: 0.75em 0;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-2));
|
||||
}
|
||||
|
||||
.messageContent a {
|
||||
color: light-dark(var(--mantine-color-blue-7), var(--mantine-color-blue-4));
|
||||
text-decoration: none;
|
||||
|
||||
@mixin hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.messageContent a[href^="/s/"],
|
||||
.messageContent a[href^="/p/"] {
|
||||
color: light-dark(var(--mantine-color-dark-4), var(--mantine-color-dark-1));
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
@mixin light {
|
||||
border-bottom: 0.05em solid var(--mantine-color-dark-0);
|
||||
}
|
||||
|
||||
@mixin dark {
|
||||
border-bottom: 0.05em solid var(--mantine-color-dark-2);
|
||||
}
|
||||
|
||||
@mixin hover {
|
||||
text-decoration: none;
|
||||
@mixin light {
|
||||
border-bottom-color: var(--mantine-color-dark-2);
|
||||
}
|
||||
@mixin dark {
|
||||
border-bottom-color: var(--mantine-color-dark-0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.messageContent hr {
|
||||
border: none;
|
||||
border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.toolGroup {
|
||||
margin: 6px 0;
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
}
|
||||
|
||||
.toolGroupHeader {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
line-height: 1.4;
|
||||
transition: color 120ms ease;
|
||||
}
|
||||
|
||||
.toolGroupHeader:hover {
|
||||
color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0));
|
||||
}
|
||||
|
||||
.toolGroupLabel {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.toolGroupSteps {
|
||||
margin-top: 4px;
|
||||
padding-left: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.toolStep {
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
}
|
||||
|
||||
.toolStepRow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
line-height: 1.5;
|
||||
transition: color 120ms ease;
|
||||
}
|
||||
|
||||
.toolStepRow:hover {
|
||||
color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0));
|
||||
}
|
||||
|
||||
.toolStepBullet {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
text-align: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.toolStepDetails {
|
||||
margin-top: 4px;
|
||||
margin-left: 18px;
|
||||
padding: 6px 10px;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.messageActions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-top: 4px;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
}
|
||||
|
||||
.processingIndicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
}
|
||||
|
||||
.processingSpinner {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.streamingCursor {
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
height: 1em;
|
||||
background: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
animation: blink 1s step-end infinite;
|
||||
vertical-align: text-bottom;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: var(--mantine-spacing-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
margin-bottom: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.chatList {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.chatGroup + .chatGroup {
|
||||
margin-top: var(--mantine-spacing-sm);
|
||||
}
|
||||
|
||||
.chatGroupLabel {
|
||||
padding: 4px var(--mantine-spacing-xs);
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
font-weight: 600;
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.chatListEmpty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--mantine-spacing-xl) var(--mantine-spacing-md);
|
||||
text-align: center;
|
||||
gap: 4px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.chatListEmptyIcon {
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
margin-bottom: var(--mantine-spacing-xs);
|
||||
}
|
||||
|
||||
.chatListEmptyTitle {
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
font-weight: 600;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
}
|
||||
|
||||
.chatListEmptyHint {
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-3));
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.chatItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px var(--mantine-spacing-xs);
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
user-select: none;
|
||||
gap: var(--mantine-spacing-xs);
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-1),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-2),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.chatItemTitle {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chatItemDate {
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||
white-space: nowrap;
|
||||
transition: opacity 150ms;
|
||||
}
|
||||
|
||||
.chatItemRenameInput {
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
padding: 0;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.chatItem:hover .chatItemDate {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.chatItemActions {
|
||||
position: absolute;
|
||||
right: var(--mantine-spacing-xs);
|
||||
opacity: 0;
|
||||
transition: opacity 150ms;
|
||||
}
|
||||
|
||||
.chatItem {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chatItem:hover .chatItemActions {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
export type AiChat = {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
creatorId: string;
|
||||
title: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type AiChatToolCall = {
|
||||
id: string;
|
||||
name: string;
|
||||
args: Record<string, unknown>;
|
||||
result?: unknown;
|
||||
};
|
||||
|
||||
export type AiChatMessage = {
|
||||
id: string;
|
||||
chatId: string;
|
||||
role: 'user' | 'assistant' | 'tool';
|
||||
content: string | null;
|
||||
toolCalls: AiChatToolCall[] | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type AiChatStreamEvent =
|
||||
| { type: 'chat_created'; chatId: string }
|
||||
| { type: 'content'; text: string }
|
||||
| { type: 'tool_call'; id: string; name: string; args: Record<string, unknown> }
|
||||
| { type: 'tool_result'; id: string; result: unknown }
|
||||
| { type: 'done'; messageId: string; usage?: Record<string, number> }
|
||||
| { type: 'error'; message: string; code?: string; retryable?: boolean };
|
||||
|
||||
export type PageMention = {
|
||||
id: string;
|
||||
title: string;
|
||||
slugId: string;
|
||||
spaceSlug?: string;
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
export type ChatAttachment = {
|
||||
id: string;
|
||||
fileName: string;
|
||||
fileExt: string;
|
||||
fileSize: number;
|
||||
mimeType: string;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { AiChat } from "../types/ai-chat.types";
|
||||
|
||||
export type ChatGroup = { key: string; label: string; chats: AiChat[] };
|
||||
|
||||
export function groupChatsByAge(
|
||||
chats: AiChat[],
|
||||
t: (key: string) => string,
|
||||
): ChatGroup[] {
|
||||
if (chats.length === 0) return [];
|
||||
|
||||
const now = new Date();
|
||||
const startOfToday = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
).getTime();
|
||||
const startOfYesterday = startOfToday - 24 * 60 * 60 * 1000;
|
||||
const startOfLast7 = startOfToday - 7 * 24 * 60 * 60 * 1000;
|
||||
const startOfLast30 = startOfToday - 30 * 24 * 60 * 60 * 1000;
|
||||
|
||||
const buckets: Record<string, ChatGroup> = {
|
||||
today: { key: "today", label: t("Today"), chats: [] },
|
||||
yesterday: { key: "yesterday", label: t("Yesterday"), chats: [] },
|
||||
last7: { key: "last7", label: t("Previous 7 days"), chats: [] },
|
||||
last30: { key: "last30", label: t("Previous 30 days"), chats: [] },
|
||||
older: { key: "older", label: t("Older"), chats: [] },
|
||||
};
|
||||
|
||||
for (const chat of chats) {
|
||||
const ts = new Date(chat.updatedAt).getTime();
|
||||
if (ts >= startOfToday) buckets.today.chats.push(chat);
|
||||
else if (ts >= startOfYesterday) buckets.yesterday.chats.push(chat);
|
||||
else if (ts >= startOfLast7) buckets.last7.chats.push(chat);
|
||||
else if (ts >= startOfLast30) buckets.last30.chats.push(chat);
|
||||
else buckets.older.chats.push(chat);
|
||||
}
|
||||
|
||||
return [
|
||||
buckets.today,
|
||||
buckets.yesterday,
|
||||
buckets.last7,
|
||||
buckets.last30,
|
||||
buckets.older,
|
||||
].filter((b) => b.chats.length > 0);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
.aiMenu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
min-height: 2.25rem;
|
||||
}
|
||||
|
||||
.aiInput {
|
||||
width: 100%;
|
||||
|
||||
& input {
|
||||
height: 44px;
|
||||
border-radius: 22px;
|
||||
padding-left: 20px;
|
||||
padding-right: 40px;
|
||||
border: 1px solid
|
||||
light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
|
||||
&:focus {
|
||||
border-color: light-dark(
|
||||
var(--mantine-color-gray-4),
|
||||
var(--mantine-color-dark-3)
|
||||
);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
.menuItemSelected {
|
||||
background-color: var(--mantine-color-gray-1);
|
||||
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-5);
|
||||
}
|
||||
}
|
||||
|
||||
.resultPreview {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-white),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
border: 1px solid
|
||||
light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.resultPreviewWrapper {
|
||||
font-size: var(--mantine-font-size-md);
|
||||
line-height: 1.6;
|
||||
padding: var(--mantine-spacing-md);
|
||||
|
||||
*:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
*:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
import { Editor } from "@tiptap/react";
|
||||
import { ActionIcon, TextInput } from "@mantine/core";
|
||||
import { useDebouncedCallback, useMediaQuery } from "@mantine/hooks";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useAtom } from "jotai";
|
||||
import { IconArrowUp } from "@tabler/icons-react";
|
||||
import { showAiMenuAtom } from "@/features/editor/atoms/editor-atoms.ts";
|
||||
import { useAiGenerateStreamMutation } from "@/ee/ai/queries/ai-query.ts";
|
||||
import { AiAction } from "@/ee/ai/types/ai.types.ts";
|
||||
import { CommandItem, commandItems, CommandSet } from "./command-items.ts";
|
||||
import { CommandSelector } from "./command-selector.tsx";
|
||||
import { ResultPreview } from "./result-preview.tsx";
|
||||
import classes from "./ai-menu.module.css";
|
||||
import { marked } from "marked";
|
||||
import { DOMSerializer } from "@tiptap/pm/model";
|
||||
import { copyToClipboard, htmlToMarkdown } from "@docmost/editor-ext";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
interface EditorAiMenuProps {
|
||||
editor: Editor | null;
|
||||
}
|
||||
|
||||
const EditorAiMenu = ({ editor }: EditorAiMenuProps): JSX.Element | null => {
|
||||
const aiGenerateStreamMutation = useAiGenerateStreamMutation();
|
||||
const location = useLocation();
|
||||
const isSmBreakpoint = useMediaQuery("(max-width: 48em)");
|
||||
const [showAiMenu, setShowAiMenu] = useAtom(showAiMenuAtom);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [prompt, setPrompt] = useState("");
|
||||
const [output, setOutput] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
const [activeCommandSet, setActiveCommandSet] = useState<CommandSet>("main");
|
||||
const [lastAction, setLastAction] = useState<CommandItem | null>(null);
|
||||
const [menuPlacement, setMenuPlacement] = useState<{
|
||||
top: number;
|
||||
left: number;
|
||||
width: number;
|
||||
}>({
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 0,
|
||||
});
|
||||
const currentItems = useMemo(() => {
|
||||
return commandItems[activeCommandSet].filter((item) => {
|
||||
return item.name.toLowerCase().includes(prompt.toLowerCase());
|
||||
});
|
||||
}, [prompt, output, activeCommandSet]);
|
||||
const updateMenuPlacement = useCallback(() => {
|
||||
if (!editor || !showAiMenu) return;
|
||||
|
||||
const { view } = editor;
|
||||
const { from, to } = editor.state.selection;
|
||||
const editorRect = view.dom.getBoundingClientRect();
|
||||
const fromCoords = view.coordsAtPos(from);
|
||||
const toCoords = view.coordsAtPos(to);
|
||||
const topOffset = 8;
|
||||
const editorPadding = isSmBreakpoint ? 16 : 48;
|
||||
|
||||
const anchorBottom =
|
||||
toCoords.bottom > 0 && toCoords.bottom < window.innerHeight
|
||||
? toCoords.bottom
|
||||
: fromCoords.bottom;
|
||||
|
||||
const menuMaxWidth = 600;
|
||||
const editorLeft = editorRect.left + editorPadding;
|
||||
const editorRight = editorRect.right - editorPadding;
|
||||
const availableWidth = editorRight - editorLeft;
|
||||
const menuWidth = Math.min(menuMaxWidth, availableWidth);
|
||||
|
||||
let menuLeft = Math.max(editorLeft, fromCoords.left);
|
||||
if (menuLeft + menuWidth > editorRight) {
|
||||
menuLeft = editorRight - menuWidth;
|
||||
}
|
||||
menuLeft = Math.max(editorLeft, menuLeft);
|
||||
|
||||
setMenuPlacement({
|
||||
top: anchorBottom + topOffset + window.scrollY,
|
||||
left: menuLeft + window.scrollX,
|
||||
width: menuWidth,
|
||||
});
|
||||
}, [editor, showAiMenu, isSmBreakpoint]);
|
||||
const resetMenu = useCallback(() => {
|
||||
setPrompt("");
|
||||
setOutput("");
|
||||
setActiveCommandSet("main");
|
||||
setLastAction(null);
|
||||
aiGenerateStreamMutation.reset();
|
||||
}, [aiGenerateStreamMutation.reset]);
|
||||
const debouncedUpdateMenuPlacement = useDebouncedCallback(
|
||||
updateMenuPlacement,
|
||||
60,
|
||||
);
|
||||
const handleGenerate = useCallback(
|
||||
(item?: CommandItem) => {
|
||||
if (!editor || isLoading) return;
|
||||
|
||||
let command: CommandItem | null = item || null;
|
||||
|
||||
if (!command) {
|
||||
if (!prompt) return;
|
||||
|
||||
command = {
|
||||
id: "custom",
|
||||
name: "Custom",
|
||||
action: AiAction.CUSTOM,
|
||||
prompt,
|
||||
};
|
||||
}
|
||||
|
||||
const { from, to } = editor.state.selection;
|
||||
const slice = editor.state.doc.slice(from, to);
|
||||
const serializer = DOMSerializer.fromSchema(editor.schema);
|
||||
const fragment = serializer.serializeFragment(slice.content);
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.appendChild(fragment);
|
||||
const content = htmlToMarkdown(wrapper.innerHTML);
|
||||
|
||||
setOutput("");
|
||||
setIsLoading(true);
|
||||
aiGenerateStreamMutation.mutate({
|
||||
action: command.action,
|
||||
prompt: command.prompt,
|
||||
content,
|
||||
onChunk: (chunk) => {
|
||||
setOutput((output) => output + chunk.content);
|
||||
},
|
||||
onComplete: () => {
|
||||
setPrompt("");
|
||||
setIsLoading(false);
|
||||
setActiveCommandSet("result");
|
||||
},
|
||||
onError: () => {
|
||||
setIsLoading(false);
|
||||
resetMenu();
|
||||
},
|
||||
});
|
||||
setLastAction(command);
|
||||
},
|
||||
[
|
||||
editor,
|
||||
prompt,
|
||||
isLoading,
|
||||
aiGenerateStreamMutation.mutateAsync,
|
||||
resetMenu,
|
||||
],
|
||||
);
|
||||
const handleCommand = useCallback(
|
||||
(item?: CommandItem) => {
|
||||
setPrompt("");
|
||||
|
||||
if (!item) {
|
||||
return handleGenerate();
|
||||
}
|
||||
if (item.id === "back") {
|
||||
return setActiveCommandSet("main");
|
||||
}
|
||||
if (item.id === "result-replace") {
|
||||
const chain = editor.chain().focus();
|
||||
|
||||
if (lastAction.action === AiAction.CONTINUE_WRITING) {
|
||||
chain.setTextSelection(editor.state.selection.to);
|
||||
}
|
||||
|
||||
const html = (marked.parse(output) as string).trim();
|
||||
const isSingleParagraph =
|
||||
html.startsWith("<p>") &&
|
||||
html.endsWith("</p>") &&
|
||||
html.lastIndexOf("<p>") === 0;
|
||||
|
||||
// Strip <p> wrapper for single-paragraph output to preserve inline context,
|
||||
// then decode HTML entities via DOMParser since TipTap would otherwise
|
||||
// treat the tagless string as plain text and insert entities literally.
|
||||
const content = isSingleParagraph
|
||||
? new DOMParser().parseFromString(html.slice(3, -4), "text/html")
|
||||
.body.innerHTML
|
||||
: html;
|
||||
|
||||
chain.insertContent(content).run();
|
||||
|
||||
return setShowAiMenu(false);
|
||||
}
|
||||
if (item.id === "result-insert-below") {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.setTextSelection(editor.state.selection.to)
|
||||
.insertContent(marked.parse(output))
|
||||
.run();
|
||||
|
||||
return setShowAiMenu(false);
|
||||
}
|
||||
if (item.id === "result-copy") {
|
||||
copyToClipboard(output);
|
||||
|
||||
return setShowAiMenu(false);
|
||||
}
|
||||
if (item.id === "result-discard") {
|
||||
setOutput("");
|
||||
|
||||
return resetMenu();
|
||||
}
|
||||
if (item.id === "result-try-again" && lastAction) {
|
||||
return handleGenerate(lastAction);
|
||||
}
|
||||
if (item.subCommandSet) {
|
||||
return setActiveCommandSet(item.subCommandSet);
|
||||
}
|
||||
|
||||
return handleGenerate(item);
|
||||
},
|
||||
[editor, output, lastAction, handleGenerate, resetMenu],
|
||||
);
|
||||
const handleKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
const totalItems = currentItems.length;
|
||||
const cycleSize = totalItems + 1;
|
||||
|
||||
if (event.key === "Escape") {
|
||||
return setShowAiMenu(false);
|
||||
}
|
||||
|
||||
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
||||
event.preventDefault();
|
||||
|
||||
return setSelectedIndex((selectedIndex) => {
|
||||
const direction = event.key === "ArrowDown" ? 1 : -1;
|
||||
const newIndex = selectedIndex + direction;
|
||||
|
||||
if (newIndex < -1) return cycleSize - 1;
|
||||
if (newIndex >= cycleSize) return 0;
|
||||
|
||||
return newIndex;
|
||||
});
|
||||
}
|
||||
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
|
||||
return handleCommand(currentItems[selectedIndex]);
|
||||
}
|
||||
},
|
||||
[currentItems, selectedIndex],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
|
||||
const handleClose = () => setShowAiMenu(false);
|
||||
const observer = new ResizeObserver(() => {
|
||||
debouncedUpdateMenuPlacement();
|
||||
});
|
||||
|
||||
updateMenuPlacement();
|
||||
editor.on("focus", handleClose);
|
||||
editor.on("blur", handleClose);
|
||||
window.addEventListener("resize", debouncedUpdateMenuPlacement);
|
||||
window.addEventListener("scroll", debouncedUpdateMenuPlacement, true);
|
||||
observer.observe(editor.view.dom);
|
||||
|
||||
return () => {
|
||||
editor.off("focus", handleClose);
|
||||
editor.off("blur", handleClose);
|
||||
window.removeEventListener("resize", debouncedUpdateMenuPlacement);
|
||||
window.removeEventListener("scroll", debouncedUpdateMenuPlacement, true);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [editor, updateMenuPlacement, debouncedUpdateMenuPlacement]);
|
||||
|
||||
useEffect(() => {
|
||||
setShowAiMenu(false);
|
||||
}, [location]);
|
||||
useEffect(() => {
|
||||
if (showAiMenu) {
|
||||
resetMenu();
|
||||
}
|
||||
}, [showAiMenu, resetMenu]);
|
||||
useEffect(() => {
|
||||
// Focus input when menu opens or command set changes
|
||||
requestAnimationFrame(() => {
|
||||
inputRef.current?.focus({ preventScroll: true });
|
||||
});
|
||||
}, [showAiMenu, isLoading, currentItems]);
|
||||
useEffect(() => {
|
||||
if (!currentItems.length) {
|
||||
setSelectedIndex(-1);
|
||||
}
|
||||
setSelectedIndex(prompt || activeCommandSet !== "main" ? 0 : -1);
|
||||
}, [prompt, activeCommandSet, currentItems]);
|
||||
|
||||
if (!showAiMenu) return null;
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
style={{
|
||||
zIndex: 199,
|
||||
position: "absolute",
|
||||
top: menuPlacement.top,
|
||||
left: menuPlacement.left,
|
||||
width: menuPlacement.width,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={classes.aiMenu}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
tabIndex={0}
|
||||
ref={containerRef}
|
||||
>
|
||||
<ResultPreview output={output} isLoading={isLoading} />
|
||||
<CommandSelector
|
||||
selectedIndex={selectedIndex}
|
||||
isLoading={isLoading}
|
||||
output={output}
|
||||
currentItems={currentItems}
|
||||
handleCommand={handleCommand}
|
||||
>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
className={classes.aiInput}
|
||||
placeholder="Ask AI..."
|
||||
data-autofocus
|
||||
value={prompt}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setPrompt(e.currentTarget.value)}
|
||||
rightSection={
|
||||
<ActionIcon
|
||||
disabled={!prompt || isLoading}
|
||||
variant="filled"
|
||||
color="blue"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
onClick={() => handleGenerate()}
|
||||
>
|
||||
<IconArrowUp size={14} stroke={2.5} />
|
||||
</ActionIcon>
|
||||
}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</CommandSelector>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
};
|
||||
|
||||
export { EditorAiMenu };
|
||||
@@ -0,0 +1,219 @@
|
||||
import { AiAction } from "@/ee/ai/types/ai.types.ts";
|
||||
import {
|
||||
IconSparkles,
|
||||
IconArrowsMaximize,
|
||||
IconArrowsMinimize,
|
||||
IconWriting,
|
||||
IconHelp,
|
||||
IconList,
|
||||
IconMoodSmile,
|
||||
IconLanguage,
|
||||
IconTrash,
|
||||
IconRefresh,
|
||||
IconChevronLeft,
|
||||
IconCheck,
|
||||
IconArrowDownLeft,
|
||||
IconCopy,
|
||||
IconTextPlus,
|
||||
IconAlignJustified,
|
||||
} from "@tabler/icons-react";
|
||||
|
||||
interface CommandItem {
|
||||
name: string;
|
||||
id: string;
|
||||
icon?: typeof IconSparkles;
|
||||
action?: AiAction;
|
||||
prompt?: string;
|
||||
subCommandSet?: CommandSet;
|
||||
}
|
||||
|
||||
type CommandSet = "main" | "tone" | "translate" | "result";
|
||||
|
||||
const mainItems: CommandItem[] = [
|
||||
{
|
||||
id: "improve-writing",
|
||||
name: "Improve writing",
|
||||
icon: IconSparkles,
|
||||
action: AiAction.IMPROVE_WRITING,
|
||||
},
|
||||
{
|
||||
id: "fix-spelling-grammar",
|
||||
name: "Fix spelling & grammar",
|
||||
icon: IconCheck,
|
||||
action: AiAction.FIX_SPELLING_GRAMMAR,
|
||||
},
|
||||
{
|
||||
id: "make-longer",
|
||||
name: "Make longer",
|
||||
icon: IconTextPlus,
|
||||
action: AiAction.MAKE_LONGER,
|
||||
},
|
||||
{
|
||||
id: "make-shorter",
|
||||
name: "Make shorter",
|
||||
icon: IconAlignJustified,
|
||||
action: AiAction.MAKE_SHORTER,
|
||||
},
|
||||
{
|
||||
id: "continue-writing",
|
||||
name: "Continue writing",
|
||||
icon: IconWriting,
|
||||
action: AiAction.CONTINUE_WRITING,
|
||||
},
|
||||
{
|
||||
id: "explain",
|
||||
name: "Explain",
|
||||
icon: IconHelp,
|
||||
action: AiAction.EXPLAIN,
|
||||
},
|
||||
{
|
||||
id: "summarize",
|
||||
name: "Summarize",
|
||||
icon: IconList,
|
||||
action: AiAction.SUMMARIZE,
|
||||
},
|
||||
{
|
||||
id: "change-tone",
|
||||
name: "Change tone",
|
||||
icon: IconMoodSmile,
|
||||
subCommandSet: "tone",
|
||||
},
|
||||
{
|
||||
id: "translate",
|
||||
name: "Translate",
|
||||
icon: IconLanguage,
|
||||
subCommandSet: "translate",
|
||||
},
|
||||
];
|
||||
const toneItems: CommandItem[] = [
|
||||
{
|
||||
id: "back",
|
||||
name: "Back",
|
||||
icon: IconChevronLeft,
|
||||
},
|
||||
{
|
||||
id: "tone-professional",
|
||||
name: "Professional",
|
||||
icon: IconMoodSmile,
|
||||
action: AiAction.CHANGE_TONE,
|
||||
prompt: "Professional",
|
||||
},
|
||||
{
|
||||
id: "tone-casual",
|
||||
name: "Casual",
|
||||
icon: IconMoodSmile,
|
||||
action: AiAction.CHANGE_TONE,
|
||||
prompt: "Casual",
|
||||
},
|
||||
{
|
||||
id: "tone-friendly",
|
||||
name: "Friendly",
|
||||
icon: IconMoodSmile,
|
||||
action: AiAction.CHANGE_TONE,
|
||||
prompt: "Friendly",
|
||||
},
|
||||
];
|
||||
const translateItems: CommandItem[] = [
|
||||
{
|
||||
id: "back",
|
||||
name: "Back",
|
||||
icon: IconChevronLeft,
|
||||
},
|
||||
{
|
||||
id: "translate-english",
|
||||
name: "English",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "English",
|
||||
},
|
||||
{
|
||||
id: "translate-spanish",
|
||||
name: "Spanish",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Spanish",
|
||||
},
|
||||
{
|
||||
id: "translate-german",
|
||||
name: "German",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "German",
|
||||
},
|
||||
{
|
||||
id: "translate-french",
|
||||
name: "French",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "French",
|
||||
},
|
||||
{
|
||||
id: "translate-dutch",
|
||||
name: "Dutch",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Dutch",
|
||||
},
|
||||
{
|
||||
id: "translate-portuguese",
|
||||
name: "Portuguese",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Portuguese",
|
||||
},
|
||||
{
|
||||
id: "translate-italian",
|
||||
name: "Italian",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Italian",
|
||||
},
|
||||
{
|
||||
id: "translate-japanese",
|
||||
name: "Japanese",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Japanese",
|
||||
},
|
||||
{
|
||||
id: "translate-korean",
|
||||
name: "Korean",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Korean",
|
||||
},
|
||||
{
|
||||
id: "translate-swedish",
|
||||
name: "Swedish",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Swedish",
|
||||
},
|
||||
{
|
||||
id: "translate-chinese",
|
||||
name: "Chinese (Simplified)",
|
||||
icon: IconLanguage,
|
||||
action: AiAction.TRANSLATE,
|
||||
prompt: "Simplified Chinese",
|
||||
},
|
||||
];
|
||||
const resultItems: CommandItem[] = [
|
||||
{ id: "result-replace", name: "Replace", icon: IconCheck },
|
||||
{ id: "result-insert-below", name: "Insert below", icon: IconArrowDownLeft },
|
||||
{ id: "result-copy", name: "Copy", icon: IconCopy },
|
||||
{ id: "result-discard", name: "Discard", icon: IconTrash },
|
||||
{
|
||||
id: "result-try-again",
|
||||
name: "Try again",
|
||||
icon: IconRefresh,
|
||||
},
|
||||
];
|
||||
const commandItems: Record<CommandSet, CommandItem[]> = {
|
||||
main: mainItems,
|
||||
tone: toneItems,
|
||||
translate: translateItems,
|
||||
result: resultItems,
|
||||
};
|
||||
|
||||
export type { CommandItem, CommandSet };
|
||||
export { commandItems };
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Loader, Menu, ScrollArea } from "@mantine/core";
|
||||
import { IconChevronRight } from "@tabler/icons-react";
|
||||
import { ReactNode } from "react";
|
||||
import { CommandItem } from "./command-items.ts";
|
||||
import classes from "./ai-menu.module.css";
|
||||
|
||||
interface CommandSelectorProps {
|
||||
selectedIndex: number;
|
||||
|
||||
isLoading: boolean;
|
||||
output: string;
|
||||
currentItems: CommandItem[];
|
||||
children: ReactNode;
|
||||
handleCommand(item: CommandItem): void;
|
||||
}
|
||||
|
||||
const CommandSelector = ({
|
||||
selectedIndex,
|
||||
children,
|
||||
isLoading,
|
||||
output,
|
||||
currentItems,
|
||||
handleCommand,
|
||||
}: CommandSelectorProps) => {
|
||||
return (
|
||||
<Menu
|
||||
opened={!isLoading && currentItems.length > 0}
|
||||
middlewares={{ flip: false }}
|
||||
position="bottom-start"
|
||||
offset={4}
|
||||
width={250}
|
||||
trapFocus={false}
|
||||
shadow="lg"
|
||||
>
|
||||
<Menu.Target>{children}</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<ScrollArea.Autosize type="scroll" scrollbarSize={5} mah={300}>
|
||||
{currentItems.map((item, index) => {
|
||||
const isSelected = selectedIndex === index;
|
||||
const showLoader =
|
||||
isLoading && output === "" && !item.subCommandSet;
|
||||
|
||||
return (
|
||||
<Menu.Item
|
||||
key={item.id}
|
||||
className={isSelected ? classes.menuItemSelected : undefined}
|
||||
leftSection={
|
||||
showLoader ? (
|
||||
<Loader size={14} />
|
||||
) : item.icon ? (
|
||||
<item.icon size={16} />
|
||||
) : undefined
|
||||
}
|
||||
rightSection={
|
||||
item.subCommandSet ? (
|
||||
<IconChevronRight size={14} />
|
||||
) : undefined
|
||||
}
|
||||
onClick={() => handleCommand(item)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{item.name}
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</ScrollArea.Autosize>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export { CommandSelector };
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Loader, Paper, ScrollArea } from "@mantine/core";
|
||||
import DOMPurify from "dompurify";
|
||||
import { marked } from "marked";
|
||||
import { memo } from "react";
|
||||
import classes from "./ai-menu.module.css";
|
||||
|
||||
interface ResultPreviewProps {
|
||||
output: string;
|
||||
isLoading: boolean;
|
||||
}
|
||||
const ResultPreview = memo(({ output, isLoading }: ResultPreviewProps) => {
|
||||
if (!output && !isLoading) return;
|
||||
|
||||
const parsedOutput = `${marked.parse(output)}`;
|
||||
|
||||
return (
|
||||
<Paper mb={4} shadow="lg" radius="md" className={classes.resultPreview}>
|
||||
<ScrollArea.Autosize mah={300} type="scroll" scrollbarSize={5}>
|
||||
<div className={classes.resultPreviewWrapper}>
|
||||
{parsedOutput && (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(parsedOutput) }}
|
||||
/>
|
||||
)}
|
||||
{isLoading && <Loader size={12} ml="xs" display="inline-block" />}
|
||||
</div>
|
||||
</ScrollArea.Autosize>
|
||||
</Paper>
|
||||
);
|
||||
});
|
||||
|
||||
export { ResultPreview };
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Group, Text, Switch, MantineSize, Title } from "@mantine/core";
|
||||
import { Group, Text, Switch, MantineSize, Tooltip } from "@mantine/core";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { isCloud } from "@/lib/config.ts";
|
||||
import useLicense from "@/ee/hooks/use-license.tsx";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature";
|
||||
import { Feature } from "@/ee/features";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
|
||||
|
||||
export default function EnableAiSearch() {
|
||||
const { t } = useTranslation();
|
||||
@@ -15,7 +16,7 @@ export default function EnableAiSearch() {
|
||||
<>
|
||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
||||
<div>
|
||||
<Text size="md">{t("AI-powered search (Ask AI)")}</Text>
|
||||
<Text size="md">{t("AI-powered search (AI Answers)")}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t(
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.",
|
||||
@@ -37,9 +38,8 @@ export function AiSearchToggle({ size, label }: AiSearchToggleProps) {
|
||||
const { t } = useTranslation();
|
||||
const [workspace, setWorkspace] = useAtom(workspaceAtom);
|
||||
const [checked, setChecked] = useState(workspace?.settings?.ai?.search);
|
||||
const { hasLicenseKey } = useLicense();
|
||||
|
||||
const hasAccess = isCloud() || (!isCloud() && hasLicenseKey);
|
||||
const hasAccess = useHasFeature(Feature.AI);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.checked;
|
||||
@@ -56,14 +56,16 @@ export function AiSearchToggle({ size, label }: AiSearchToggleProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Switch
|
||||
size={size}
|
||||
label={label}
|
||||
labelPosition="left"
|
||||
defaultChecked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={!hasAccess}
|
||||
aria-label={t("Toggle AI search")}
|
||||
/>
|
||||
<Tooltip label={upgradeLabel} disabled={hasAccess} refProp="rootRef">
|
||||
<Switch
|
||||
size={size}
|
||||
label={label}
|
||||
labelPosition="left"
|
||||
defaultChecked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={!hasAccess}
|
||||
aria-label={t("Toggle AI search")}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Group, Text, Switch, Tooltip } from "@mantine/core";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature";
|
||||
import { Feature } from "@/ee/features";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
|
||||
|
||||
export default function EnableGenerativeAi() {
|
||||
const { t } = useTranslation();
|
||||
const [workspace, setWorkspace] = useAtom(workspaceAtom);
|
||||
const [checked, setChecked] = useState(workspace?.settings?.ai?.generative);
|
||||
const hasAccess = useHasFeature(Feature.AI);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.checked;
|
||||
try {
|
||||
const updatedWorkspace = await updateWorkspace({ generativeAi: value });
|
||||
setChecked(value);
|
||||
setWorkspace(updatedWorkspace);
|
||||
} catch (err) {
|
||||
notifications.show({
|
||||
message: err?.response?.data?.message,
|
||||
color: "red",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
||||
<div>
|
||||
<Text size="md">{t("Generative AI (Ask AI)")}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t(
|
||||
"Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.",
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Tooltip label={upgradeLabel} disabled={hasAccess} refProp="rootRef">
|
||||
<Switch
|
||||
defaultChecked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={!hasAccess}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
Anchor,
|
||||
Group,
|
||||
List,
|
||||
Text,
|
||||
Switch,
|
||||
TextInput,
|
||||
ActionIcon,
|
||||
Tooltip,
|
||||
Stack,
|
||||
Alert,
|
||||
} from "@mantine/core";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import React, { useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature";
|
||||
import { Feature } from "@/ee/features";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
|
||||
import { getAppUrl } from "@/lib/config.ts";
|
||||
import { IconCheck, IconCopy, IconInfoCircle } from "@tabler/icons-react";
|
||||
import { CopyButton } from "@/components/common/copy-button.tsx";
|
||||
|
||||
export default function McpSettings() {
|
||||
const { t } = useTranslation();
|
||||
const [workspace, setWorkspace] = useAtom(workspaceAtom);
|
||||
const [checked, setChecked] = useState(workspace?.settings?.ai?.mcp);
|
||||
const hasAccess = useHasFeature(Feature.MCP);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
|
||||
const mcpUrl = `${getAppUrl()}/mcp`;
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.checked;
|
||||
try {
|
||||
const updatedWorkspace = await updateWorkspace({ mcpEnabled: value });
|
||||
setChecked(value);
|
||||
setWorkspace(updatedWorkspace);
|
||||
} catch (err) {
|
||||
notifications.show({
|
||||
message: err?.response?.data?.message,
|
||||
color: "red",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
{!hasAccess && (
|
||||
<Alert icon={<IconInfoCircle />} title={upgradeLabel} color="blue">
|
||||
{t(
|
||||
"MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.",
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
||||
<div>
|
||||
<Text size="md">{t("Model Context Protocol (MCP)")}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t(
|
||||
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.",
|
||||
)}{" "}
|
||||
<Trans
|
||||
i18nKey="View the <anchor>MCP documentation</anchor>."
|
||||
components={{
|
||||
anchor: <Anchor href="https://docmost.com/docs/user-guide/mcp" target="_blank" size="sm" />,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Tooltip label={upgradeLabel} disabled={hasAccess} refProp="rootRef">
|
||||
<Switch
|
||||
defaultChecked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={!hasAccess}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
|
||||
{checked && (
|
||||
<div>
|
||||
<Text size="sm" fw={500} mb={4}>
|
||||
{t("MCP Server URL")}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
<TextInput value={mcpUrl} readOnly style={{ flex: 1 }} />
|
||||
<CopyButton value={mcpUrl} timeout={2000}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip
|
||||
label={copied ? t("Copied") : t("Copy")}
|
||||
withArrow
|
||||
position="right"
|
||||
>
|
||||
<ActionIcon
|
||||
color={copied ? "teal" : "gray"}
|
||||
variant="subtle"
|
||||
onClick={copy}
|
||||
>
|
||||
{copied ? <IconCheck size={16} /> : <IconCopy size={16} />}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
</CopyButton>
|
||||
</Group>
|
||||
<Text size="sm" c="dimmed" mt="xs">
|
||||
{t(
|
||||
"Use your API key for authentication. You can manage API keys in your account settings.",
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={500} mt="md" mb={4}>
|
||||
{t("Supported tools")}
|
||||
</Text>
|
||||
<List size="sm" spacing={2}>
|
||||
<List.Item>
|
||||
<Text size="sm" c="dimmed" span>
|
||||
search_pages, get_page, create_page, update_page
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" c="dimmed" span>
|
||||
list_pages, list_child_pages, duplicate_page
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" c="dimmed" span>
|
||||
copy_page_to_space, move_page, move_page_to_space
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" c="dimmed" span>
|
||||
get_space, list_spaces, create_space, update_space
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" c="dimmed" span>
|
||||
get_comments, create_comment, update_comment
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" c="dimmed" span>
|
||||
search_attachments, list_workspace_members, get_current_user
|
||||
</Text>
|
||||
</List.Item>
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation, UseMutationResult } from "@tanstack/react-query";
|
||||
import { useState, useCallback } from "react";
|
||||
import { askAi, IAiSearchResponse } from "@/ee/ai/services/ai-search-service.ts";
|
||||
import { aiAnswers, IAiSearchResponse } from "@/ee/ai/services/ai-search-service.ts";
|
||||
import { IPageSearchParams } from "@/features/search/types/search.types.ts";
|
||||
|
||||
// @ts-ignore
|
||||
@@ -26,7 +26,7 @@ export function useAiSearch(): UseAiSearchResult {
|
||||
|
||||
const { contentType, ...apiParams } = params;
|
||||
|
||||
return await askAi(apiParams, (chunk) => {
|
||||
return await aiAnswers(apiParams, (chunk) => {
|
||||
if (chunk.content) {
|
||||
setStreamingAnswer((prev) => prev + chunk.content);
|
||||
}
|
||||
|
||||
@@ -1,46 +1,85 @@
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { getAppName, isCloud } from "@/lib/config.ts";
|
||||
import { getAppName } from "@/lib/config.ts";
|
||||
import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import React from "react";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useLicense from "@/ee/hooks/use-license.tsx";
|
||||
import EnableAiSearch from "@/ee/ai/components/enable-ai-search.tsx";
|
||||
import { Alert } from "@mantine/core";
|
||||
import EnableGenerativeAi from "@/ee/ai/components/enable-generative-ai.tsx";
|
||||
import EnableAiChat from "@/ee/ai-chat/components/enable-ai-chat.tsx";
|
||||
import McpSettings from "@/ee/ai/components/mcp-settings.tsx";
|
||||
import { Alert, Stack, Tabs } from "@mantine/core";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature";
|
||||
import { Feature } from "@/ee/features";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
|
||||
import { isCloud } from "@/lib/config.ts";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
export default function AiSettings() {
|
||||
const { t } = useTranslation();
|
||||
const { isAdmin } = useUserRole();
|
||||
const { hasLicenseKey } = useLicense();
|
||||
const hasAccess = useHasFeature(Feature.AI);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const activeTab = location.pathname.endsWith("/mcp") ? "mcp" : "ai";
|
||||
|
||||
if (!isAdmin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasAccess = isCloud() || (!isCloud() && hasLicenseKey);
|
||||
const handleTabChange = (value: string | null) => {
|
||||
if (value === "mcp") {
|
||||
navigate("/settings/ai/mcp");
|
||||
} else {
|
||||
navigate("/settings/ai");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>AI - {getAppName()}</title>
|
||||
<title>AI settings - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("AI settings")} />
|
||||
|
||||
{!hasAccess && (
|
||||
<Alert
|
||||
icon={<IconInfoCircle />}
|
||||
title={t("Enterprise feature")}
|
||||
color="blue"
|
||||
mb="lg"
|
||||
>
|
||||
{t(
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.",
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
<Tabs color="dark" value={activeTab} onChange={handleTabChange}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab fw={500} value="ai">
|
||||
{t("AI")}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab fw={500} value="mcp">
|
||||
{t("MCP")}
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<EnableAiSearch />
|
||||
<Tabs.Panel value="ai" pt="md">
|
||||
{!hasAccess && (
|
||||
<Alert
|
||||
icon={<IconInfoCircle />}
|
||||
title={upgradeLabel}
|
||||
color="blue"
|
||||
mb="lg"
|
||||
>
|
||||
{t(
|
||||
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.",
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Stack gap="md">
|
||||
{!isCloud() && <EnableAiSearch />}
|
||||
<EnableGenerativeAi />
|
||||
<EnableAiChat />
|
||||
</Stack>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="mcp" pt="md">
|
||||
<McpSettings />
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ export interface IAiSearchResponse {
|
||||
}>;
|
||||
}
|
||||
|
||||
export async function askAi(
|
||||
export async function aiAnswers(
|
||||
params: IPageSearchParams,
|
||||
onChunk?: (chunk: { content?: string; sources?: any[] }) => void,
|
||||
): Promise<IAiSearchResponse> {
|
||||
const response = await fetch("/api/ai/ask", {
|
||||
const response = await fetch("/api/ai/answers", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -43,13 +43,16 @@ export async function generateAiContentStream(
|
||||
}
|
||||
|
||||
const processStream = async () => {
|
||||
let buffer = "";
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
const lines = chunk.split("\n");
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split("\n");
|
||||
|
||||
buffer = lines.pop() || "";
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
@@ -66,7 +69,7 @@ export async function generateAiContentStream(
|
||||
onChunk(parsed);
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore parse errors for incomplete chunks
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export enum AiAction {
|
||||
SIMPLIFY = "simplify",
|
||||
CHANGE_TONE = "change_tone",
|
||||
SUMMARIZE = "summarize",
|
||||
EXPLAIN = "explain",
|
||||
CONTINUE_WRITING = "continue_writing",
|
||||
TRANSLATE = "translate",
|
||||
CUSTOM = "custom",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { lazy, Suspense, useState } from "react";
|
||||
import { Modal, TextInput, Button, Group, Stack, Select } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { zodResolver } from "mantine-form-zod-resolver";
|
||||
import { z } from "zod";
|
||||
import { zod4Resolver } from "mantine-form-zod-resolver";
|
||||
import { z } from "zod/v4";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useCreateApiKeyMutation } from "@/ee/api-key/queries/api-key-query";
|
||||
import { IconCalendar } from "@tabler/icons-react";
|
||||
@@ -36,7 +36,7 @@ export function CreateApiKeyModal({
|
||||
const createApiKeyMutation = useCreateApiKeyMutation();
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
validate: zodResolver(formSchema),
|
||||
validate: zod4Resolver(formSchema),
|
||||
initialValues: {
|
||||
name: "",
|
||||
expiresAt: "",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Text, Switch, Tooltip } from "@mantine/core";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useHasFeature } from "@/ee/hooks/use-feature";
|
||||
import { Feature } from "@/ee/features";
|
||||
import {
|
||||
ResponsiveSettingsRow,
|
||||
ResponsiveSettingsContent,
|
||||
ResponsiveSettingsControl,
|
||||
} from "@/components/ui/responsive-settings-row";
|
||||
import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label.ts";
|
||||
|
||||
export default function RestrictApiToAdmins() {
|
||||
const { t } = useTranslation();
|
||||
const [workspace, setWorkspace] = useAtom(workspaceAtom);
|
||||
const [checked, setChecked] = useState(
|
||||
workspace?.settings?.api?.restrictToAdmins === true,
|
||||
);
|
||||
const hasAccess = useHasFeature(Feature.API_KEYS);
|
||||
const upgradeLabel = useUpgradeLabel();
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.checked;
|
||||
try {
|
||||
const updatedWorkspace = await updateWorkspace({
|
||||
restrictApiToAdmins: value,
|
||||
});
|
||||
setChecked(value);
|
||||
setWorkspace(updatedWorkspace);
|
||||
} catch (err) {
|
||||
notifications.show({
|
||||
message: err?.response?.data?.message,
|
||||
color: "red",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ResponsiveSettingsRow>
|
||||
<ResponsiveSettingsContent>
|
||||
<Text size="md">
|
||||
{t("Restrict API key creation to admins")}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t(
|
||||
"Only admins and owners can create new API keys. Existing member keys will continue to work.",
|
||||
)}
|
||||
</Text>
|
||||
</ResponsiveSettingsContent>
|
||||
|
||||
<ResponsiveSettingsControl>
|
||||
<Tooltip
|
||||
label={upgradeLabel}
|
||||
disabled={hasAccess}
|
||||
refProp="rootRef"
|
||||
>
|
||||
<Switch
|
||||
checked={checked}
|
||||
onChange={handleChange}
|
||||
disabled={!hasAccess}
|
||||
aria-label={t("Toggle restrict API keys to admins")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ResponsiveSettingsControl>
|
||||
</ResponsiveSettingsRow>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Modal, TextInput, Button, Group, Stack } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { zodResolver } from "mantine-form-zod-resolver";
|
||||
import { z } from "zod";
|
||||
import { zod4Resolver } from "mantine-form-zod-resolver";
|
||||
import { z } from "zod/v4";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useUpdateApiKeyMutation } from "@/ee/api-key/queries/api-key-query";
|
||||
import { IApiKey } from "@/ee/api-key";
|
||||
@@ -27,7 +27,7 @@ export function UpdateApiKeyModal({
|
||||
const updateApiKeyMutation = useUpdateApiKeyMutation();
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
validate: zodResolver(formSchema),
|
||||
validate: zod4Resolver(formSchema),
|
||||
initialValues: {
|
||||
name: "",
|
||||
},
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, Group, Space } from "@mantine/core";
|
||||
import { Anchor, Alert, Button, Group, Space, Text } from "@mantine/core";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import SettingsTitle from "@/components/settings/settings-title";
|
||||
import { getAppName } from "@/lib/config";
|
||||
import { getAppName, getAppUrl } from "@/lib/config";
|
||||
import { ApiKeyTable } from "@/ee/api-key/components/api-key-table";
|
||||
import { CreateApiKeyModal } from "@/ee/api-key/components/create-api-key-modal";
|
||||
import { ApiKeyCreatedModal } from "@/ee/api-key/components/api-key-created-modal";
|
||||
@@ -13,6 +14,9 @@ import Paginate from "@/components/common/paginate";
|
||||
import { useCursorPaginate } from "@/hooks/use-cursor-paginate";
|
||||
import { useGetApiKeysQuery } from "@/ee/api-key/queries/api-key-query.ts";
|
||||
import { IApiKey } from "@/ee/api-key";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
|
||||
export default function UserApiKeys() {
|
||||
const { t } = useTranslation();
|
||||
@@ -23,6 +27,11 @@ export default function UserApiKeys() {
|
||||
const [revokeModalOpened, setRevokeModalOpened] = useState(false);
|
||||
const [selectedApiKey, setSelectedApiKey] = useState<IApiKey | null>(null);
|
||||
const { data, isLoading } = useGetApiKeysQuery({ cursor });
|
||||
const [workspace] = useAtom(workspaceAtom);
|
||||
const { isAdmin } = useUserRole();
|
||||
const mcpEnabled = workspace?.settings?.ai?.mcp === true;
|
||||
const restrictToAdmins = workspace?.settings?.api?.restrictToAdmins === true;
|
||||
const canCreate = !restrictToAdmins || isAdmin;
|
||||
|
||||
const handleCreateSuccess = (response: IApiKey) => {
|
||||
setCreatedApiKey(response);
|
||||
@@ -48,11 +57,51 @@ export default function UserApiKeys() {
|
||||
|
||||
<SettingsTitle title={t("API keys")} />
|
||||
|
||||
<Group justify="flex-end" mb="md">
|
||||
<Button onClick={() => setCreateModalOpened(true)}>
|
||||
{t("Create API Key")}
|
||||
</Button>
|
||||
</Group>
|
||||
<Text size="sm" c="dimmed" mb="md">
|
||||
<Trans
|
||||
i18nKey="View the <anchor>API documentation</anchor> for usage details."
|
||||
components={{
|
||||
anchor: <Anchor href="https://docmost.com/api-docs" target="_blank" size="sm" />,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
|
||||
{mcpEnabled && canCreate && (
|
||||
<Alert variant="light" color="blue" mb="md" p="sm" icon={<IconInfoCircle />}>
|
||||
<Text size="sm">
|
||||
{t(
|
||||
"Your workspace has MCP enabled. Use your API key to connect AI assistants.",
|
||||
)}{" "}
|
||||
<Anchor
|
||||
href="https://docmost.com/docs/user-guide/mcp"
|
||||
target="_blank"
|
||||
size="sm"
|
||||
>
|
||||
{t("Learn more")}
|
||||
</Anchor>
|
||||
</Text>
|
||||
<Text size="sm" mt={4}>
|
||||
{t("MCP server URL:")}{" "}
|
||||
<Text size="sm" fw={500} span ff="monospace">
|
||||
{`${getAppUrl()}/mcp`}
|
||||
</Text>
|
||||
</Text>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{canCreate ? (
|
||||
<Group justify="flex-end" mb="md">
|
||||
<Button onClick={() => setCreateModalOpened(true)}>
|
||||
{t("Create API Key")}
|
||||
</Button>
|
||||
</Group>
|
||||
) : restrictToAdmins ? (
|
||||
<Alert variant="light" color="yellow" mb="md" p="sm" icon={<IconInfoCircle />}>
|
||||
<Text size="sm">
|
||||
{t("API key creation is restricted to admins by your workspace administrator.")}
|
||||
</Text>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<ApiKeyTable
|
||||
apiKeys={data?.items || []}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, Group, Space, Text } from "@mantine/core";
|
||||
import { Anchor, Button, Divider, Group, Space, Text } from "@mantine/core";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import SettingsTitle from "@/components/settings/settings-title";
|
||||
import { getAppName } from "@/lib/config";
|
||||
import { ApiKeyTable } from "@/ee/api-key/components/api-key-table";
|
||||
@@ -14,6 +14,7 @@ import { useCursorPaginate } from "@/hooks/use-cursor-paginate";
|
||||
import { useGetApiKeysQuery } from "@/ee/api-key/queries/api-key-query.ts";
|
||||
import { IApiKey } from "@/ee/api-key";
|
||||
import useUserRole from '@/hooks/use-user-role.tsx';
|
||||
import RestrictApiToAdmins from "@/ee/api-key/components/restrict-api-to-admins";
|
||||
|
||||
export default function WorkspaceApiKeys() {
|
||||
const { t } = useTranslation();
|
||||
@@ -54,10 +55,18 @@ export default function WorkspaceApiKeys() {
|
||||
|
||||
<SettingsTitle title={t("API management")} />
|
||||
|
||||
<Text size="md" c="dimmed" mb="md">
|
||||
{t("Manage API keys for all users in the workspace")}
|
||||
<Text size="sm" c="dimmed" mb="md">
|
||||
<Trans
|
||||
i18nKey="Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details."
|
||||
components={{
|
||||
anchor: <Anchor href="https://docmost.com/api-docs" target="_blank" size="sm" />,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
|
||||
<RestrictApiToAdmins />
|
||||
<Divider my="lg" />
|
||||
|
||||
<Group justify="flex-end" mb="md">
|
||||
<Button onClick={() => setCreateModalOpened(true)}>
|
||||
{t("Create API Key")}
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
import { Fragment, useState } from "react";
|
||||
import {
|
||||
Table,
|
||||
Text,
|
||||
Group,
|
||||
Skeleton,
|
||||
Anchor,
|
||||
Collapse,
|
||||
Box,
|
||||
} from "@mantine/core";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
IconChevronRight,
|
||||
IconChevronDown,
|
||||
IconArrowRight,
|
||||
} from "@tabler/icons-react";
|
||||
import { IAuditLog } from "@/ee/audit/types/audit.types";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar";
|
||||
import { getEventLabel } from "@/ee/audit/lib/audit-event-labels";
|
||||
import { formattedDate } from "@/lib/time";
|
||||
import NoTableResults from "@/components/common/no-table-results";
|
||||
import classes from "./audit-logs.module.css";
|
||||
|
||||
type AuditLogsTableProps = {
|
||||
items?: IAuditLog[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
function hasDetails(entry: IAuditLog): boolean {
|
||||
return !!(entry.changes?.before || entry.changes?.after || entry.metadata);
|
||||
}
|
||||
|
||||
function getResourceUrl(entry: IAuditLog): string | null {
|
||||
if (!entry.resource) return null;
|
||||
|
||||
switch (entry.resourceType) {
|
||||
case "group":
|
||||
return `/settings/groups/${entry.resource.id}`;
|
||||
case "space":
|
||||
case "space_member":
|
||||
return entry.resource.slug ? `/s/${entry.resource.slug}` : null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(value: unknown): string {
|
||||
if (value === null || value === undefined) return "—";
|
||||
if (typeof value === "boolean") return value ? "true" : "false";
|
||||
if (typeof value === "object") return JSON.stringify(value);
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function ChangesDiff({ changes }: { changes: IAuditLog["changes"] }) {
|
||||
const { t } = useTranslation();
|
||||
if (!changes) return null;
|
||||
|
||||
const { before, after } = changes;
|
||||
const allKeys = new Set([
|
||||
...Object.keys(before ?? {}),
|
||||
...Object.keys(after ?? {}),
|
||||
]);
|
||||
|
||||
if (allKeys.size === 0) return null;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text fz="xs" fw={600} mb={4}>
|
||||
{t("Changes")}
|
||||
</Text>
|
||||
{[...allKeys].map((key) => {
|
||||
const hasBefore = before && key in before;
|
||||
const hasAfter = after && key in after;
|
||||
|
||||
return (
|
||||
<Group key={key} gap={6} mb={2} wrap="nowrap" align="center">
|
||||
<Text
|
||||
fz="xs"
|
||||
c="dimmed"
|
||||
fw={500}
|
||||
style={{ minWidth: "fit-content" }}
|
||||
>
|
||||
{key}:
|
||||
</Text>
|
||||
{hasBefore && (
|
||||
<Text fz="xs" component="span">
|
||||
{formatValue(before[key])}
|
||||
</Text>
|
||||
)}
|
||||
{hasBefore && hasAfter && (
|
||||
<IconArrowRight size={10} color="var(--mantine-color-dimmed)" />
|
||||
)}
|
||||
{hasAfter && (
|
||||
<Text fz="xs" component="span">
|
||||
{formatValue(after[key])}
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function MetadataDisplay({ metadata }: { metadata: Record<string, any> }) {
|
||||
const { t } = useTranslation();
|
||||
const entries = Object.entries(metadata);
|
||||
if (entries.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text fz="xs" fw={600} mb={4}>
|
||||
{t("Metadata")}
|
||||
</Text>
|
||||
{entries.map(([key, value]) => (
|
||||
<Group key={key} gap={6} mb={2} wrap="nowrap">
|
||||
<Text fz="xs" c="dimmed" fw={500}>
|
||||
{key}:
|
||||
</Text>
|
||||
<Text fz="xs">{formatValue(value)}</Text>
|
||||
</Group>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function TableSkeleton() {
|
||||
return (
|
||||
<>
|
||||
{Array.from({ length: 8 }).map((_, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<Skeleton circle height={36} />
|
||||
<div>
|
||||
<Skeleton height={14} width={120} mb={4} />
|
||||
<Skeleton height={10} width={160} />
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Skeleton height={14} width={140} />
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Skeleton height={14} width={120} />
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Skeleton height={14} width={120} />
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ResourceCell({ entry }: { entry: IAuditLog }) {
|
||||
if (!entry.resource?.name) {
|
||||
return (
|
||||
<Text fz="sm" c="dimmed">
|
||||
—
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const url = getResourceUrl(entry);
|
||||
|
||||
if (url) {
|
||||
return (
|
||||
<Anchor
|
||||
size="sm"
|
||||
underline="never"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--mantine-color-text)",
|
||||
}}
|
||||
component={Link}
|
||||
to={url}
|
||||
>
|
||||
<div className={classes.resourceLinkText}>
|
||||
<Text fz="sm" fw={500} lineClamp={1}>
|
||||
{entry.resource.name}
|
||||
</Text>
|
||||
</div>
|
||||
</Anchor>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Text fz="sm" lineClamp={1}>
|
||||
{entry.resource.name}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AuditLogsTable({
|
||||
items,
|
||||
isLoading,
|
||||
}: AuditLogsTableProps) {
|
||||
const { t } = useTranslation();
|
||||
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
||||
|
||||
const toggleExpanded = (id: string) => {
|
||||
setExpanded((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(id)) {
|
||||
next.delete(id);
|
||||
} else {
|
||||
next.add(id);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Table.ScrollContainer minWidth={700}>
|
||||
<Table highlightOnHover verticalSpacing="xs" className={classes.table}>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Actor")}</Table.Th>
|
||||
<Table.Th>{t("Event")}</Table.Th>
|
||||
<Table.Th>{t("Resource")}</Table.Th>
|
||||
<Table.Th>{t("Date")}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{isLoading ? (
|
||||
<TableSkeleton />
|
||||
) : items && items.length > 0 ? (
|
||||
items.map((entry) => {
|
||||
const expandable = hasDetails(entry);
|
||||
const isExpanded = expanded.has(entry.id);
|
||||
|
||||
return (
|
||||
<Fragment key={entry.id}>
|
||||
<Table.Tr
|
||||
onClick={
|
||||
expandable ? () => toggleExpanded(entry.id) : undefined
|
||||
}
|
||||
style={{ cursor: expandable ? "pointer" : undefined }}
|
||||
>
|
||||
<Table.Td>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
{expandable ? (
|
||||
isExpanded ? (
|
||||
<IconChevronDown
|
||||
size={16}
|
||||
color="var(--mantine-color-dimmed)"
|
||||
/>
|
||||
) : (
|
||||
<IconChevronRight
|
||||
size={16}
|
||||
color="var(--mantine-color-dimmed)"
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<Box w={16} />
|
||||
)}
|
||||
{entry.actor ? (
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<CustomAvatar
|
||||
avatarUrl={entry.actor.avatarUrl}
|
||||
name={entry.actor.name}
|
||||
size={36}
|
||||
/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500} lineClamp={1}>
|
||||
{entry.actor.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{entry.actor.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
) : (
|
||||
<Text fz="sm" c="dimmed" fs="italic">
|
||||
{entry.actorType === "system"
|
||||
? t("System")
|
||||
: t("System")}
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<Text fz="sm">{t(getEventLabel(entry.event))}</Text>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<ResourceCell entry={entry} />
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<Text fz="sm" style={{ whiteSpace: "nowrap" }}>
|
||||
{formattedDate(new Date(entry.createdAt))}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
|
||||
{expandable && (
|
||||
<Table.Tr className={classes.detailRow}>
|
||||
<Table.Td colSpan={4} p={0}>
|
||||
<Collapse in={isExpanded}>
|
||||
<Box
|
||||
px="md"
|
||||
py="sm"
|
||||
className={classes.detailContent}
|
||||
>
|
||||
<Group gap="xl" align="flex-start">
|
||||
{entry.changes && (
|
||||
<ChangesDiff changes={entry.changes} />
|
||||
)}
|
||||
{entry.metadata && (
|
||||
<MetadataDisplay metadata={entry.metadata} />
|
||||
)}
|
||||
</Group>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<NoTableResults colSpan={4} />
|
||||
)}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
.table {
|
||||
--table-border-color: var(--mantine-color-gray-2);
|
||||
|
||||
@mixin dark {
|
||||
--table-border-color: var(--mantine-color-dark-5);
|
||||
}
|
||||
}
|
||||
|
||||
.resourceLinkText {
|
||||
width: fit-content;
|
||||
|
||||
@mixin light {
|
||||
border-bottom: 0.05em solid var(--mantine-color-dark-0);
|
||||
}
|
||||
@mixin dark {
|
||||
border-bottom: 0.05em solid var(--mantine-color-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
.detailRow {
|
||||
&:hover {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.detailContent {
|
||||
@mixin light {
|
||||
background: var(--mantine-color-gray-0);
|
||||
}
|
||||
@mixin dark {
|
||||
background: var(--mantine-color-dark-7);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
type EventOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
type EventGroup = {
|
||||
group: string;
|
||||
items: EventOption[];
|
||||
};
|
||||
|
||||
export const auditEventLabels: Record<string, string> = {
|
||||
"workspace.created": "Created workspace",
|
||||
"workspace.updated": "Updated workspace",
|
||||
"workspace.invite_created": "Created invitation",
|
||||
"workspace.invite_resent": "Resent invitation",
|
||||
"workspace.invite_revoked": "Revoked invitation",
|
||||
|
||||
"user.created": "Created user",
|
||||
"user.deleted": "Deleted user",
|
||||
"user.login": "Logged in",
|
||||
"user.logout": "Logged out",
|
||||
"user.role_changed": "Changed user role",
|
||||
"user.password_changed": "Changed password",
|
||||
"user.password_reset": "Reset password",
|
||||
"user.updated": "Updated user",
|
||||
"user.deactivated": "Deactivated user",
|
||||
"user.activated": "Activated user",
|
||||
"user.mfa_enabled": "Enabled MFA",
|
||||
"user.mfa_disabled": "Disabled MFA",
|
||||
"user.mfa_backup_code_generated": "Generated MFA backup codes",
|
||||
|
||||
"api_key.created": "Created API key",
|
||||
"api_key.updated": "Updated API key",
|
||||
"api_key.deleted": "Deleted API key",
|
||||
|
||||
"space.created": "Created space",
|
||||
"space.updated": "Updated space",
|
||||
"space.deleted": "Deleted space",
|
||||
"space.member_added": "Added space member",
|
||||
"space.member_removed": "Removed space member",
|
||||
"space.member_role_changed": "Changed space member role",
|
||||
"space.exported": "Exported space",
|
||||
|
||||
"group.created": "Created group",
|
||||
"group.updated": "Updated group",
|
||||
"group.deleted": "Deleted group",
|
||||
"group.member_added": "Added group member",
|
||||
"group.member_removed": "Removed group member",
|
||||
|
||||
"comment.deleted": "Deleted comment",
|
||||
|
||||
"page.trashed": "Trashed page",
|
||||
"page.deleted": "Deleted page",
|
||||
"page.restored": "Restored page",
|
||||
"page.imported": "Imported page",
|
||||
"page.exported": "Exported page",
|
||||
"page.restricted": "Restricted page",
|
||||
"page.restriction_removed": "Removed page restriction",
|
||||
"page.permission_added": "Added page permission",
|
||||
"page.permission_removed": "Removed page permission",
|
||||
"page.verification_created": "Created page verification",
|
||||
"page.verification_updated": "Updated page verification",
|
||||
"page.verification_removed": "Removed page verification",
|
||||
"page.verified": "Verified page",
|
||||
"page.approval_requested": "Requested page approval",
|
||||
"page.approval_rejected": "Rejected page approval",
|
||||
"page.marked_obsolete": "Marked page as obsolete",
|
||||
|
||||
"share.created": "Created share link",
|
||||
"share.deleted": "Deleted share link",
|
||||
|
||||
"sso.provider_created": "Created SSO provider",
|
||||
"sso.provider_updated": "Updated SSO provider",
|
||||
"sso.provider_deleted": "Deleted SSO provider",
|
||||
|
||||
"license.activated": "Activated license",
|
||||
"license.removed": "Removed license",
|
||||
};
|
||||
|
||||
export function getEventLabel(event: string): string {
|
||||
return auditEventLabels[event] ?? event;
|
||||
}
|
||||
|
||||
export const eventFilterOptions: EventGroup[] = [
|
||||
{
|
||||
group: "Workspace",
|
||||
items: [
|
||||
{ value: "workspace.updated", label: "Updated workspace" },
|
||||
{ value: "workspace.invite_created", label: "Created invitation" },
|
||||
{ value: "workspace.invite_revoked", label: "Revoked invitation" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "User",
|
||||
items: [
|
||||
{ value: "user.login", label: "Logged in" },
|
||||
{ value: "user.logout", label: "Logged out" },
|
||||
{ value: "user.created", label: "Created user" },
|
||||
{ value: "user.deleted", label: "Deleted user" },
|
||||
{ value: "user.deactivated", label: "Deactivated user" },
|
||||
{ value: "user.activated", label: "Activated user" },
|
||||
{ value: "user.role_changed", label: "Changed user role" },
|
||||
{ value: "user.password_changed", label: "Changed password" },
|
||||
{ value: "user.mfa_enabled", label: "Enabled MFA" },
|
||||
{ value: "user.mfa_disabled", label: "Disabled MFA" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Space",
|
||||
items: [
|
||||
{ value: "space.created", label: "Created space" },
|
||||
{ value: "space.updated", label: "Updated space" },
|
||||
{ value: "space.deleted", label: "Deleted space" },
|
||||
{ value: "space.member_added", label: "Added space member" },
|
||||
{ value: "space.member_removed", label: "Removed space member" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Group",
|
||||
items: [
|
||||
{ value: "group.created", label: "Created group" },
|
||||
{ value: "group.updated", label: "Updated group" },
|
||||
{ value: "group.deleted", label: "Deleted group" },
|
||||
{ value: "group.member_added", label: "Added group member" },
|
||||
{ value: "group.member_removed", label: "Removed group member" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Comment",
|
||||
items: [
|
||||
{ value: "comment.deleted", label: "Deleted comment" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Page",
|
||||
items: [
|
||||
{ value: "page.trashed", label: "Trashed page" },
|
||||
{ value: "page.deleted", label: "Deleted page" },
|
||||
{ value: "page.restored", label: "Restored page" },
|
||||
{ value: "page.imported", label: "Imported page" },
|
||||
{ value: "page.exported", label: "Exported page" },
|
||||
{ value: "page.restricted", label: "Restricted page" },
|
||||
{ value: "page.restriction_removed", label: "Removed page restriction" },
|
||||
{ value: "page.permission_added", label: "Added page permission" },
|
||||
{ value: "page.permission_removed", label: "Removed page permission" },
|
||||
{ value: "page.verification_created", label: "Created page verification" },
|
||||
{ value: "page.verification_updated", label: "Updated page verification" },
|
||||
{ value: "page.verification_removed", label: "Removed page verification" },
|
||||
{ value: "page.verified", label: "Verified page" },
|
||||
{ value: "page.approval_requested", label: "Requested page approval" },
|
||||
{ value: "page.approval_rejected", label: "Rejected page approval" },
|
||||
{ value: "page.marked_obsolete", label: "Marked page as obsolete" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Share",
|
||||
items: [
|
||||
{ value: "share.created", label: "Created share link" },
|
||||
{ value: "share.deleted", label: "Deleted share link" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "SSO",
|
||||
items: [
|
||||
{ value: "sso.provider_created", label: "Created SSO provider" },
|
||||
{ value: "sso.provider_updated", label: "Updated SSO provider" },
|
||||
{ value: "sso.provider_deleted", label: "Deleted SSO provider" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "API key",
|
||||
items: [
|
||||
{ value: "api_key.created", label: "Created API key" },
|
||||
{ value: "api_key.deleted", label: "Deleted API key" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "License",
|
||||
items: [
|
||||
{ value: "license.activated", label: "Activated license" },
|
||||
{ value: "license.removed", label: "Removed license" },
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,223 @@
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
Group,
|
||||
NumberInput,
|
||||
Popover,
|
||||
Select,
|
||||
Space,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconSettings } from "@tabler/icons-react";
|
||||
import SettingsTitle from "@/components/settings/settings-title";
|
||||
import { getAppName } from "@/lib/config";
|
||||
import Paginate from "@/components/common/paginate";
|
||||
import { useCursorPaginate } from "@/hooks/use-cursor-paginate";
|
||||
import {
|
||||
useAuditLogsQuery,
|
||||
useAuditRetentionQuery,
|
||||
useUpdateAuditRetentionMutation,
|
||||
} from "@/ee/audit/queries/audit-query";
|
||||
import { IAuditLogParams } from "@/ee/audit/types/audit.types";
|
||||
import { eventFilterOptions } from "@/ee/audit/lib/audit-event-labels";
|
||||
import AuditLogsTable from "@/ee/audit/components/audit-logs-table";
|
||||
import useUserRole from "@/hooks/use-user-role";
|
||||
|
||||
type RetentionUnit = "days" | "months" | "years";
|
||||
|
||||
function daysToRetention(days: number): { amount: number; unit: RetentionUnit } {
|
||||
if (days >= 365 && days % 365 === 0) {
|
||||
return { amount: days / 365, unit: "years" };
|
||||
}
|
||||
if (days >= 30 && days % 30 === 0) {
|
||||
return { amount: days / 30, unit: "months" };
|
||||
}
|
||||
return { amount: days, unit: "days" };
|
||||
}
|
||||
|
||||
function retentionToDays(amount: number, unit: RetentionUnit): number {
|
||||
if (unit === "years") return amount * 365;
|
||||
if (unit === "months") return amount * 30;
|
||||
return amount;
|
||||
}
|
||||
|
||||
export default function AuditLogs() {
|
||||
const { t } = useTranslation();
|
||||
const { isOwner } = useUserRole();
|
||||
const { cursor, goNext, goPrev, resetCursor } = useCursorPaginate();
|
||||
|
||||
const [eventFilter, setEventFilter] = useState<string | null>(null);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
|
||||
const { data: retentionData } = useAuditRetentionQuery();
|
||||
const updateRetention = useUpdateAuditRetentionMutation();
|
||||
|
||||
const currentDays = retentionData?.retentionDays ?? 365;
|
||||
const parsed = daysToRetention(currentDays);
|
||||
const [retentionAmount, setRetentionAmount] = useState<number | string>(parsed.amount);
|
||||
const [retentionUnit, setRetentionUnit] = useState<RetentionUnit>(parsed.unit);
|
||||
|
||||
useEffect(() => {
|
||||
if (retentionData) {
|
||||
const { amount, unit } = daysToRetention(retentionData.retentionDays);
|
||||
setRetentionAmount(amount);
|
||||
setRetentionUnit(unit);
|
||||
}
|
||||
}, [retentionData?.retentionDays]);
|
||||
|
||||
const resetRetentionForm = () => {
|
||||
const { amount, unit } = daysToRetention(currentDays);
|
||||
setRetentionAmount(amount);
|
||||
setRetentionUnit(unit);
|
||||
};
|
||||
|
||||
const params: IAuditLogParams = useMemo(
|
||||
() => ({
|
||||
cursor,
|
||||
limit: 50,
|
||||
event: eventFilter ?? undefined,
|
||||
}),
|
||||
[cursor, eventFilter],
|
||||
);
|
||||
|
||||
const { data, isLoading } = useAuditLogsQuery(params);
|
||||
|
||||
if (!isOwner) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleEventChange = (value: string | null) => {
|
||||
setEventFilter(value);
|
||||
resetCursor();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t("Audit log")} - {getAppName()}
|
||||
</title>
|
||||
</Helmet>
|
||||
|
||||
<SettingsTitle title={t("Audit log")} />
|
||||
|
||||
<Group mb="md" gap="sm">
|
||||
<Select
|
||||
placeholder={t("Filter by event")}
|
||||
data={eventFilterOptions.map((group) => ({
|
||||
group: t(group.group),
|
||||
items: group.items.map((item) => ({
|
||||
value: item.value,
|
||||
label: t(item.label),
|
||||
})),
|
||||
}))}
|
||||
value={eventFilter}
|
||||
onChange={handleEventChange}
|
||||
clearable
|
||||
searchable
|
||||
w={220}
|
||||
size="sm"
|
||||
/>
|
||||
|
||||
<Popover
|
||||
position="bottom-end"
|
||||
shadow="md"
|
||||
width={260}
|
||||
withArrow
|
||||
opened={settingsOpen}
|
||||
onChange={(opened) => {
|
||||
if (!opened) resetRetentionForm();
|
||||
setSettingsOpen(opened);
|
||||
}}
|
||||
>
|
||||
<Popover.Target>
|
||||
<Tooltip label={t("Audit settings")}>
|
||||
<ActionIcon variant="default" size="input-sm" ml="auto" onClick={() => setSettingsOpen((o) => !o)}>
|
||||
<IconSettings size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
<Text fz="sm" fw={500} mb={4}>
|
||||
{t("Retention")}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed" mb="sm">
|
||||
{t("Logs older than this period are automatically deleted.")}
|
||||
</Text>
|
||||
<Group gap="xs" wrap="nowrap" mb="sm">
|
||||
<NumberInput
|
||||
value={retentionAmount}
|
||||
onChange={(val) => setRetentionAmount(val)}
|
||||
min={1}
|
||||
hideControls
|
||||
size="sm"
|
||||
w={60}
|
||||
/>
|
||||
<Select
|
||||
data={[
|
||||
{ value: "days", label: t("days") },
|
||||
{ value: "months", label: t("months") },
|
||||
{ value: "years", label: t("years") },
|
||||
]}
|
||||
value={retentionUnit}
|
||||
onChange={(value) => {
|
||||
if (value === "days" || value === "months" || value === "years") {
|
||||
setRetentionUnit(value);
|
||||
}
|
||||
}}
|
||||
size="sm"
|
||||
style={{ flex: 1 }}
|
||||
comboboxProps={{ withinPortal: false }}
|
||||
/>
|
||||
</Group>
|
||||
<Group gap="xs" grow>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
resetRetentionForm();
|
||||
setSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
{t("Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
const num = typeof retentionAmount === "number" ? retentionAmount : 1;
|
||||
const clamped = Math.max(1, num);
|
||||
setRetentionAmount(clamped);
|
||||
const days = retentionToDays(clamped, retentionUnit);
|
||||
if (days !== currentDays) {
|
||||
updateRetention.mutate({ auditRetentionDays: days });
|
||||
}
|
||||
setSettingsOpen(false);
|
||||
}}
|
||||
loading={updateRetention.isPending}
|
||||
>
|
||||
{t("Save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
</Group>
|
||||
|
||||
<AuditLogsTable items={data?.items} isLoading={isLoading} />
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
{data?.items && data.items.length > 0 && (
|
||||
<Paginate
|
||||
hasPrevPage={data?.meta?.hasPrevPage}
|
||||
hasNextPage={data?.meta?.hasNextPage}
|
||||
onNext={() => goNext(data?.meta?.nextCursor)}
|
||||
onPrev={goPrev}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
keepPreviousData,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
UseQueryResult,
|
||||
} from "@tanstack/react-query";
|
||||
import {
|
||||
getAuditLogs,
|
||||
getAuditRetention,
|
||||
updateAuditRetention,
|
||||
} from "@/ee/audit/services/audit-service";
|
||||
import { IAuditLog, IAuditLogParams } from "@/ee/audit/types/audit.types";
|
||||
import { IPagination } from "@/lib/types";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export function useAuditLogsQuery(
|
||||
params?: IAuditLogParams,
|
||||
): UseQueryResult<IPagination<IAuditLog>, Error> {
|
||||
return useQuery({
|
||||
queryKey: ["audit-logs", params],
|
||||
queryFn: () => getAuditLogs(params),
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAuditRetentionQuery() {
|
||||
return useQuery({
|
||||
queryKey: ["audit-retention"],
|
||||
queryFn: () => getAuditRetention(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateAuditRetentionMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: { auditRetentionDays: number }) =>
|
||||
updateAuditRetention(data),
|
||||
onSuccess: () => {
|
||||
notifications.show({ message: t("Audit retention updated") });
|
||||
queryClient.invalidateQueries({ queryKey: ["audit-retention"] });
|
||||
},
|
||||
onError: (error) => {
|
||||
const errorMessage = error["response"]?.data?.message;
|
||||
notifications.show({ message: errorMessage, color: "red" });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import api from "@/lib/api-client";
|
||||
import { IAuditLog, IAuditLogParams } from "@/ee/audit/types/audit.types";
|
||||
import { IPagination } from "@/lib/types";
|
||||
|
||||
export async function getAuditLogs(
|
||||
params?: IAuditLogParams,
|
||||
): Promise<IPagination<IAuditLog>> {
|
||||
const req = await api.post("/audit", { ...params });
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function getAuditRetention(): Promise<{ retentionDays: number }> {
|
||||
const req = await api.post("/audit/retention");
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function updateAuditRetention(data: {
|
||||
auditRetentionDays: number;
|
||||
}): Promise<{ retentionDays: number }> {
|
||||
const req = await api.post("/audit/retention/update", data);
|
||||
return req.data;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
export type IAuditLog = {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
actorId?: string;
|
||||
actorType: string;
|
||||
event: string;
|
||||
resourceType: string;
|
||||
resourceId?: string;
|
||||
spaceId?: string;
|
||||
changes?: {
|
||||
before?: Record<string, any>;
|
||||
after?: Record<string, any>;
|
||||
};
|
||||
metadata?: Record<string, any>;
|
||||
ipAddress?: string;
|
||||
createdAt: string;
|
||||
actor?: {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
avatarUrl?: string;
|
||||
};
|
||||
resource?: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug?: string;
|
||||
slugId?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type IAuditLogParams = {
|
||||
event?: string;
|
||||
resourceType?: string;
|
||||
actorId?: string;
|
||||
spaceId?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
};
|
||||
@@ -5,3 +5,15 @@ export async function getJoinedWorkspaces(): Promise<Partial<IWorkspace[]>> {
|
||||
const req = await api.post<Partial<IWorkspace[]>>("/workspace/joined");
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function findWorkspacesByEmail(email: string): Promise<void> {
|
||||
await api.post("/workspace/find-by-email", { email });
|
||||
}
|
||||
|
||||
export async function verifyEmail(data: { token: string }): Promise<void> {
|
||||
await api.post("/workspace/verify-email", data);
|
||||
}
|
||||
|
||||
export async function resendVerificationEmail(data: { email: string; sig: string }): Promise<void> {
|
||||
await api.post("/workspace/resend-verification", data);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
InfiniteData,
|
||||
} from "@tanstack/react-query";
|
||||
import { resolveComment } from "@/features/comment/services/comment-service";
|
||||
import {
|
||||
@@ -10,41 +11,54 @@ import {
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { IPagination } from "@/lib/types.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQueryEmit } from "@/features/websocket/use-query-emit";
|
||||
import { RQ_KEY } from "@/features/comment/queries/comment-query";
|
||||
|
||||
function updateCommentInCache(
|
||||
cache: InfiniteData<IPagination<IComment>>,
|
||||
commentId: string,
|
||||
updater: (comment: IComment) => IComment,
|
||||
): InfiniteData<IPagination<IComment>> {
|
||||
return {
|
||||
...cache,
|
||||
pages: cache.pages.map((page) => ({
|
||||
...page,
|
||||
items: page.items.map((comment) =>
|
||||
comment.id === commentId ? updater(comment) : comment,
|
||||
),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export function useResolveCommentMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslation();
|
||||
const emit = useQueryEmit();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: IResolveComment) => resolveComment(data),
|
||||
onMutate: async (variables) => {
|
||||
await queryClient.cancelQueries({ queryKey: RQ_KEY(variables.pageId) });
|
||||
const previousComments = queryClient.getQueryData(RQ_KEY(variables.pageId));
|
||||
queryClient.setQueryData(RQ_KEY(variables.pageId), (old: IPagination<IComment>) => {
|
||||
if (!old || !old.items) return old;
|
||||
const updatedItems = old.items.map((comment) =>
|
||||
comment.id === variables.commentId
|
||||
? {
|
||||
...comment,
|
||||
resolvedAt: variables.resolved ? new Date() : null,
|
||||
resolvedById: variables.resolved ? 'optimistic-user' : null,
|
||||
resolvedBy: variables.resolved ? { id: 'optimistic-user', name: 'Resolving...', avatarUrl: null } : null
|
||||
}
|
||||
: comment,
|
||||
const previousCache = queryClient.getQueryData(RQ_KEY(variables.pageId));
|
||||
|
||||
const cache = previousCache as InfiniteData<IPagination<IComment>> | undefined;
|
||||
if (cache) {
|
||||
queryClient.setQueryData(
|
||||
RQ_KEY(variables.pageId),
|
||||
updateCommentInCache(cache, variables.commentId, (comment) => ({
|
||||
...comment,
|
||||
resolvedAt: variables.resolved ? new Date() : null,
|
||||
resolvedById: variables.resolved ? "optimistic" : null,
|
||||
resolvedBy: variables.resolved
|
||||
? ({ id: "optimistic", name: "", avatarUrl: null } as IComment["resolvedBy"])
|
||||
: null,
|
||||
})),
|
||||
);
|
||||
return {
|
||||
...old,
|
||||
items: updatedItems,
|
||||
};
|
||||
});
|
||||
return { previousComments };
|
||||
}
|
||||
|
||||
return { previousCache };
|
||||
},
|
||||
onError: (err, variables, context) => {
|
||||
if (context?.previousComments) {
|
||||
queryClient.setQueryData(RQ_KEY(variables.pageId), context.previousComments);
|
||||
onError: (_err, variables, context) => {
|
||||
if (context?.previousCache) {
|
||||
queryClient.setQueryData(RQ_KEY(variables.pageId), context.previousCache);
|
||||
}
|
||||
notifications.show({
|
||||
message: t("Failed to resolve comment"),
|
||||
@@ -52,35 +66,26 @@ export function useResolveCommentMutation() {
|
||||
});
|
||||
},
|
||||
onSuccess: (data: IComment, variables) => {
|
||||
const pageId = data.pageId;
|
||||
const currentComments = queryClient.getQueryData(
|
||||
RQ_KEY(pageId),
|
||||
) as IPagination<IComment>;
|
||||
if (currentComments && currentComments.items) {
|
||||
const updatedComments = currentComments.items.map((comment) =>
|
||||
comment.id === variables.commentId
|
||||
? { ...comment, resolvedAt: data.resolvedAt, resolvedById: data.resolvedById, resolvedBy: data.resolvedBy }
|
||||
: comment,
|
||||
const cache = queryClient.getQueryData(
|
||||
RQ_KEY(data.pageId),
|
||||
) as InfiniteData<IPagination<IComment>> | undefined;
|
||||
|
||||
if (cache) {
|
||||
queryClient.setQueryData(
|
||||
RQ_KEY(data.pageId),
|
||||
updateCommentInCache(cache, variables.commentId, (comment) => ({
|
||||
...comment,
|
||||
resolvedAt: data.resolvedAt,
|
||||
resolvedById: data.resolvedById,
|
||||
resolvedBy: data.resolvedBy,
|
||||
})),
|
||||
);
|
||||
queryClient.setQueryData(RQ_KEY(pageId), {
|
||||
...currentComments,
|
||||
items: updatedComments,
|
||||
});
|
||||
}
|
||||
emit({
|
||||
operation: "resolveComment",
|
||||
pageId: pageId,
|
||||
commentId: variables.commentId,
|
||||
resolved: variables.resolved,
|
||||
resolvedAt: data.resolvedAt,
|
||||
resolvedById: data.resolvedById,
|
||||
resolvedBy: data.resolvedBy,
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: RQ_KEY(pageId) });
|
||||
notifications.show({
|
||||
message: variables.resolved
|
||||
? t("Comment resolved successfully")
|
||||
: t("Comment re-opened successfully")
|
||||
|
||||
notifications.show({
|
||||
message: variables.resolved
|
||||
? t("Comment resolved successfully")
|
||||
: t("Comment re-opened successfully"),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as z from "zod";
|
||||
import { useForm, zodResolver } from "@mantine/form";
|
||||
import { z } from "zod/v4";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { zod4Resolver } from "mantine-form-zod-resolver";
|
||||
import {
|
||||
Container,
|
||||
Title,
|
||||
@@ -19,23 +20,38 @@ import APP_ROUTE from "@/lib/app-route.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import JoinedWorkspaces from "@/ee/components/joined-workspaces.tsx";
|
||||
import { useJoinedWorkspacesQuery } from "@/ee/cloud/query/cloud-query.ts";
|
||||
import { findWorkspacesByEmail } from "@/ee/cloud/service/cloud-service.ts";
|
||||
import { AuthLayout } from "@/features/auth/components/auth-layout.tsx";
|
||||
|
||||
const formSchema = z.object({
|
||||
hostname: z.string().min(1, { message: "subdomain is required" }),
|
||||
});
|
||||
|
||||
const findWorkspaceSchema = z.object({
|
||||
email: z.string().email({ message: "Please enter a valid email" }),
|
||||
});
|
||||
|
||||
export function CloudLoginForm() {
|
||||
const { t } = useTranslation();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isFindLoading, setIsFindLoading] = useState<boolean>(false);
|
||||
const [findEmailSent, setFindEmailSent] = useState<boolean>(false);
|
||||
const { data: joinedWorkspaces } = useJoinedWorkspacesQuery();
|
||||
|
||||
const form = useForm<any>({
|
||||
validate: zodResolver(formSchema),
|
||||
validate: zod4Resolver(formSchema),
|
||||
initialValues: {
|
||||
hostname: "",
|
||||
},
|
||||
});
|
||||
|
||||
const findForm = useForm<any>({
|
||||
validate: zod4Resolver(findWorkspaceSchema),
|
||||
initialValues: {
|
||||
email: "",
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(data: { hostname: string }) {
|
||||
setIsLoading(true);
|
||||
|
||||
@@ -53,8 +69,21 @@ export function CloudLoginForm() {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
async function onFindSubmit(data: { email: string }) {
|
||||
setIsFindLoading(true);
|
||||
|
||||
try {
|
||||
await findWorkspacesByEmail(data.email);
|
||||
setFindEmailSent(true);
|
||||
} catch {
|
||||
findForm.setFieldError("email", "An error occurred. Please try again.");
|
||||
}
|
||||
|
||||
setIsFindLoading(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AuthLayout>
|
||||
<Container size={420} className={classes.container}>
|
||||
<Box p="xl" className={classes.containerBox}>
|
||||
<Title order={2} ta="center" fw={500} mb="md">
|
||||
@@ -82,15 +111,47 @@ export function CloudLoginForm() {
|
||||
{t("Continue")}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<Divider my="lg" label="or" labelPosition="center" />
|
||||
|
||||
{findEmailSent ? (
|
||||
<Text ta="center" size="sm" c="dimmed">
|
||||
{t("We've sent you an email with your associated workspaces.")}
|
||||
</Text>
|
||||
) : (
|
||||
<form onSubmit={findForm.onSubmit(onFindSubmit)}>
|
||||
<Text fw={600} mb="xs">
|
||||
{t("Find your workspaces")}
|
||||
</Text>
|
||||
<TextInput
|
||||
type="email"
|
||||
placeholder="name@company.com"
|
||||
description={t(
|
||||
"We'll send a list of your workspaces to this email.",
|
||||
)}
|
||||
withErrorStyles={false}
|
||||
{...findForm.getInputProps("email")}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
mt="md"
|
||||
variant="light"
|
||||
loading={isFindLoading}
|
||||
>
|
||||
{t("Send")}
|
||||
</Button>
|
||||
</form>
|
||||
)}
|
||||
</Box>
|
||||
</Container>
|
||||
|
||||
<Text ta="center">
|
||||
<Text ta="center" mb="xl">
|
||||
{t("Don't have a workspace?")}{" "}
|
||||
<Anchor component={Link} to={APP_ROUTE.AUTH.CREATE_WORKSPACE} fw={500}>
|
||||
{t("Create new workspace")}
|
||||
</Anchor>
|
||||
</Text>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useState } from "react";
|
||||
import { Modal, TextInput, PasswordInput, Button, Stack } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { zodResolver } from "mantine-form-zod-resolver";
|
||||
import { z } from "zod";
|
||||
import { zod4Resolver } from "mantine-form-zod-resolver";
|
||||
import { z } from "zod/v4";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IAuthProvider } from "@/ee/security/types/security.types";
|
||||
import APP_ROUTE from "@/lib/app-route";
|
||||
import APP_ROUTE, { getPostLoginRedirect } from "@/lib/app-route";
|
||||
import { ldapLogin } from "@/ee/security/services/ldap-auth-service";
|
||||
|
||||
const formSchema = z.object({
|
||||
@@ -34,7 +34,7 @@ export function LdapLoginModal({
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const form = useForm({
|
||||
validate: zodResolver(formSchema),
|
||||
validate: zod4Resolver(formSchema),
|
||||
initialValues: {
|
||||
username: "",
|
||||
password: "",
|
||||
@@ -59,13 +59,13 @@ export function LdapLoginModal({
|
||||
// Handle MFA like the regular login
|
||||
if (response?.userHasMfa) {
|
||||
onClose();
|
||||
navigate(APP_ROUTE.AUTH.MFA_CHALLENGE);
|
||||
navigate(APP_ROUTE.AUTH.MFA_CHALLENGE + window.location.search);
|
||||
} else if (response?.requiresMfaSetup) {
|
||||
onClose();
|
||||
navigate(APP_ROUTE.AUTH.MFA_SETUP_REQUIRED);
|
||||
navigate(APP_ROUTE.AUTH.MFA_SETUP_REQUIRED + window.location.search);
|
||||
} else {
|
||||
onClose();
|
||||
navigate(APP_ROUTE.HOME);
|
||||
navigate(getPostLoginRedirect());
|
||||
}
|
||||
} catch (err: any) {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { Button, Group, Text, Modal, TextInput } from "@mantine/core";
|
||||
import * as z from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { useState } from "react";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import * as React from "react";
|
||||
import { useForm, zodResolver } from "@mantine/form";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { zod4Resolver } from "mantine-form-zod-resolver";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getSubdomainHost } from "@/lib/config.ts";
|
||||
import { IWorkspace } from "@/features/workspace/types/workspace.types.ts";
|
||||
import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts";
|
||||
import { getHostnameUrl } from "@/ee/utils.ts";
|
||||
import { useAtom } from "jotai/index";
|
||||
import { useAtom } from "jotai";
|
||||
import {
|
||||
currentUserAtom,
|
||||
workspaceAtom,
|
||||
@@ -66,7 +67,7 @@ function ChangeHostnameForm({ onClose }: ChangeHostnameFormProps) {
|
||||
const [currentUser, setCurrentUser] = useAtom(currentUserAtom);
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
validate: zodResolver(formSchema),
|
||||
validate: zod4Resolver(formSchema),
|
||||
initialValues: {
|
||||
hostname: currentUser?.workspace?.hostname,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,6 @@ import { IAuthProvider } from "@/ee/security/types/security.types.ts";
|
||||
import { buildSsoLoginUrl } from "@/ee/security/sso.utils.ts";
|
||||
import { SSO_PROVIDER } from "@/ee/security/contants.ts";
|
||||
import { GoogleIcon } from "@/components/icons/google-icon.tsx";
|
||||
import { isCloud } from "@/lib/config.ts";
|
||||
import { LdapLoginModal } from "@/ee/components/ldap-login-modal.tsx";
|
||||
|
||||
export default function SsoLogin() {
|
||||
@@ -57,7 +56,7 @@ export default function SsoLogin() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{(isCloud() || data.hasLicenseKey) && (
|
||||
{data.authProviders.length > 0 && (
|
||||
<>
|
||||
<Stack align="stretch" justify="center" gap="sm">
|
||||
{data.authProviders.map((provider) => (
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import type { Entitlements } from "./entitlement.types";
|
||||
|
||||
export const entitlementAtom = atomWithStorage<Entitlements | null>(
|
||||
"entitlements",
|
||||
null,
|
||||
);
|
||||
@@ -0,0 +1,7 @@
|
||||
import api from "@/lib/api-client";
|
||||
import { Entitlements } from "./entitlement.types";
|
||||
|
||||
export async function getEntitlements(): Promise<Entitlements> {
|
||||
const req = await api.post<Entitlements>("/workspace/entitlements");
|
||||
return req.data as Entitlements;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export type Tier = "free" | "standard" | "business" | "enterprise";
|
||||
|
||||
export type Entitlements = {
|
||||
cloud: boolean;
|
||||
tier: Tier;
|
||||
features: string[];
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user