Compare commits

...

39 Commits

Author SHA1 Message Date
Anthony LC b893a29138 🔖(minor) release 3.7.0
Added:
- (api) add API route to fetch document content

Changed:
- 🔒️(backend) configure throttle on every viewsets
- ⬆️ Bump eslint to V9
- (frontend) improve accessibility:
  - fix major accessibility issues reported
  by wave and axe
  - unify tab focus style for better visual consistency
  - improve modal a11y: structure, labels, and title
  - improve accessibility of cdoc content with
  correct aria tags
  - unify tab focus style for better visual consistency
  - hide decorative icons, label menus, avoid
  accessible name
- ♻️(tilt) use helm dev-backend chart

Removed:
- 🔥(frontend) remove multi column drop cursor

Fixed:
- 🐛(frontend) fix callout emoji list
2025-09-12 14:21:13 +02:00
Anthony LC a812580d6c ♻️(frontend) add categories on top of the EmojiPicker
In a recent fix we had to remove the categories
from the EmojiPicker component due to a bug in the
underlying library. This commit reintroduces the
categories feature, placing them at the top of the
picker for improved user experience. The
categories help users quickly find emojis
by grouping them into relevant sections.

We set the default color as well to ensure
consistency across the emoji picker.
2025-09-12 14:21:13 +02:00
AntoLC 1062e38c92 🌐(i18n) update translated strings
Update translated files with new translations
2025-09-12 12:11:02 +02:00
renovate[bot] 62e122b05f ⬆️(dependencies) update js dependencies 2025-09-12 11:33:17 +02:00
Anthony LC 32bc2890e0 📌(dependencies) pin wrap-ansi to 9.0.2
By security we pin wrap-ansi to 9.0.2,
the 9.0.1 version being infected.
2025-09-12 10:32:40 +02:00
Anthony LC 3c3686dc7e 🔧(frontend) add meta information to package.json files
- Add missing repository, author, and license fields
- Add recommended packageManager
2025-09-12 10:20:01 +02:00
Anthony LC ab90611c36 🔥(frontend) remove multi column drop cursor
The drop cursor for multi column was causing
issues with the editor's usability.
This commit removes the custom drop cursor
implementation to enhance user experience.
2025-09-11 16:11:48 +02:00
Cyril f9c08cf5ec Revert "(frontend) add document visible in list and openable via enter key"
This reverts commit b619850b1420421f09f56aa8644a93e0fa698682.
2025-09-11 13:43:36 +02:00
Cyril 2155c2ff1f (frontend) add document visible in list and openable via enter key
the document now appears in the list and can be opened using the enter key

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-11 13:43:35 +02:00
Cyril ef08ba3a00 (frontend) hide decorative icons, label menus, avoid name duplicates
improves a11y by hiding decorative icons, labeling menus and deduping names

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-11 13:43:20 +02:00
Anthony LC 7a903041f8 🐛(frontend) fix callout emoji list
Removing explicit categories prop from
EmojiPicker to let emoji-mart manage categories
and avoid mismatch causing runtime error with
locales.
2025-09-11 11:54:52 +02:00
Anthony LC 4f2e07f949 🛂(frontend) limit input search to 254 characters
254 characters should be sufficient for most
of our usecases.
Limit input search to 254 characters to prevent
errors caused by overly long email addresses.
2025-09-10 16:11:16 +02:00
Anthony LC 8c1e95c587 (demo) change email from user to user.test in demo
When we create a new user in the demo environment,
the email address will now follow the format
user.test@example.com instead of user@example.com.
"user" was only 4 characters long, it created failing
tests in the e2e suite.
2025-09-10 16:11:16 +02:00
Manuel Raynaud 20161fd6db 🐛(backend) validate user search input data
Only the input data min length was checked. We also have to check the
mex length because the levenshtein dos not accept more than 254
characters and the email field has a max length of 254
2025-09-10 16:11:15 +02:00
dependabot[bot] e827cfeee1 Bump django from 5.2.4 to 5.2.6 in /src/backend (#1360)
⬆️(backend) bump django from 5.2.4 to 5.2.6
    
Bumps [django](https://github.com/django/django) from 5.2.4 to 5.2.6.
- [Commits](https://github.com/django/django/compare/5.2.4...5.2.6)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.6
  dependency-type: direct:production
...
    
Signed-off-by: dependabot[bot] <support@github.com>
2025-09-10 14:09:17 +00:00
Manuel Raynaud eab2a75bff ♻️(tilt) use hem dev-backend chart (#1340)
Remove usage of bitnami charts and use our own dev-backend charts
instead.
2025-09-10 11:43:30 +00:00
Cyril cd84751cb9 (frontend) fix major accessibility issues found by wave and axe
improves a11y by fixing multiple critical validation errors

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-10 10:26:47 +02:00
Anthony LC 1d20a8b0a7 💄(frontend) remove margin from modal title
Recent improvement changes the modal title with
a h1 tag, h1 tag adds margin by default.
We remove the margin from the h1 tag to stick to
the design system.
2025-09-10 09:35:54 +02:00
Cyril 8a310d004b (frontend) improve modal a11y: structure, labels, and title
added aria-label, structured text in p, and added title for better accessibility

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-10 08:10:35 +02:00
Cyril 9f9fae96e5 (frontend) unify tab focus style for better visual consistency
standardizes keyboard focus appearance to improve UI coherence

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-09 18:37:28 +02:00
Cyril 9cb2b6a6fb (frontend) improve accessibility of cdoc content with correct aria tags
added appropriate aria attributes and semantic tags to enhance accessibility

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-09 15:25:38 +02:00
Anthony LC 0a1eaa3c40 🚨(i18n) upgrade eslint to v9 with i18n package
We upgraded ESLint to version 9 in the i18n package,
which includes several improvements and fixes.
This change also involves updating the ESLint
configuration files to the new format and ensuring
compatibility with the latest ESLint features.
2025-09-09 12:27:32 +02:00
Anthony LC da72a1601a 🚨(y-provider) upgrade eslint to v9 with y-provider server
We upgraded ESLint to version 9 in the y-provider server,
which includes several improvements and fixes.
This change also involves updating the ESLint
configuration files to the new format and ensuring
compatibility with the latest ESLint features.
2025-09-09 12:27:32 +02:00
Anthony LC 9a51e02cd7 🚨(e2e) upgrade eslint to v9 with e2e app
We upgraded ESLint to version 9 in the e2e app,
which includes several improvements and fixes.
This change also involves updating the ESLint
configuration files to the new format and ensuring
compatibility with the latest ESLint features.
2025-09-09 12:27:31 +02:00
Anthony LC 4184c339eb 🚨(docs) upgrade eslint to v9 with Docs app
We upgraded ESLint to version 9 in the Docs app,
which includes several improvements and fixes.
This change also involves updating the ESLint
configuration files to the new format and ensuring
compatibility with the latest ESLint features.
2025-09-09 12:27:31 +02:00
Anthony LC 3688591dd1 ⬆️(dependency) upgrade eslint to v9
We upgraded ESLint to version 9 in the
eslint-config-impress package.
We rename it to eslint-plugin-docs.
2025-09-09 11:03:54 +02:00
Sylvain Zimmer 25783182b8 🗑️(convert) cleanup old content route
Remove rout /api/content, there is no more controller behind and is not
used anymore.
2025-09-08 14:25:10 +02:00
Sylvain Zimmer 80a62bcbc1 (convert) improve tests with stricter tests and less ipsum
Use real example data to run convert handler tests.
2025-09-08 14:24:11 +02:00
Sylvain Zimmer ede0a77665 ♻️(convert) reuse existing convert yprovider endpoint for content API
reuse convert service instead of renaming it in content
2025-09-08 14:23:42 +02:00
Sylvain Zimmer 8a8a1460e5 (api) add API route to fetch document content
This allows API users to process document content, enabling the
use of Docs as a headless CMS for instance, or any kind of document
processing. Fixes #1206.
2025-09-08 14:21:38 +02:00
Manuel Raynaud 0ac9f059b6 🔒️(backend) configure throttle on every viewsets
We want to configure the throttle on all doc's viewsets. In order to
monitor them, we use the MonitoredScopedRateThrottle class and a custom
callback caputing the message in sentry at the warning level.
2025-09-08 09:23:17 +02:00
Manuel Raynaud 179a84150b ⬆️(backend) upgrade django-lasuite to version 0.0.14
To use monitored throttling
2025-09-08 08:16:32 +02:00
Cyril 084d0c1089 (frontend) make delete buttons nvda-accessible
add aria-labels and include close button in title prop so NVDA announces actions

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-05 17:49:18 +02:00
Cyril c9a6c4d4c6 (frontend) improve placeholder contrast in blocknote for wcag
fixes insufficient contrast

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-09-05 16:27:33 +02:00
Quentin BEY 9db7d0af8d 🔒️(all) refactor Docker Hub login to use official GitHub actions
Replace custom Docker Hub authentication with standard, secure,
official GitHub actions for improved security and maintainability.

Uses officially supported actions that follow security best practices
and receive regular updates from GitHub.

Avoid unsecure handling of GitHub secrets.

Thanks to @lebaudantoine
2025-09-05 16:05:10 +02:00
Anthony LC 9135dff088 🔖(minor) release 3.6.0
Added:
- 👷(CI) add bundle size check job
- (frontend) use title first emoji as doc icon in tree

Changed:
- ♻️(docs-app) Switch from Jest tests to Vitest
- (frontend) improve accessibility:
  - 🌐(frontend) set html lang attribute dynamically
  - (frontend) inject language attribute to pdf export
  - (frontend) improve accessibility of search modal
  - (frontend) add correct attributes to decorative and interactive icons
  - 🎨(frontend) improve nav structure
  - ️(frontend) keyboard interaction with menu
  - (frontend) improve header accessibility
  - (frontend) improve accessibility for decorative images in editor
- ♻️(backend) fallback to email identifier when no name
- 🐛(backend) allow ASCII characters in user sub field
- ️(frontend) improve fallback width calculation

Fixed:
- 🐛(makefile) Windows compatibility fix for Docker volume mounting
- 🐛(minio) fix user permission error with Minio and Windows
- 🐛(frontend) fix export when quote block and inline code
- 🐛(frontend) fix base64 font
- 🐛(backend) allow editor to delete subpages
- 🐛(frontend) fix dnd conflict with tree and Blocknote
- 🐛(frontend) fix display bug on homepage
2025-09-05 09:11:58 +02:00
Anthony LC cc4c67d15b 🩹(frontend) add computed_link_reach on PUT link-configuration
By default a document is "restricted", a restricted
document cannot have a role "editor" or "reader".
With inheritance, a child document could have a computed
link reach different than "restricted" though.

We pass the computed link reach when we update the
link role, to be sure if follows the parent computed
link reach.
2025-09-05 09:11:58 +02:00
Anthony LC 63a2bde11e (e2e) fix e2e tests
After translating to french, a selector was not
accessible anymore because the aria label
was in english. We update the selector to use the
french aria label.
We increase as well the timeout of another test
that was flaky.
2025-09-04 16:15:08 +02:00
Anthony LC b317a2a596 🌐(i18n) update translated strings
Update translated files with new translations
2025-09-04 13:56:23 +02:00
166 changed files with 5120 additions and 2628 deletions
+8 -2
View File
@@ -32,7 +32,10 @@ jobs:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
@@ -65,7 +68,10 @@ jobs:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
+50 -15
View File
@@ -8,36 +8,69 @@ and this project adheres to
## [Unreleased]
## [3.7.0] - 2025-09-12
### Added
- ✨(api) add API route to fetch document content #1206
### Changed
- 🔒️(backend) configure throttle on every viewsets #1343
- ⬆️ Bump eslint to V9 #1071
- ♿(frontend) improve accessibility:
- ♿fix major accessibility issues reported by wave and axe #1344
- ✨unify tab focus style for better visual consistency #1341
- ✨improve modal a11y: structure, labels, and title #1349
- ✨improve accessibility of cdoc content with correct aria tags #1271
- ✨unify tab focus style for better visual consistency #1341
- ♿hide decorative icons, label menus, avoid accessible name… #1362
- ♻️(tilt) use helm dev-backend chart
### Removed
- 🔥(frontend) remove multi column drop cursor #1370
### Fixed
- 🐛(frontend) fix callout emoji list #1366
## [3.6.0] - 2025-09-04
### Added
- 👷(CI) add bundle size check job #1268
- ✨(frontend) use title first emoji as doc icon in tree
- ✨(frontend) use title first emoji as doc icon in tree #1289
### Changed
- ♻️(docs-app) Switch from Jest tests to Vitest #1269
- ⚡️(frontend) improve accessibility:
- #1248
- #1235
- #1275
- #1255
- #1262
- #1244
- #1270
- #1282
- (frontend) improve accessibility:
- 🌐(frontend) set html lang attribute dynamically #1248
- ♿(frontend) inject language attribute to pdf export #1235
- ♿(frontend) improve accessibility of search modal #1275
- ♿(frontend) add correct attributes to icons #1255
- 🎨(frontend) improve nav structure #1262
- ♿️(frontend) keyboard interaction with menu #1244
- ♿(frontend) improve header accessibility #1270
- ♿(frontend) improve accessibility for decorative images in editor #1282
- #1338
- #1281
- ♻️(backend) fallback to email identifier when no name #1298
- 🐛(backend) allow ASCII characters in user sub field #1295
- 🐛(backend) allow ASCII characters in user sub field #1295
- ⚡️(frontend) improve fallback width calculation #1333
### Fixed
- 🐛(makefile) Windows compatibility fix for Docker volume mounting #1264
- 🐛(minio) fix user permission error with Minio and Windows #1264
- 🐛(makefile) Windows compatibility fix for Docker volume mounting #1263
- 🐛(minio) fix user permission error with Minio and Windows #1263
- 🐛(frontend) fix export when quote block and inline code #1319
- 🐛(frontend) fix base64 font #1324
- 🐛(backend) allow editor to delete subpages #1296
- 🐛(backend) allow creator to delete subpages #1297
- 🐛(frontend) fix dnd conflict with tree and Blocknote #1328
- 🐛(frontend) fix display bug on homepage #1332
- 🐛link role update #1287
## [3.5.0] - 2025-07-31
@@ -709,7 +742,9 @@ and this project adheres to
- ✨(frontend) Coming Soon page (#67)
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/suitenumerique/docs/compare/v3.5.0...main
[unreleased]: https://github.com/suitenumerique/docs/compare/v3.7.0...main
[v3.7.0]: https://github.com/suitenumerique/docs/releases/v3.7.0
[v3.6.0]: https://github.com/suitenumerique/docs/releases/v3.6.0
[v3.5.0]: https://github.com/suitenumerique/docs/releases/v3.5.0
[v3.4.2]: https://github.com/suitenumerique/docs/releases/v3.4.2
[v3.4.1]: https://github.com/suitenumerique/docs/releases/v3.4.1
+1 -1
View File
@@ -440,6 +440,6 @@ bump-packages-version: ## bump the version of the project - VERSION_TYPE can be
cd ./src/frontend/apps/e2e/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/apps/impress/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/servers/y-provider/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/packages/eslint-config-impress/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/packages/eslint-plugin-docs/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/packages/i18n/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
.PHONY: bump-packages-version
+3 -2
View File
@@ -39,9 +39,10 @@ docker_build(
]
)
k8s_resource('impress-docs-backend-migrate', resource_deps=['postgres-postgresql'])
k8s_resource('impress-docs-backend-migrate', resource_deps=['dev-backend-postgres'])
k8s_resource('impress-docs-backend-createsuperuser', resource_deps=['impress-docs-backend-migrate'])
k8s_resource('impress-docs-backend', resource_deps=['impress-docs-backend-migrate'])
k8s_resource('dev-backend-keycloak', resource_deps=['dev-backend-keycloak-pg'])
k8s_resource('impress-docs-backend', resource_deps=['impress-docs-backend-migrate', 'dev-backend-redis', 'dev-backend-keycloak', 'dev-backend-postgres', 'dev-backend-minio:statefulset'])
k8s_yaml(local('cd ../src/helm && helmfile -n impress -e dev template .'))
migration = '''
+4
View File
@@ -3,3 +3,7 @@ BURST_THROTTLE_RATES="200/minute"
COLLABORATION_API_URL=http://y-provider:4444/collaboration/api/
SUSTAINED_THROTTLE_RATES="200/hour"
Y_PROVIDER_API_BASE_URL=http://y-provider:4444/api/
# Throttle
API_DOCUMENT_THROTTLE_RATE=1000/min
API_CONFIG_THROTTLE_RATE=1000/min
-1
View File
@@ -27,7 +27,6 @@
"@hocuspocus/provider",
"@hocuspocus/server",
"docx",
"eslint",
"fetch-mock",
"node",
"node-fetch",
+8
View File
@@ -128,3 +128,11 @@ class ListDocumentFilter(DocumentFilter):
queryset_method = queryset.filter if bool(value) else queryset.exclude
return queryset_method(link_traces__user=user, link_traces__is_masked=True)
class UserSearchFilter(django_filters.FilterSet):
"""
Custom filter for searching users.
"""
q = django_filters.CharFilter(min_length=5, max_length=254)
+21
View File
@@ -0,0 +1,21 @@
"""Throttling modules for the API."""
from rest_framework.throttling import UserRateThrottle
from sentry_sdk import capture_message
def sentry_monitoring_throttle_failure(message):
"""Log when a failure occurs to detect rate limiting issues."""
capture_message(message, "warning")
class UserListThrottleBurst(UserRateThrottle):
"""Throttle for the user list endpoint."""
scope = "user_list_burst"
class UserListThrottleSustained(UserRateThrottle):
"""Throttle for the user list endpoint."""
scope = "user_list_sustained"
+90 -16
View File
@@ -1,6 +1,7 @@
"""API endpoints"""
# pylint: disable=too-many-lines
import base64
import json
import logging
import uuid
@@ -33,16 +34,25 @@ from lasuite.malware_detection import malware_detection
from rest_framework import filters, status, viewsets
from rest_framework import response as drf_response
from rest_framework.permissions import AllowAny
from rest_framework.throttling import UserRateThrottle
from core import authentication, choices, enums, models
from core.services.ai_services import AIService
from core.services.collaboration_services import CollaborationService
from core.services.converter_services import (
ServiceUnavailableError as YProviderServiceUnavailableError,
)
from core.services.converter_services import (
ValidationError as YProviderValidationError,
)
from core.services.converter_services import (
YdocConverter,
)
from core.tasks.mail import send_ask_for_access_mail
from core.utils import extract_attachments, filter_descendants
from . import permissions, serializers, utils
from .filters import DocumentFilter, ListDocumentFilter
from .filters import DocumentFilter, ListDocumentFilter, UserSearchFilter
from .throttling import UserListThrottleBurst, UserListThrottleSustained
logger = logging.getLogger(__name__)
@@ -136,18 +146,6 @@ class Pagination(drf.pagination.PageNumberPagination):
page_size_query_param = "page_size"
class UserListThrottleBurst(UserRateThrottle):
"""Throttle for the user list endpoint."""
scope = "user_list_burst"
class UserListThrottleSustained(UserRateThrottle):
"""Throttle for the user list endpoint."""
scope = "user_list_sustained"
class UserViewSet(
drf.mixins.UpdateModelMixin, viewsets.GenericViewSet, drf.mixins.ListModelMixin
):
@@ -178,12 +176,18 @@ class UserViewSet(
if self.action != "list":
return queryset
filterset = UserSearchFilter(
self.request.GET, queryset=queryset, request=self.request
)
if not filterset.is_valid():
raise drf.exceptions.ValidationError(filterset.errors)
# Exclude all users already in the given document
if document_id := self.request.query_params.get("document_id", ""):
queryset = queryset.exclude(documentaccess__document_id=document_id)
if not (query := self.request.query_params.get("q", "")) or len(query) < 5:
return queryset.none()
filter_data = filterset.form.cleaned_data
query = filter_data["q"]
# For emails, match emails by Levenstein distance to prevent typing errors
if "@" in query:
@@ -360,6 +364,7 @@ class DocumentViewSet(
permission_classes = [
permissions.DocumentPermission,
]
throttle_scope = "document"
queryset = models.Document.objects.select_related("creator").all()
serializer_class = serializers.DocumentSerializer
ai_translate_serializer_class = serializers.AITranslateSerializer
@@ -1505,6 +1510,69 @@ class DocumentViewSet(
status=status.HTTP_400_BAD_REQUEST,
)
@drf.decorators.action(
detail=True,
methods=["get"],
url_path="content",
name="Get document content in different formats",
)
def content(self, request, pk=None):
"""
Retrieve document content in different formats (JSON, Markdown, HTML).
Query parameters:
- content_format: The desired output format (json, markdown, html)
Returns:
JSON response with content in the specified format.
"""
document = self.get_object()
content_format = request.query_params.get("content_format", "json").lower()
if content_format not in {"json", "markdown", "html"}:
raise drf.exceptions.ValidationError(
"Invalid format. Must be one of: json, markdown, html"
)
# Get the base64 content from the document
content = None
base64_content = document.content
if base64_content is not None:
# Convert using the y-provider service
try:
yprovider = YdocConverter()
result = yprovider.convert(
base64.b64decode(base64_content),
"application/vnd.yjs.doc",
{
"markdown": "text/markdown",
"html": "text/html",
"json": "application/json",
}[content_format],
)
content = result
except YProviderValidationError as e:
return drf_response.Response(
{"error": str(e)}, status=status.HTTP_400_BAD_REQUEST
)
except YProviderServiceUnavailableError as e:
logger.error("Error getting content for document %s: %s", pk, e)
return drf_response.Response(
{"error": "Failed to get document content"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
return drf_response.Response(
{
"id": str(document.id),
"title": document.title,
"content": content,
"created_at": document.created_at,
"updated_at": document.updated_at,
}
)
class DocumentAccessViewSet(
ResourceAccessViewsetMixin,
@@ -1555,6 +1623,7 @@ class DocumentAccessViewSet(
"document__depth",
)
resource_field_name = "document"
throttle_scope = "document_access"
@cached_property
def document(self):
@@ -1714,6 +1783,7 @@ class TemplateViewSet(
permissions.IsAuthenticatedOrSafe,
permissions.ResourceWithAccessPermission,
]
throttle_scope = "template"
ordering = ["-created_at"]
ordering_fields = ["created_at", "updated_at", "title"]
serializer_class = serializers.TemplateSerializer
@@ -1804,6 +1874,7 @@ class TemplateAccessViewSet(
lookup_field = "pk"
permission_classes = [permissions.ResourceAccessPermission]
throttle_scope = "template_access"
queryset = models.TemplateAccess.objects.select_related("user").all()
resource_field_name = "template"
serializer_class = serializers.TemplateAccessSerializer
@@ -1886,6 +1957,7 @@ class InvitationViewset(
permissions.CanCreateInvitationPermission,
permissions.ResourceWithAccessPermission,
]
throttle_scope = "invitation"
queryset = (
models.Invitation.objects.all()
.select_related("document")
@@ -1964,6 +2036,7 @@ class DocumentAskForAccessViewSet(
permissions.IsAuthenticated,
permissions.ResourceWithAccessPermission,
]
throttle_scope = "document_ask_for_access"
queryset = models.DocumentAskForAccess.objects.all()
serializer_class = serializers.DocumentAskForAccessSerializer
_document = None
@@ -2036,6 +2109,7 @@ class ConfigView(drf.views.APIView):
"""API ViewSet for sharing some public settings."""
permission_classes = [AllowAny]
throttle_scope = "config"
def get(self, request):
"""
+1
View File
@@ -783,6 +783,7 @@ class Document(MP_Node, BaseModel):
"children_list": can_get,
"children_create": can_create_children,
"collaboration_auth": can_get,
"content": can_get,
"cors_proxy": can_get,
"descendants": can_get,
"destroy": can_destroy,
+31 -12
View File
@@ -1,4 +1,4 @@
"""Converter services."""
"""Y-Provider API services."""
from base64 import b64encode
@@ -28,25 +28,44 @@ class YdocConverter:
# Note: Yprovider microservice accepts only raw token, which is not recommended
return f"Bearer {settings.Y_PROVIDER_API_KEY}"
def convert(self, text):
def _request(self, url, data, content_type, accept):
"""Make a request to the Y-Provider API."""
response = requests.post(
url,
data=data,
headers={
"Authorization": self.auth_header,
"Content-Type": content_type,
"Accept": accept,
},
timeout=settings.CONVERSION_API_TIMEOUT,
verify=settings.CONVERSION_API_SECURE,
)
response.raise_for_status()
return response
def convert(
self, text, content_type="text/markdown", accept="application/vnd.yjs.doc"
):
"""Convert a Markdown text into our internal format using an external microservice."""
if not text:
raise ValidationError("Input text cannot be empty")
try:
response = requests.post(
response = self._request(
f"{settings.Y_PROVIDER_API_BASE_URL}{settings.CONVERSION_API_ENDPOINT}/",
data=text,
headers={
"Authorization": self.auth_header,
"Content-Type": "text/markdown",
},
timeout=settings.CONVERSION_API_TIMEOUT,
verify=settings.CONVERSION_API_SECURE,
text,
content_type,
accept,
)
response.raise_for_status()
return b64encode(response.content).decode("utf-8")
if accept == "application/vnd.yjs.doc":
return b64encode(response.content).decode("utf-8")
if accept in {"text/markdown", "text/html"}:
return response.text
if accept == "application/json":
return response.json()
raise ValidationError("Unsupported format")
except requests.RequestException as err:
raise ServiceUnavailableError(
"Failed to connect to conversion service",
@@ -4,6 +4,7 @@ Test document accesses API endpoints for users in impress's core app.
# pylint: disable=too-many-lines
import random
from unittest import mock
from uuid import uuid4
import pytest
@@ -1344,3 +1345,24 @@ def test_api_document_accesses_delete_owners_last_owner_child_team(
assert response.status_code == 204
assert models.DocumentAccess.objects.count() == 1
def test_api_document_accesses_throttling(settings):
"""Test api document accesses throttling."""
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["document_access"] = "2/minute"
user = factories.UserFactory()
document = factories.DocumentFactory()
factories.UserDocumentAccessFactory(
document=document, user=user, role="administrator"
)
client = APIClient()
client.force_login(user)
for _i in range(2):
response = client.get(f"/api/v1.0/documents/{document.id!s}/accesses/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get(f"/api/v1.0/documents/{document.id!s}/accesses/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope document_access", "warning"
)
@@ -824,3 +824,29 @@ def test_api_document_invitations_delete_readers_or_editors(via, role, mock_user
response.json()["detail"]
== "You do not have permission to perform this action."
)
def test_api_document_invitations_throttling(settings):
"""Test api document ask for access throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["invitation"]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["invitation"] = "2/minute"
user = factories.UserFactory()
document = factories.DocumentFactory()
factories.UserDocumentAccessFactory(document=document, user=user, role="owner")
factories.InvitationFactory(document=document, issuer=user)
client = APIClient()
client.force_login(user)
for _i in range(2):
response = client.get(f"/api/v1.0/documents/{document.id}/invitations/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get(f"/api/v1.0/documents/{document.id}/invitations/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope invitation", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["invitation"] = current_rate
@@ -1,6 +1,7 @@
"""Test API for document ask for access."""
import uuid
from unittest import mock
from django.core import mail
@@ -768,3 +769,35 @@ def test_api_documents_ask_for_access_accept_authenticated_non_root_document(rol
f"/api/v1.0/documents/{child.id}/ask-for-access/{document_ask_for_access.id}/accept/"
)
assert response.status_code == 404
def test_api_document_ask_for_access_throttling(settings):
"""Test api document ask for access throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"][
"document_ask_for_access"
]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["document_ask_for_access"] = (
"2/minute"
)
document = DocumentFactory()
DocumentAskForAccessFactory.create_batch(
3, document=document, role=RoleChoices.READER
)
user = UserFactory()
client = APIClient()
client.force_login(user)
for _i in range(2):
response = client.get(f"/api/v1.0/documents/{document.id}/ask-for-access/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get(f"/api/v1.0/documents/{document.id}/ask-for-access/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope document_ask_for_access", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["document_ask_for_access"] = (
current_rate
)
@@ -0,0 +1,176 @@
"""
Tests for Documents API endpoint in impress's core app: content
"""
import base64
from unittest.mock import patch
import pytest
import requests
from rest_framework import status
from rest_framework.test import APIClient
from core import factories
pytestmark = pytest.mark.django_db
@pytest.mark.parametrize(
"reach, role",
[
("public", "reader"),
("public", "editor"),
],
)
@patch("core.services.converter_services.YdocConverter.convert")
def test_api_documents_content_public(mock_content, reach, role):
"""Anonymous users should be allowed to access content of public documents."""
document = factories.DocumentFactory(link_reach=reach, link_role=role)
mock_content.return_value = {"some": "data"}
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/content/")
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["id"] == str(document.id)
assert data["title"] == document.title
assert data["content"] == {"some": "data"}
mock_content.assert_called_once_with(
base64.b64decode(document.content),
"application/vnd.yjs.doc",
"application/json",
)
@pytest.mark.parametrize(
"reach, doc_role, user_role",
[
("restricted", "reader", "reader"),
("restricted", "reader", "editor"),
("restricted", "reader", "administrator"),
("restricted", "reader", "owner"),
("restricted", "editor", "reader"),
("restricted", "editor", "editor"),
("restricted", "editor", "administrator"),
("restricted", "editor", "owner"),
("authenticated", "reader", None),
("authenticated", "editor", None),
],
)
@patch("core.services.converter_services.YdocConverter.convert")
def test_api_documents_content_not_public(mock_content, reach, doc_role, user_role):
"""Authenticated users need access to get non-public document content."""
user = factories.UserFactory()
document = factories.DocumentFactory(link_reach=reach, link_role=doc_role)
mock_content.return_value = {"some": "data"}
# First anonymous request should fail
client = APIClient()
response = client.get(f"/api/v1.0/documents/{document.id!s}/content/")
assert response.status_code == status.HTTP_401_UNAUTHORIZED
mock_content.assert_not_called()
# Login and try again
client.force_login(user)
response = client.get(f"/api/v1.0/documents/{document.id!s}/content/")
# If restricted, we still should not have access
if user_role is not None:
assert response.status_code == status.HTTP_403_FORBIDDEN
mock_content.assert_not_called()
# Create an access as a reader. This should unlock the access.
factories.UserDocumentAccessFactory(
document=document, user=user, role=user_role
)
response = client.get(f"/api/v1.0/documents/{document.id!s}/content/")
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["id"] == str(document.id)
assert data["title"] == document.title
assert data["content"] == {"some": "data"}
mock_content.assert_called_once_with(
base64.b64decode(document.content),
"application/vnd.yjs.doc",
"application/json",
)
@pytest.mark.parametrize(
"content_format, accept",
[
("markdown", "text/markdown"),
("html", "text/html"),
("json", "application/json"),
],
)
@patch("core.services.converter_services.YdocConverter.convert")
def test_api_documents_content_format(mock_content, content_format, accept):
"""Test that the content endpoint returns a specific format."""
document = factories.DocumentFactory(link_reach="public")
mock_content.return_value = {"some": "data"}
response = APIClient().get(
f"/api/v1.0/documents/{document.id!s}/content/?content_format={content_format}"
)
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["id"] == str(document.id)
assert data["title"] == document.title
assert data["content"] == {"some": "data"}
mock_content.assert_called_once_with(
base64.b64decode(document.content), "application/vnd.yjs.doc", accept
)
@patch("core.services.converter_services.YdocConverter._request")
def test_api_documents_content_invalid_format(mock_request):
"""Test that the content endpoint rejects invalid formats."""
document = factories.DocumentFactory(link_reach="public")
response = APIClient().get(
f"/api/v1.0/documents/{document.id!s}/content/?content_format=invalid"
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
mock_request.assert_not_called()
@patch("core.services.converter_services.YdocConverter._request")
def test_api_documents_content_yservice_error(mock_request):
"""Test that service errors are handled properly."""
document = factories.DocumentFactory(link_reach="public")
mock_request.side_effect = requests.RequestException()
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/content/")
mock_request.assert_called_once()
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
@patch("core.services.converter_services.YdocConverter._request")
def test_api_documents_content_nonexistent_document(mock_request):
"""Test that accessing a nonexistent document returns 404."""
client = APIClient()
response = client.get(
"/api/v1.0/documents/00000000-0000-0000-0000-000000000000/content/"
)
assert response.status_code == status.HTTP_404_NOT_FOUND
mock_request.assert_not_called()
@patch("core.services.converter_services.YdocConverter._request")
def test_api_documents_content_empty_document(mock_request):
"""Test that accessing an empty document returns empty content."""
document = factories.DocumentFactory(link_reach="public", content="")
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/content/")
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["id"] == str(document.id)
assert data["title"] == document.title
assert data["content"] is None
mock_request.assert_not_called()
@@ -427,3 +427,20 @@ def test_api_documents_list_favorites_no_extra_queries(django_assert_num_queries
assert result["is_favorite"] is True
else:
assert result["is_favorite"] is False
def test_api_documents_list_throttling(settings):
"""Test api documents throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["document"]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["document"] = "2/minute"
client = APIClient()
for _i in range(2):
response = client.get("/api/v1.0/documents/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get("/api/v1.0/documents/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope document", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["document"] = current_rate
@@ -37,6 +37,7 @@ def test_api_documents_retrieve_anonymous_public_standalone():
"children_list": True,
"collaboration_auth": True,
"cors_proxy": True,
"content": True,
"descendants": True,
"destroy": False,
"duplicate": False,
@@ -113,6 +114,7 @@ def test_api_documents_retrieve_anonymous_public_parent():
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": False,
# Anonymous user can't favorite a document even with read access
@@ -218,6 +220,7 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": True,
"favorite": True,
@@ -300,6 +303,7 @@ def test_api_documents_retrieve_authenticated_public_or_authenticated_parent(rea
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": True,
"favorite": True,
@@ -494,6 +498,7 @@ def test_api_documents_retrieve_authenticated_related_parent():
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": access.role in ["administrator", "owner"],
"duplicate": True,
"favorite": True,
@@ -81,6 +81,7 @@ def test_api_documents_trashbin_format():
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": True,
"duplicate": True,
"favorite": True,
@@ -3,6 +3,7 @@ Test template accesses API endpoints for users in impress's core app.
"""
import random
from unittest import mock
from uuid import uuid4
import pytest
@@ -773,3 +774,26 @@ def test_api_template_accesses_delete_owners_last_owner(via, mock_user_teams):
assert response.status_code == 403
assert models.TemplateAccess.objects.count() == 2
def test_api_template_accesses_throttling(settings):
"""Test api template accesses throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template_access"]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template_access"] = "2/minute"
template = factories.TemplateFactory()
user = factories.UserFactory()
factories.UserTemplateAccessFactory(
template=template, user=user, role="administrator"
)
client = APIClient()
client.force_login(user)
for _i in range(2):
response = client.get(f"/api/v1.0/templates/{template.id!s}/accesses/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get(f"/api/v1.0/templates/{template.id!s}/accesses/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope template_access", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template_access"] = current_rate
@@ -218,3 +218,20 @@ def test_api_templates_list_order_param():
assert response_template_ids == templates_ids, (
"created_at values are not sorted from oldest to newest"
)
def test_api_template_throttling(settings):
"""Test api template throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template"]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template"] = "2/minute"
client = APIClient()
for _i in range(2):
response = client.get("/api/v1.0/templates/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get("/api/v1.0/templates/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope template", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template"] = current_rate
+18
View File
@@ -3,6 +3,7 @@ Test config API endpoints in the Impress core app.
"""
import json
from unittest.mock import patch
from django.test import override_settings
@@ -174,3 +175,20 @@ def test_api_config_with_original_theme_customization(is_authenticated, settings
theme_customization = json.load(f)
assert content["theme_customization"] == theme_customization
def test_api_config_throttling(settings):
"""Test api config throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["config"]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["config"] = "2/minute"
client = APIClient()
for _i in range(2):
response = client.get("/api/v1.0/config/")
assert response.status_code == 200
with patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get("/api/v1.0/config/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope config", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["config"] = current_rate
+27 -4
View File
@@ -194,18 +194,41 @@ def test_api_users_list_query_short_queries():
factories.UserFactory(email="john.lennon@example.com")
response = client.get("/api/v1.0/users/?q=jo")
assert response.status_code == 200
assert response.json() == []
assert response.status_code == 400
assert response.json() == {
"q": ["Ensure this value has at least 5 characters (it has 2)."]
}
response = client.get("/api/v1.0/users/?q=john")
assert response.status_code == 200
assert response.json() == []
assert response.status_code == 400
assert response.json() == {
"q": ["Ensure this value has at least 5 characters (it has 4)."]
}
response = client.get("/api/v1.0/users/?q=john.")
assert response.status_code == 200
assert len(response.json()) == 2
def test_api_users_list_query_long_queries():
"""
Queries longer than 255 characters should return an empty result set.
"""
user = factories.UserFactory(email="paul@example.com")
client = APIClient()
client.force_login(user)
factories.UserFactory(email="john.doe@example.com")
factories.UserFactory(email="john.lennon@example.com")
query = "a" * 244
response = client.get(f"/api/v1.0/users/?q={query}@example.com")
assert response.status_code == 400
assert response.json() == {
"q": ["Ensure this value has at most 254 characters (it has 256)."]
}
def test_api_users_list_query_inactive():
"""Inactive users should not be listed."""
user = factories.UserFactory()
@@ -161,6 +161,7 @@ def test_models_documents_get_abilities_forbidden(
"collaboration_auth": False,
"descendants": False,
"cors_proxy": False,
"content": False,
"destroy": False,
"duplicate": False,
"favorite": False,
@@ -224,6 +225,7 @@ def test_models_documents_get_abilities_reader(
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": is_authenticated,
"favorite": is_authenticated,
@@ -289,6 +291,7 @@ def test_models_documents_get_abilities_editor(
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": is_authenticated,
"favorite": is_authenticated,
@@ -343,6 +346,7 @@ def test_models_documents_get_abilities_owner(django_assert_num_queries):
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": True,
"duplicate": True,
"favorite": True,
@@ -394,6 +398,7 @@ def test_models_documents_get_abilities_administrator(django_assert_num_queries)
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": True,
"favorite": True,
@@ -448,6 +453,7 @@ def test_models_documents_get_abilities_editor_user(django_assert_num_queries):
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": True,
"favorite": True,
@@ -509,6 +515,7 @@ def test_models_documents_get_abilities_reader_user(
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": True,
"favorite": True,
@@ -568,6 +575,7 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"content": True,
"destroy": False,
"duplicate": True,
"favorite": True,
@@ -1,4 +1,4 @@
"""Test converter services."""
"""Test y-provider services."""
from base64 import b64decode
from unittest.mock import MagicMock, patch
@@ -84,6 +84,42 @@ def test_convert_full_integration(mock_post, settings):
headers={
"Authorization": "Bearer test-key",
"Content-Type": "text/markdown",
"Accept": "application/vnd.yjs.doc",
},
timeout=5,
verify=False,
)
@patch("requests.post")
def test_convert_full_integration_with_specific_headers(mock_post, settings):
"""Test successful conversion with specific content type and accept headers."""
settings.Y_PROVIDER_API_BASE_URL = "http://test.com/"
settings.Y_PROVIDER_API_KEY = "test-key"
settings.CONVERSION_API_ENDPOINT = "conversion-endpoint"
settings.CONVERSION_API_TIMEOUT = 5
settings.CONVERSION_API_SECURE = False
converter = YdocConverter()
expected_response = "# Test Document\n\nThis is test content."
mock_response = MagicMock()
mock_response.text = expected_response
mock_response.raise_for_status.return_value = None
mock_post.return_value = mock_response
result = converter.convert(
b"test_content", "application/vnd.yjs.doc", "text/markdown"
)
assert result == expected_response
mock_post.assert_called_once_with(
"http://test.com/conversion-endpoint/",
data=b"test_content",
headers={
"Authorization": "Bearer test-key",
"Content-Type": "application/vnd.yjs.doc",
"Accept": "text/markdown",
},
timeout=5,
verify=False,
+11 -3
View File
@@ -8,11 +8,19 @@ NB_OBJECTS = {
DEV_USERS = [
{"username": "impress", "email": "impress@impress.world", "language": "en-us"},
{"username": "user-e2e-webkit", "email": "user@webkit.test", "language": "en-us"},
{"username": "user-e2e-firefox", "email": "user@firefox.test", "language": "en-us"},
{
"username": "user-e2e-webkit",
"email": "user.test@webkit.test",
"language": "en-us",
},
{
"username": "user-e2e-firefox",
"email": "user.test@firefox.test",
"language": "en-us",
},
{
"username": "user-e2e-chromium",
"email": "user@chromium.test",
"email": "user.test@chromium.test",
"language": "en-us",
},
]
@@ -119,8 +119,8 @@ def create_demo(stdout):
first_name = random.choice(first_names)
queue.push(
models.User(
admin_email=f"user{i:d}@example.com",
email=f"user{i:d}@example.com",
admin_email=f"user.test{i:d}@example.com",
email=f"user.test{i:d}@example.com",
password="!",
is_superuser=False,
is_active=True,
@@ -33,9 +33,9 @@ def test_commands_create_demo():
# assert dev users have doc accesses
user = models.User.objects.get(email="impress@impress.world")
assert models.DocumentAccess.objects.filter(user=user).exists()
user = models.User.objects.get(email="user@webkit.test")
user = models.User.objects.get(email="user.test@webkit.test")
assert models.DocumentAccess.objects.filter(user=user).exists()
user = models.User.objects.get(email="user@firefox.test")
user = models.User.objects.get(email="user.test@firefox.test")
assert models.DocumentAccess.objects.filter(user=user).exists()
user = models.User.objects.get(email="user@chromium.test")
user = models.User.objects.get(email="user.test@chromium.test")
assert models.DocumentAccess.objects.filter(user=user).exists()
+41
View File
@@ -356,6 +356,9 @@ class Base(Configuration):
"PAGE_SIZE": 20,
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_THROTTLE_CLASSES": [
"lasuite.drf.throttling.MonitoredScopedRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"user_list_sustained": values.Value(
default="180/hour",
@@ -367,8 +370,46 @@ class Base(Configuration):
environ_name="API_USERS_LIST_THROTTLE_RATE_BURST",
environ_prefix=None,
),
"document": values.Value(
default="80/minute",
environ_name="API_DOCUMENT_THROTTLE_RATE",
environ_prefix=None,
),
"document_access": values.Value(
default="50/minute",
environ_name="API_DOCUMENT_ACCESS_THROTTLE_RATE",
environ_prefix=None,
),
"template": values.Value(
default="30/minute",
environ_name="API_TEMPLATE_THROTTLE_RATE",
environ_prefix=None,
),
"template_access": values.Value(
default="30/minute",
environ_name="API_TEMPLATE_ACCESS_THROTTLE_RATE",
environ_prefix=None,
),
"invitation": values.Value(
default="60/minute",
environ_name="API_INVITATION_THROTTLE_RATE",
environ_prefix=None,
),
"document_ask_for_access": values.Value(
default="30/minute",
environ_name="API_DOCUMENT_ASK_FOR_ACCESS_THROTTLE_RATE",
environ_prefix=None,
),
"config": values.Value(
default="30/minute",
environ_name="API_CONFIG_THROTTLE_RATE",
environ_prefix=None,
),
},
}
MONITORED_THROTTLE_FAILURE_CALLBACK = (
"core.api.throttling.sentry_monitoring_throttle_failure"
)
SPECTACULAR_SETTINGS = {
"TITLE": "Impress API",
+80 -84
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Breton\n"
"Language: br_FR\n"
@@ -44,33 +44,33 @@ msgstr "Me eo an aozer"
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
msgid "Masked"
msgstr ""
msgstr "Kuzhet"
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
msgid "Favorite"
msgstr "Sinedoù"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Ur restr nevez a zo bet krouet ganeoc'h!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "C'hwi zo bet disklaeriet perc'henn ur restr nevez:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Korf"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Doare korf"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Stumm"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "eilenn {title}"
@@ -129,272 +129,268 @@ msgstr "Kleiz"
msgid "Right"
msgstr "Dehoù"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "alc'hwez kentañ evit an enrollañ evel UIID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "krouet d'ar/al"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "deiziad hag eurvezh krouidigezh an enrolladenn"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "hizivaet d'ar/al"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "deiziad hag eurvezh m'eo bet hizivaet an enrolladenn"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "N'hon eus kavet implijer ebet gant an isstrollad-mañ met ar postel a zo liammet ouzh un implijer enrollet."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "Ebarzhit un isstrollad mat. An talvoud-mañ a c'hall enderc'hel lizhiri, sifroù hag arouezioù @/./+/-/_/: hepken."
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "isstrollad"
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Rekis. 255 arouezenn pe nebeutoc'h. Lizhiri, sifroù hag arouezioù @/./+/-/_/: hepken."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "anv klok"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "anv berr"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "postel identelezh"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "postel ar merour"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "yezh"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "Ar yezh en deus c'hoant da welet an implijer an etrefas enni."
msgstr "Ar yezh a vo implijet evit etrefas an implijer."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Ar gwerzhid-eur en deus c'hoant da welet an implijer an eur drezañ."
msgstr "Ar gwerzhid-eur a vo implijet evit etrefas an implijer."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "trevnad"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Pe vefe an implijer un aparailh pe un implijer gwirion."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "statud ar skipailh"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Ma c'hall an implijer kevreañ ouzh al lec'hienn verañ-mañ."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "oberiant"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Ma rank bezañ tretet an implijer-mañ evel oberiant. Diziuzit an dra-mañ e-plas dilemel kontoù."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "implijer"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "implijerien"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "titl"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "bomm"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Restr"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Restroù"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Restr hep titl"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} en deus rannet ur restr ganeoc'h!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} en deus pedet ac'hanoc'h gant ar rol \"{role}\" war ar restr da-heul:"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} en deus rannet ur restr ganeoc'h: {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Roud liamm ar restr/an implijer"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Roudoù liamm ar restr/an implijer"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Ur roud liamm a zo dija evit an restr/an implijer."
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Restr muiañ-karet"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Restroù muiañ-karet"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Ar restr-mañ a zo ur restr muiañ karet gant an implijer-mañ."
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Liamm restr/implijer"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Liammoù restr/implijer"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "An implijer-mañ a zo dija er restr-mañ."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Ar skipailh-mañ a zo dija en restr-mañ."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "An implijer pe ar skipailh a rank bezañ termenet, ket an daou avat."
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr "Goulenn tizhout ar restr"
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr "Goulennoù tizhout ar restr"
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr "An implijer en deus goulennet tizhout ar restr-mañ."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} en defe c'hoant da dizhout ar restr-mañ!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} en defe c'hoant da dizhout ar restr da-heul:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} en defe c'hoant da dizhout ar restr: {title}"
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "deskrivadur"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "kod"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "publik"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "M'eo foran ar patrom-mañ hag implijus gant n'eus forzh piv."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Patrom"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Patromoù"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Liamm patrom/implijer"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Liammoù patrom/implijer"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "An implijer-mañ a zo dija er patrom-mañ."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Ar skipailh-mañ a zo dija er patrom-mañ."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "postel"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Pedadenn d'ur restr"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Pedadennoù d'ur restr"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Ar postel-mañ a zo liammet ouzh un implijer enskrivet."
+77 -81
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Favorit"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Ein neues Dokument wurde in Ihrem Namen erstellt!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Sie sind Besitzer eines neuen Dokuments:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Inhalt"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Typ"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Format"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "Kopie von {title}"
@@ -129,272 +129,268 @@ msgstr "Links"
msgid "Right"
msgstr "Rechts"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "primärer Schlüssel für den Datensatz als UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "Erstellt"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "Datum und Uhrzeit, an dem ein Datensatz erstellt wurde"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "Aktualisiert"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "Datum und Uhrzeit, an dem zuletzt aktualisiert wurde"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "Wir konnten keinen Benutzer mit diesem Abo finden, aber die E-Mail-Adresse ist bereits einem registrierten Benutzer zugeordnet."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "Geben Sie eine gültige Unterseite ein. Dieser Wert darf nur Buchstaben, Zahlen und die @/./+/-/_/: Zeichen enthalten."
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "unter"
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Erforderlich. 255 Zeichen oder weniger. Buchstaben, Zahlen und die Zeichen @/./+/-/_/:"
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "Name"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "Kurzbezeichnung"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "Identitäts-E-Mail-Adresse"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "Admin E-Mail-Adresse"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "Sprache"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "Die Sprache, in der der Benutzer die Benutzeroberfläche sehen möchte."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Die Zeitzone, in der der Nutzer Zeiten sehen möchte."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "Gerät"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Ob der Benutzer ein Gerät oder ein echter Benutzer ist."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "Status des Teammitgliedes"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Gibt an, ob der Benutzer sich in diese Admin-Seite einloggen kann."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "aktiviert"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Ob dieser Benutzer als aktiviert behandelt werden soll. Deaktivieren Sie diese Option, anstatt Konten zu löschen."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "Benutzer"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "Benutzer"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "Titel"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "Auszug"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Dokument"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Dokumente"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Unbenanntes Dokument"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} hat ein Dokument mit Ihnen geteilt!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} hat Sie mit der Rolle \"{role}\" zu folgendem Dokument eingeladen:"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} hat ein Dokument mit Ihnen geteilt: {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Dokument/Benutzer Linkverfolgung"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Dokument/Benutzer Linkverfolgung"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Für dieses Dokument/ diesen Benutzer ist bereits eine Linkverfolgung vorhanden."
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Dokumentenfavorit"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Dokumentfavoriten"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Dieses Dokument ist bereits durch den gleichen Benutzer favorisiert worden."
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Dokument/Benutzerbeziehung"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Dokument/Benutzerbeziehungen"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Dieser Benutzer befindet sich bereits in diesem Dokument."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Dieses Team befindet sich bereits in diesem Dokument."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "Benutzer oder Team müssen gesetzt werden, nicht beides."
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "Beschreibung"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "Code"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "CSS"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "öffentlich"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Ob diese Vorlage für jedermann öffentlich ist."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Vorlage"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Vorlagen"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Vorlage/Benutzer-Beziehung"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Vorlage/Benutzerbeziehungen"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Dieser Benutzer ist bereits in dieser Vorlage."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Dieses Team ist bereits in diesem Template."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "E-Mail-Adresse"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Einladung zum Dokument"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Dokumenteinladungen"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Diese E-Mail ist bereits einem registrierten Benutzer zugeordnet."
+76 -80
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: English\n"
"Language: en_US\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr ""
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr ""
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr ""
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr ""
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -129,272 +129,268 @@ msgstr ""
msgid "Right"
msgstr ""
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr ""
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr ""
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr ""
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr ""
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr ""
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr ""
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr ""
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr ""
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr ""
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr ""
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr ""
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr ""
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr ""
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr ""
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr ""
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr ""
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr ""
+81 -85
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Favorito"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "¡Un nuevo documento se ha creado por ti!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Se le ha concedido la propiedad de un nuevo documento :"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Cuerpo"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Tipo de Cuerpo"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Formato"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "copia de {title}"
@@ -129,272 +129,268 @@ msgstr "Izquierda"
msgid "Right"
msgstr "Derecha"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "clave primaria para el registro como UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "creado el"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "fecha y hora en la que se creó un registro"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "actualizado el"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "fecha y hora en la que un registro fue actualizado por última vez"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "No se ha podido encontrar un usuario con este sub (UUID), pero el correo electrónico ya está asociado con un usuario."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "Introduzca un sub (UUID) válido. Este valor solo puede contener letras, números y los siguientes caracteres @/./+/-/_/:"
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "sub (UUID)"
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Requerido. 255 caracteres o menos. Letras, números y los siguientes caracteres @/./+/-/_/: solamente."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr "Obligatorio. 255 caracteres o menos. Solo caracteres ASCII."
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "nombre completo"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "nombre abreviado"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "correo electrónico de identidad"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "correo electrónico del administrador"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "idioma"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "El idioma en el que el usuario desea ver la interfaz."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "La zona horaria en la que el usuario quiere ver los tiempos."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "dispositivo"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Si el usuario es un dispositivo o un usuario real."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "rol en el equipo"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Si el usuario puede iniciar sesión en esta página web de administración."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "activo"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Si este usuario debe ser considerado como activo. Deseleccionar en lugar de eliminar cuentas."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "usuario"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "usuarios"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "título"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "resumen"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Documento"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Documentos"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Documento sin título"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "¡{name} ha compartido un documento contigo!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "Te ha invitado {name} al siguiente documento con el rol \"{role}\" :"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} ha compartido un documento contigo: {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Traza del enlace de documento/usuario"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Trazas del enlace de documento/usuario"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Ya existe una traza de enlace para este documento/usuario."
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Documento favorito"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Documentos favoritos"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Este documento ya ha sido marcado como favorito por el usuario."
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Relación documento/usuario"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Relaciones documento/usuario"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Este usuario ya forma parte del documento."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Este equipo ya forma parte del documento."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "Debe establecerse un usuario o un equipo, no ambos."
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
msgstr "Solicitud de acceso"
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
msgstr "Solicitud de accesos"
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr "Este usuario ya ha solicitado acceso a este documento."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "¡{name} desea acceder a un documento!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
msgstr "{name} desea acceso al siguiente documento:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
msgstr "{name} está pidiendo acceso al documento: {title}"
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "descripción"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "código"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "público"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Si esta plantilla es pública para que cualquiera la utilice."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Plantilla"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Plantillas"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Relación plantilla/usuario"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Relaciones plantilla/usuario"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Este usuario ya forma parte de la plantilla."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Este equipo ya se encuentra en esta plantilla."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "dirección de correo electrónico"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Invitación al documento"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Invitaciones a documentos"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Este correo electrónico está asociado a un usuario registrado."
+78 -82
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -44,33 +44,33 @@ msgstr "Je suis l'auteur"
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
msgid "Masked"
msgstr ""
msgstr "Masqué"
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
msgid "Favorite"
msgstr "Favoris"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Un nouveau document a été créé pour vous !"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Vous avez été déclaré propriétaire d'un nouveau document :"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Corps"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Type de corps"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Format"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "copie de {title}"
@@ -129,272 +129,268 @@ msgstr "Gauche"
msgid "Right"
msgstr "Droite"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "identifiant/id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "clé primaire pour l'enregistrement en tant que UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "créé le"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "date et heure de création de l'enregistrement"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "mis à jour le"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "date et heure de la dernière mise à jour de l'enregistrement"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "Nous n'avons pas pu trouver un utilisateur avec ce sous-groupe mais l'e-mail est déjà associé à un utilisateur enregistré."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "Saisissez un sous-groupe valide. Cette valeur ne peut contenir que des lettres, des chiffres et les caractères @/./+/-/_/: uniquement."
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "sous-groupe"
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Obligatoire. 255 caractères ou moins. Lettres, chiffres et caractères @/./+/-/_/: uniquement."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr "Obligatoire. 255 caractères ou moins. Caractères ASCII uniquement."
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "nom complet"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "nom court"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "adresse e-mail d'identité"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "adresse e-mail de l'administrateur"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "langue"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "La langue dans laquelle l'utilisateur veut voir l'interface."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "appareil"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Si l'utilisateur est un appareil ou un utilisateur réel."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "statut d'équipe"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Si l'utilisateur peut se connecter à ce site d'administration."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "actif"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Si cet utilisateur doit être traité comme actif. Désélectionnez ceci au lieu de supprimer des comptes."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "utilisateur"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "utilisateurs"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "titre"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "extrait"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Document"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Documents"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Document sans titre"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} a partagé un document avec vous!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} vous a invité avec le rôle \"{role}\" sur le document suivant :"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} a partagé un document avec vous : {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Trace du lien document/utilisateur"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Traces du lien document/utilisateur"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Une trace de lien existe déjà pour ce document/utilisateur."
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Document favori"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Documents favoris"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Ce document est déjà un favori de cet utilisateur."
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Relation document/utilisateur"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Relations document/utilisateur"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Cet utilisateur est déjà dans ce document."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Cette équipe est déjà dans ce document."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "L'utilisateur ou l'équipe doivent être définis, pas les deux."
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr "Demande d'accès au document"
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr "Demande d'accès au document"
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr "Cet utilisateur a déjà demandé l'accès à ce document."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} souhaiterait accéder au document suivant !"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} souhaiterait accéder au document suivant :"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} demande l'accès au document : {title}"
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "description"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "code"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "CSS"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "public"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Si ce modèle est public, utilisable par n'importe qui."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Modèle"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Modèles"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Relation modèle/utilisateur"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Relations modèle/utilisateur"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Cet utilisateur est déjà dans ce modèle."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Cette équipe est déjà modèle."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "adresse e-mail"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Invitation à un document"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Invitations à un document"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Cette adresse email est déjà associée à un utilisateur inscrit."
+77 -81
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Preferiti"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Un nuovo documento è stato creato a tuo nome!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Sei ora proprietario di un nuovo documento:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Corpo"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Formato"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "copia di {title}"
@@ -129,272 +129,268 @@ msgstr "Sinistra"
msgid "Right"
msgstr "Destra"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "Id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "chiave primaria per il record come UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "creato il"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "data e ora in cui è stato creato un record"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "aggiornato il"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "data e ora in cui lultimo record è stato aggiornato"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr ""
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Richiesto. 255 caratteri o meno. Solo lettere, numeri e @/./+/-/_/: caratteri."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "nome completo"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "nome"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "indirizzo email di identità"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "Indirizzo email dell'amministratore"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "lingua"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "La lingua in cui l'utente vuole vedere l'interfaccia."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Il fuso orario in cui l'utente vuole vedere gli orari."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "dispositivo"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Se l'utente è un dispositivo o un utente reale."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "stato del personale"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Indica se l'utente può accedere a questo sito amministratore."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "attivo"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Indica se questo utente deve essere trattato come attivo. Deseleziona invece di eliminare gli account."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "utente"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "utenti"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "titolo"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Documento"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Documenti"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Documento senza titolo"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} ha condiviso un documento con te!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} ti ha invitato con il ruolo \"{role}\" nel seguente documento:"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} ha condiviso un documento con te: {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Documento preferito"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Documenti preferiti"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Questo utente è già presente in questo documento."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Questo team è già presente in questo documento."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "descrizione"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "code"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "pubblico"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Indica se questo modello è pubblico per chiunque."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Modello"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Modelli"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Questo utente è già in questo modello."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Questo team è già in questo modello."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "indirizzo e-mail"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Invito al documento"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Inviti al documento"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Questa email è già associata a un utente registrato."
+77 -81
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Favoriete"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Een nieuw document was gecreëerd voor u!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "U heeft eigenaarschap van een nieuw document:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Text"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Text type"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Formaat"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "kopie van {title}"
@@ -129,272 +129,268 @@ msgstr "Links"
msgid "Right"
msgstr "Rechts"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "primaire sleutel voor dossier als UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "gemaakt op"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "datum en tijd wanneer dossier was gecreëerd"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "Laatst gewijzigd op"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "datum en tijd waarop dossier laatst was gewijzigd"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "Wij konden geen gebruiker vinden met deze id, maar de email is al geassocieerd met een geregistreerde gebruiker."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ".Geef een valide id. De waarde mag alleen letters, nummers en @/./.+/-/_: karakters bevatten."
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "id"
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Verplicht. 255 karakters of minder. Alleen letters, nummers en @/./+/-/_/: karakters zijn toegestaan."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "volledige naam"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "gebruikersnaam"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "identiteit email adres"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "admin email adres"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "taal"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "De taal waarin de gebruiker de interface wilt zien."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "De tijdzone waarin de gebruiker de tijden wilt zien."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "apparaat"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Of de gebruiker een apparaat is of een echte gebruiker."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "beheerder status"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Of de gebruiker kan inloggen in het admin gedeelte."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "actief"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Of een gebruiker als actief moet worden beschouwd. Deselecteer dit in plaats van het account te deleten."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "gebruiker"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "gebruikers"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "titel"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "uittreksel"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Document"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Documenten"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Naamloos Document"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} heeft een document met gedeeld!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} heeft u uitgenodigd met de rol \"{role}\" op het volgende document:"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} heeft een document met u gedeeld: {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Document/gebruiker url"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Document/gebruiker url"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Een url bestaat al voor dit document/deze gebruiker."
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Document favoriet"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Document favorieten"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Dit document is al in gebruik als favoriete door dezelfde gebruiker."
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Document/gebruiker relatie"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Document/gebruiker relaties"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "De gebruiker is al in dit document."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Het team is al in dit document."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "Een gebruiker of team moet gekozen worden, maar niet beide."
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "omschrijving"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "code"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "publiek"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Of dit template als publiek is en door iedereen te gebruiken is."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Template"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Templates"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Template/gebruiker relatie"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Template/gebruiker relaties"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "De gebruiker bestaat al in dit template."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Het team bestaat al in dit template."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "email adres"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Document uitnodiging"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Document uitnodigingen"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Deze email is al geassocieerd met een geregistreerde gebruiker."
+76 -80
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Language: pt_PT\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Favorito"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Um novo documento foi criado em seu nome!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "A propriedade de um novo documento foi concedida a você:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Corpo"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Tipo de corpo"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Formato"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "cópia de {title}"
@@ -129,272 +129,268 @@ msgstr ""
msgid "Right"
msgstr ""
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr ""
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr ""
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr ""
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr ""
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr ""
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr ""
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr ""
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr ""
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr ""
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr ""
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr ""
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr ""
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr ""
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr ""
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr ""
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr ""
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr ""
@@ -0,0 +1,417 @@
msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
"X-Crowdin-Project: lasuite-docs\n"
"X-Crowdin-Project-ID: 754523\n"
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: backend-impress.pot\n"
"X-Crowdin-File-ID: 18\n"
#: build/lib/core/admin.py:37 core/admin.py:37
msgid "Personal info"
msgstr "Личная информация"
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
#: core/admin.py:138
msgid "Permissions"
msgstr "Разрешения"
#: build/lib/core/admin.py:62 core/admin.py:62
msgid "Important dates"
msgstr "Важные даты"
#: build/lib/core/admin.py:148 core/admin.py:148
msgid "Tree structure"
msgstr "Древовидная структура"
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
msgid "Title"
msgstr "Заголовок"
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
msgid "Creator is me"
msgstr "Создатель - я"
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
msgid "Masked"
msgstr "Скрытый"
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
msgid "Favorite"
msgstr "Избранное"
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Новый документ был создан от вашего имени!"
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Вы назначены владельцем для нового документа:"
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Текст сообщения"
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Тип сообщения"
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Формат"
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "копия {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Читатель"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Редактор"
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Администратор"
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Владелец"
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Доступ ограничен"
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Аутентификация выполнена"
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Доступно всем"
#: build/lib/core/enums.py:36 core/enums.py:36
msgid "First child"
msgstr "Первый потомок"
#: build/lib/core/enums.py:37 core/enums.py:37
msgid "Last child"
msgstr "Последний потомок"
#: build/lib/core/enums.py:38 core/enums.py:38
msgid "First sibling"
msgstr "Первый предок"
#: build/lib/core/enums.py:39 core/enums.py:39
msgid "Last sibling"
msgstr "Последний предок"
#: build/lib/core/enums.py:40 core/enums.py:40
msgid "Left"
msgstr "Слева"
#: build/lib/core/enums.py:41 core/enums.py:41
msgid "Right"
msgstr "Справа"
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "первичный ключ для записи как UUID"
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "создано"
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "дата и время создания записи"
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "обновлено"
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "дата и время последнего обновления записи"
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "Мы не смогли найти пользователя с этими данными, но этот адрес уже связан с зарегистрированным пользователем."
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "вложение"
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr "Обязательно. 255 символов или меньше. Только ASCII символы."
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "полное имя"
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "короткое имя"
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "личный адрес электронной почты"
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "e-mail администратора"
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "язык"
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "Язык, на котором пользователь хочет видеть интерфейс."
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Часовой пояс, в котором пользователь хочет видеть время."
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "устройство"
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Пользователь является устройством или человеком."
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "статус сотрудника"
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Может ли пользователь войти на этот административный сайт."
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "активный"
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Должен ли пользователь рассматриваться как активный. Альтернатива удалению учётных записей."
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "пользователь"
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "пользователи"
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "заголовок"
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "отрывок"
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Документ"
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Документы"
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Безымянный документ"
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} делится с вами документом!"
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} приглашает вас присоединиться к следующему документу с ролью \"{role}\":"
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} делится с вами документом: {title}"
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Трассировка связи документ/пользователь"
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Трассировка связей документ/пользователь"
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Для этого документа/пользователя уже существует трассировка ссылки."
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Избранный документ"
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Избранные документы"
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Этот документ уже помечен как избранный для этого пользователя."
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Отношение документ/пользователь"
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Отношения документ/пользователь"
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Этот пользователь уже имеет доступ к этому документу."
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Эта команда уже имеет доступ к этому документу."
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "Может быть выбран либо пользователь, либо команда, но не оба варианта сразу."
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr "Документ запрашивает доступ"
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr "Документ запрашивает доступы"
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr "Этот пользователь уже запросил доступ к этому документу."
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} хочет получить доступ к документу!"
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} хочет получить доступ к следующему документу:"
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} запрашивает доступ к документу: {title}"
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "описание"
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "код"
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "доступно всем"
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Этот шаблон доступен всем пользователям."
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Шаблон"
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Шаблоны"
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Отношение шаблон/пользователь"
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Отношения шаблон/пользователь"
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Этот пользователь уже указан в этом шаблоне."
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Эта команда уже указана в этом шаблоне."
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "адрес электронной почты"
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Приглашение для документа"
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Приглашения для документов"
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Этот адрес уже связан с зарегистрированным пользователем."
#: core/templates/mail/html/template.html:162
#: core/templates/mail/text/template.txt:3
msgid "Logo email"
msgstr "Логотип email"
#: core/templates/mail/html/template.html:209
#: core/templates/mail/text/template.txt:10
msgid "Open"
msgstr "Открыть"
#: core/templates/mail/html/template.html:226
#: core/templates/mail/text/template.txt:14
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
msgstr " Docs, ваш новый инструмент для организации и совместного использования документов в вашей команде. "
#: core/templates/mail/html/template.html:233
#: core/templates/mail/text/template.txt:16
#, python-format
msgid " Brought to you by %(brandname)s "
msgstr " Доступ получен от %(brandname)s "
+77 -81
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Slovenian\n"
"Language: sl_SI\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Priljubljena"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Nov dokument je bil ustvarjen v vašem imenu!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Dodeljeno vam je bilo lastništvo nad novim dokumentom:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Telo"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Vrsta telesa"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Oblika"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -129,272 +129,268 @@ msgstr "Levo"
msgid "Right"
msgstr "Desno"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr ""
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "primarni ključ za zapis kot UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "ustvarjen na"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "datum in čas, ko je bil zapis ustvarjen"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "posodobljeno dne"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "datum in čas, ko je bil zapis nazadnje posodobljen"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "Nismo mogli najti uporabnika s tem sub, vendar je e-poštni naslov že povezan z registriranim uporabnikom."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "Vnesite veljavno sub. Ta vrednost lahko vsebuje samo črke, številke in znake @/./+/-/_/:."
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr ""
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Obvezno. 255 znakov ali manj. Samo črke, številke in znaki @/./+/-/_/: ."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "polno ime"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "kratko ime"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "elektronski naslov identitete"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "elektronski naslov skrbnika"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "jezik"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "Jezik, v katerem uporabnik želi videti vmesnik."
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Časovni pas, v katerem želi uporabnik videti uro."
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "naprava"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Ali je uporabnik naprava ali pravi uporabnik."
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "kadrovski status"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Ali se uporabnik lahko prijavi na to skrbniško mesto."
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "aktivni"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Ali je treba tega uporabnika obravnavati kot aktivnega. Namesto brisanja računov počistite to izbiro."
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "uporabnik"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "uporabniki"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "naslov"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "odlomek"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Dokument"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Dokumenti"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Dokument brez naslova"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} je delil dokument z vami!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} vas je povabil z vlogo \"{role}\" na naslednjem dokumentu:"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} je delil dokument z vami: {title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Dokument/sled povezave uporabnika"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Sledi povezav dokumenta/uporabnika"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Za ta dokument/uporabnika že obstaja sled povezave."
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Priljubljeni dokument"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Priljubljeni dokumenti"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Ta dokument je že ciljno usmerjen s priljubljenim primerkom relacije za istega uporabnika."
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Odnos dokument/uporabnik"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Odnosi dokument/uporabnik"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Ta uporabnik je že v tem dokumentu."
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Ta ekipa je že v tem dokumentu."
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "Nastaviti je treba bodisi uporabnika ali ekipo, a ne obojega."
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "opis"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "koda"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "javno"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Ali je ta predloga javna za uporabo."
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Predloga"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Predloge"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Odnos predloga/uporabnik"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Odnosi med predlogo in uporabnikom"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Ta uporabnik je že v tej predlogi."
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Ta ekipa je že v tej predlogi."
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "elektronski naslov"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Vabilo na dokument"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Vabila na dokument"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Ta e-poštni naslov je že povezan z registriranim uporabnikom."
+76 -80
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Swedish\n"
"Language: sv_SE\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "Favoriter"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Ett nytt dokument skapades åt dig!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Du har beviljats äganderätt till ett nytt dokument:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Format"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -129,272 +129,268 @@ msgstr ""
msgid "Right"
msgstr ""
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr ""
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr ""
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr ""
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr ""
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr ""
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr ""
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr ""
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "aktiv"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr ""
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr ""
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr ""
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr ""
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr ""
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr ""
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr ""
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr ""
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "e-postadress"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Bjud in dokument"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Inbjudningar dokument"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Denna e-postadress är redan associerad med en registrerad användare."
+76 -80
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr ""
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr ""
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr ""
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr ""
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -129,272 +129,268 @@ msgstr ""
msgid "Right"
msgstr ""
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr ""
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr ""
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr ""
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr ""
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr ""
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr ""
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr ""
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr ""
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr ""
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr ""
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr ""
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr ""
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr ""
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr ""
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr ""
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr ""
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr ""
@@ -0,0 +1,417 @@
msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
"X-Crowdin-Project: lasuite-docs\n"
"X-Crowdin-Project-ID: 754523\n"
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: backend-impress.pot\n"
"X-Crowdin-File-ID: 18\n"
#: build/lib/core/admin.py:37 core/admin.py:37
msgid "Personal info"
msgstr "Особисті дані"
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
#: core/admin.py:138
msgid "Permissions"
msgstr "Дозволи"
#: build/lib/core/admin.py:62 core/admin.py:62
msgid "Important dates"
msgstr "Важливі дати"
#: build/lib/core/admin.py:148 core/admin.py:148
msgid "Tree structure"
msgstr "Ієрархічна структура"
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
msgid "Title"
msgstr "Заголовок"
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
msgid "Creator is me"
msgstr "Творець — я"
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
msgid "Masked"
msgstr "Приховано"
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
msgid "Favorite"
msgstr "Обране"
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "Новий документ був створений від вашого імені!"
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "Ви тепер є власником нового документа:"
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "Вміст"
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "Тип вмісту"
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "Формат"
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "копія {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Читач"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Редактор"
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Адміністратор"
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Власник"
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Обмежено"
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Підтверджено"
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Публічне"
#: build/lib/core/enums.py:36 core/enums.py:36
msgid "First child"
msgstr "Перший нащадок"
#: build/lib/core/enums.py:37 core/enums.py:37
msgid "Last child"
msgstr "Останній нащадок"
#: build/lib/core/enums.py:38 core/enums.py:38
msgid "First sibling"
msgstr "Перший пращур"
#: build/lib/core/enums.py:39 core/enums.py:39
msgid "Last sibling"
msgstr "Останній пращур"
#: build/lib/core/enums.py:40 core/enums.py:40
msgid "Left"
msgstr "Ліворуч"
#: build/lib/core/enums.py:41 core/enums.py:41
msgid "Right"
msgstr "Праворуч"
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "первинний ключ для запису як UUID"
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "створено"
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "дата і час, коли запис було створено"
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "оновлено"
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "дата і час, коли запис був востаннє оновлений"
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "Ми не змогли знайти користувача з цими даними, але адреса вже пов'язана з зареєстрованим користувачем."
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "вкладений документ"
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr "Обов'язкове. 255 символів або менше. Тільки символи ASCII."
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "повне ім'я"
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "коротке ім'я"
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "адреса електронної пошти особи"
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "електронна адреса адміністратора"
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "мова"
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "Мова, якою користувач хоче бачити інтерфейс."
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "Часовий пояс, в якому користувач хоче бачити час."
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "пристрій"
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "Чи є користувач пристроєм чи реальним користувачем."
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "статус співробітника"
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "Чи може користувач увійти на цей сайт адміністратора."
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "активний"
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Чи слід ставитися до цього користувача як до активного. Зніміть вибір замість видалення облікового запису."
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "користувач"
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "користувачі"
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "заголовок"
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "уривок"
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "Документ"
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "Документи"
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "Документ без назви"
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} ділиться з вами документом!"
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} запрошує вас для роботи з документом із роллю \"{role}\":"
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} ділиться з вами документом: {title}"
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "Трасування посилання Документ/користувач"
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "Трасування посилань Документ/користувач"
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "Відстеження вже існуючих посилань для цього документа/користувача."
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "Обраний документ"
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "Обрані документи"
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Цей документ вже вказаний як обраний для одного користувача."
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "Відносини документ/користувач"
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "Відносини документ/користувач"
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "Цей користувач вже має доступ до цього документу."
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "Ця команда вже має доступ до цього документа."
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "Вкажіть користувача або команду, а не обох."
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr "Запит доступу до документа"
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr "Запит доступу для документа"
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr "Цей користувач вже попросив доступ до цього документа."
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} хоче отримати доступ до документа!"
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} бажає отримати доступ до наступного документа:"
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} запитує доступ до документа: {title}"
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "опис"
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "код"
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "публічне"
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "Чи є цей шаблон публічним для будь-кого користувача."
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "Шаблон"
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "Шаблони"
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "Відношення шаблон/користувач"
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "Відношення шаблон/користувач"
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "Цей користувач вже має доступ до цього шаблону."
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "Ця команда вже має доступ до цього шаблону."
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "електронна адреса"
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "Запрошення до редагування документа"
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "Запрошення до редагування документів"
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "Ця електронна пошта вже пов'язана з зареєстрованим користувачем."
#: core/templates/mail/html/template.html:162
#: core/templates/mail/text/template.txt:3
msgid "Logo email"
msgstr "Логотип пошти"
#: core/templates/mail/html/template.html:209
#: core/templates/mail/text/template.txt:10
msgid "Open"
msgstr "Відкрити"
#: core/templates/mail/html/template.html:226
#: core/templates/mail/text/template.txt:14
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
msgstr " Docs, ваш новий важливий інструмент для організації, обміну та командної співпраці над вашими документами. "
#: core/templates/mail/html/template.html:233
#: core/templates/mail/text/template.txt:16
#, python-format
msgid " Brought to you by %(brandname)s "
msgstr " Запрошення отримане від %(brandname)s "
+77 -81
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 20:42+0000\n"
"PO-Revision-Date: 2025-07-31 12:38\n"
"POT-Creation-Date: 2025-09-10 14:29+0000\n"
"PO-Revision-Date: 2025-09-12 09:50\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -50,27 +50,27 @@ msgstr ""
msgid "Favorite"
msgstr "收藏"
#: build/lib/core/api/serializers.py:467 core/api/serializers.py:467
#: build/lib/core/api/serializers.py:487 core/api/serializers.py:487
msgid "A new document was created on your behalf!"
msgstr "已为您创建了一份新文档!"
#: build/lib/core/api/serializers.py:471 core/api/serializers.py:471
#: build/lib/core/api/serializers.py:491 core/api/serializers.py:491
msgid "You have been granted ownership of a new document:"
msgstr "您已被授予新文档的所有权:"
#: build/lib/core/api/serializers.py:608 core/api/serializers.py:608
#: build/lib/core/api/serializers.py:628 core/api/serializers.py:628
msgid "Body"
msgstr "正文"
#: build/lib/core/api/serializers.py:611 core/api/serializers.py:611
#: build/lib/core/api/serializers.py:631 core/api/serializers.py:631
msgid "Body type"
msgstr "正文类型"
#: build/lib/core/api/serializers.py:617 core/api/serializers.py:617
#: build/lib/core/api/serializers.py:637 core/api/serializers.py:637
msgid "Format"
msgstr "格式"
#: build/lib/core/api/viewsets.py:942 core/api/viewsets.py:942
#: build/lib/core/api/viewsets.py:965 core/api/viewsets.py:965
#, python-brace-format
msgid "copy of {title}"
msgstr "{title} 的副本"
@@ -129,272 +129,268 @@ msgstr "左"
msgid "Right"
msgstr "右"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:80 core/models.py:80
msgid "id"
msgstr "id"
#: build/lib/core/models.py:80 core/models.py:80
#: build/lib/core/models.py:81 core/models.py:81
msgid "primary key for the record as UUID"
msgstr "记录的主密钥为 UUID"
#: build/lib/core/models.py:86 core/models.py:86
#: build/lib/core/models.py:87 core/models.py:87
msgid "created on"
msgstr "创建时间"
#: build/lib/core/models.py:87 core/models.py:87
#: build/lib/core/models.py:88 core/models.py:88
msgid "date and time at which a record was created"
msgstr "记录的创建日期和时间"
#: build/lib/core/models.py:92 core/models.py:92
#: build/lib/core/models.py:93 core/models.py:93
msgid "updated on"
msgstr "更新时间"
#: build/lib/core/models.py:93 core/models.py:93
#: build/lib/core/models.py:94 core/models.py:94
msgid "date and time at which a record was last updated"
msgstr "记录的最后更新时间"
#: build/lib/core/models.py:129 core/models.py:129
#: build/lib/core/models.py:130 core/models.py:130
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr "未找到具有该 sub 的用户,但该邮箱已关联到一个注册用户。"
#: build/lib/core/models.py:142 core/models.py:142
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "请输入有效的 sub。该值只能包含字母、数字及 @/./+/-/_/: 字符。"
#: build/lib/core/models.py:148 core/models.py:148
#: build/lib/core/models.py:141 core/models.py:141
msgid "sub"
msgstr "sub"
#: build/lib/core/models.py:150 core/models.py:150
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "必填。最多 255 个字符,仅允许字母、数字及 @/./+/-/_/: 字符。"
#: build/lib/core/models.py:142 core/models.py:142
msgid "Required. 255 characters or fewer. ASCII characters only."
msgstr ""
#: build/lib/core/models.py:159 core/models.py:159
#: build/lib/core/models.py:150 core/models.py:150
msgid "full name"
msgstr "全名"
#: build/lib/core/models.py:160 core/models.py:160
#: build/lib/core/models.py:151 core/models.py:151
msgid "short name"
msgstr "简称"
#: build/lib/core/models.py:162 core/models.py:162
#: build/lib/core/models.py:153 core/models.py:153
msgid "identity email address"
msgstr "身份电子邮件地址"
#: build/lib/core/models.py:167 core/models.py:167
#: build/lib/core/models.py:158 core/models.py:158
msgid "admin email address"
msgstr "管理员电子邮件地址"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:165 core/models.py:165
msgid "language"
msgstr "语言"
#: build/lib/core/models.py:175 core/models.py:175
#: build/lib/core/models.py:166 core/models.py:166
msgid "The language in which the user wants to see the interface."
msgstr "用户希望看到的界面语言。"
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:174 core/models.py:174
msgid "The timezone in which the user wants to see times."
msgstr "用户查看时间希望的时区。"
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:177 core/models.py:177
msgid "device"
msgstr "设备"
#: build/lib/core/models.py:188 core/models.py:188
#: build/lib/core/models.py:179 core/models.py:179
msgid "Whether the user is a device or a real user."
msgstr "用户是设备还是真实用户。"
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:182 core/models.py:182
msgid "staff status"
msgstr "员工状态"
#: build/lib/core/models.py:193 core/models.py:193
#: build/lib/core/models.py:184 core/models.py:184
msgid "Whether the user can log into this admin site."
msgstr "用户是否可以登录该管理员站点。"
#: build/lib/core/models.py:196 core/models.py:196
#: build/lib/core/models.py:187 core/models.py:187
msgid "active"
msgstr "激活"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:190 core/models.py:190
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "是否应将此用户视为活跃用户。取消选择此选项而不是删除账户。"
#: build/lib/core/models.py:211 core/models.py:211
#: build/lib/core/models.py:202 core/models.py:202
msgid "user"
msgstr "用户"
#: build/lib/core/models.py:212 core/models.py:212
#: build/lib/core/models.py:203 core/models.py:203
msgid "users"
msgstr "个用户"
#: build/lib/core/models.py:368 build/lib/core/models.py:1283
#: core/models.py:368 core/models.py:1283
#: build/lib/core/models.py:359 build/lib/core/models.py:1281
#: core/models.py:359 core/models.py:1281
msgid "title"
msgstr "标题"
#: build/lib/core/models.py:369 core/models.py:369
#: build/lib/core/models.py:360 core/models.py:360
msgid "excerpt"
msgstr "摘要"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:409 core/models.py:409
msgid "Document"
msgstr "文档"
#: build/lib/core/models.py:419 core/models.py:419
#: build/lib/core/models.py:410 core/models.py:410
msgid "Documents"
msgstr "个文档"
#: build/lib/core/models.py:431 build/lib/core/models.py:821 core/models.py:431
#: core/models.py:821
#: build/lib/core/models.py:422 build/lib/core/models.py:819 core/models.py:422
#: core/models.py:819
msgid "Untitled Document"
msgstr "未命名文档"
#: build/lib/core/models.py:856 core/models.py:856
#: build/lib/core/models.py:854 core/models.py:854
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} 与您共享了一个文档!"
#: build/lib/core/models.py:860 core/models.py:860
#: build/lib/core/models.py:858 core/models.py:858
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} 邀请您以“{role}”角色访问以下文档:"
#: build/lib/core/models.py:866 core/models.py:866
#: build/lib/core/models.py:864 core/models.py:864
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} 与您共享了一个文档:{title}"
#: build/lib/core/models.py:966 core/models.py:966
#: build/lib/core/models.py:964 core/models.py:964
msgid "Document/user link trace"
msgstr "文档/用户链接跟踪"
#: build/lib/core/models.py:967 core/models.py:967
#: build/lib/core/models.py:965 core/models.py:965
msgid "Document/user link traces"
msgstr "个文档/用户链接跟踪"
#: build/lib/core/models.py:973 core/models.py:973
#: build/lib/core/models.py:971 core/models.py:971
msgid "A link trace already exists for this document/user."
msgstr "此文档/用户的链接跟踪已存在。"
#: build/lib/core/models.py:996 core/models.py:996
#: build/lib/core/models.py:994 core/models.py:994
msgid "Document favorite"
msgstr "文档收藏"
#: build/lib/core/models.py:997 core/models.py:997
#: build/lib/core/models.py:995 core/models.py:995
msgid "Document favorites"
msgstr "文档收藏夹"
#: build/lib/core/models.py:1003 core/models.py:1003
#: build/lib/core/models.py:1001 core/models.py:1001
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "该文档已被同一用户的收藏关系实例关联。"
#: build/lib/core/models.py:1025 core/models.py:1025
#: build/lib/core/models.py:1023 core/models.py:1023
msgid "Document/user relation"
msgstr "文档/用户关系"
#: build/lib/core/models.py:1026 core/models.py:1026
#: build/lib/core/models.py:1024 core/models.py:1024
msgid "Document/user relations"
msgstr "文档/用户关系集"
#: build/lib/core/models.py:1032 core/models.py:1032
#: build/lib/core/models.py:1030 core/models.py:1030
msgid "This user is already in this document."
msgstr "该用户已在此文档中。"
#: build/lib/core/models.py:1038 core/models.py:1038
#: build/lib/core/models.py:1036 core/models.py:1036
msgid "This team is already in this document."
msgstr "该团队已在此文档中。"
#: build/lib/core/models.py:1044 build/lib/core/models.py:1369
#: core/models.py:1044 core/models.py:1369
#: build/lib/core/models.py:1042 build/lib/core/models.py:1367
#: core/models.py:1042 core/models.py:1367
msgid "Either user or team must be set, not both."
msgstr "必须设置用户或团队之一,不能同时设置两者。"
#: build/lib/core/models.py:1190 core/models.py:1190
#: build/lib/core/models.py:1188 core/models.py:1188
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1191 core/models.py:1191
#: build/lib/core/models.py:1189 core/models.py:1189
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1197 core/models.py:1197
#: build/lib/core/models.py:1195 core/models.py:1195
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1260 core/models.py:1260
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1264 core/models.py:1264
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1270 core/models.py:1270
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1284 core/models.py:1284
#: build/lib/core/models.py:1282 core/models.py:1282
msgid "description"
msgstr "说明"
#: build/lib/core/models.py:1285 core/models.py:1285
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "code"
msgstr "代码"
#: build/lib/core/models.py:1286 core/models.py:1286
#: build/lib/core/models.py:1284 core/models.py:1284
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1288 core/models.py:1288
#: build/lib/core/models.py:1286 core/models.py:1286
msgid "public"
msgstr "公开"
#: build/lib/core/models.py:1290 core/models.py:1290
#: build/lib/core/models.py:1288 core/models.py:1288
msgid "Whether this template is public for anyone to use."
msgstr "该模板是否公开供任何人使用。"
#: build/lib/core/models.py:1296 core/models.py:1296
#: build/lib/core/models.py:1294 core/models.py:1294
msgid "Template"
msgstr "模板"
#: build/lib/core/models.py:1297 core/models.py:1297
#: build/lib/core/models.py:1295 core/models.py:1295
msgid "Templates"
msgstr "模板"
#: build/lib/core/models.py:1350 core/models.py:1350
#: build/lib/core/models.py:1348 core/models.py:1348
msgid "Template/user relation"
msgstr "模板/用户关系"
#: build/lib/core/models.py:1351 core/models.py:1351
#: build/lib/core/models.py:1349 core/models.py:1349
msgid "Template/user relations"
msgstr "模板/用户关系集"
#: build/lib/core/models.py:1357 core/models.py:1357
#: build/lib/core/models.py:1355 core/models.py:1355
msgid "This user is already in this template."
msgstr "该用户已在此模板中。"
#: build/lib/core/models.py:1363 core/models.py:1363
#: build/lib/core/models.py:1361 core/models.py:1361
msgid "This team is already in this template."
msgstr "该团队已在此模板中。"
#: build/lib/core/models.py:1440 core/models.py:1440
#: build/lib/core/models.py:1438 core/models.py:1438
msgid "email address"
msgstr "电子邮件地址"
#: build/lib/core/models.py:1459 core/models.py:1459
#: build/lib/core/models.py:1457 core/models.py:1457
msgid "Document invitation"
msgstr "文档邀请"
#: build/lib/core/models.py:1460 core/models.py:1460
#: build/lib/core/models.py:1458 core/models.py:1458
msgid "Document invitations"
msgstr "文档邀请"
#: build/lib/core/models.py:1480 core/models.py:1480
#: build/lib/core/models.py:1478 core/models.py:1478
msgid "This email is already associated to a registered user."
msgstr "此电子邮件已经与现有注册用户关联。"
+3 -3
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "3.5.0"
version = "3.7.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -34,12 +34,12 @@ dependencies = [
"django-countries==7.6.1",
"django-csp==4.0",
"django-filter==25.1",
"django-lasuite[all]==0.0.11",
"django-lasuite[all]==0.0.14",
"django-parler==2.3",
"django-redis==6.0.0",
"django-storages[s3]==1.14.6",
"django-timezone-field>=5.1",
"django==5.2.4",
"django==5.2.6",
"django-treebeard==4.7.1",
"djangorestframework==3.16.0",
"drf_spectacular==0.28.0",
+2 -2
View File
@@ -10,13 +10,13 @@ WORKDIR /home/frontend/
COPY ./src/frontend/package.json ./package.json
COPY ./src/frontend/yarn.lock ./yarn.lock
COPY ./src/frontend/apps/impress/package.json ./apps/impress/package.json
COPY ./src/frontend/packages/eslint-config-impress/package.json ./packages/eslint-config-impress/package.json
COPY ./src/frontend/packages/eslint-plugin-docs/package.json ./packages/eslint-plugin-docs/package.json
RUN yarn install --frozen-lockfile
COPY .dockerignore ./.dockerignore
COPY ./src/frontend/.prettierrc.js ./.prettierrc.js
COPY ./src/frontend/packages/eslint-config-impress ./packages/eslint-config-impress
COPY ./src/frontend/packages/eslint-plugin-docs ./packages/eslint-plugin-docs
COPY ./src/frontend/apps/impress ./apps/impress
### ---- Front-end builder image ----
-9
View File
@@ -1,9 +0,0 @@
module.exports = {
root: true,
extends: ['impress/playwright'],
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
ignorePatterns: ['node_modules'],
};
@@ -5,14 +5,19 @@ import { keyCloakSignIn } from './utils-common';
const saveStorageState = async (
browserConfig: FullProject<unknown, unknown>,
) => {
const browserName = browserConfig?.name || 'chromium';
if (!browserConfig) {
throw new Error('No browser config found');
}
const { storageState, ...useConfig } = browserConfig?.use;
const browserName = browserConfig.name || 'chromium';
const { storageState, ...useConfig } = browserConfig.use;
const browser = await chromium.launch();
const context = await browser.newContext(useConfig);
const page = await context.newPage();
try {
// eslint-disable-next-line playwright/no-networkidle
await page.goto('/', { waitUntil: 'networkidle' });
await page.content();
await expect(page.getByText('Docs').first()).toBeVisible();
@@ -45,11 +50,9 @@ const saveStorageState = async (
};
async function globalSetup(config: FullConfig) {
/* eslint-disable @typescript-eslint/no-non-null-assertion */
const chromeConfig = config.projects.find((p) => p.name === 'chromium')!;
const firefoxConfig = config.projects.find((p) => p.name === 'firefox')!;
const webkitConfig = config.projects.find((p) => p.name === 'webkit')!;
/* eslint-enable @typescript-eslint/no-non-null-assertion */
await saveStorageState(chromeConfig);
await saveStorageState(webkitConfig);
@@ -50,7 +50,7 @@ test.describe('Config', () => {
await expect(image).toBeVisible();
// Wait for the media-check to be processed
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1000);
// Check src of image
@@ -45,8 +45,8 @@ test.describe('Doc Create', () => {
})
.click();
const input = page.getByRole('textbox', { name: 'doc title input' });
await expect(input).toHaveText('');
const input = page.getByRole('textbox', { name: 'Document title' });
await expect(input).toHaveText('', { timeout: 10000 });
await expect(
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
@@ -67,8 +67,8 @@ test.describe('Doc Create', () => {
.getByText('New sub-doc')
.click();
const input = page.getByRole('textbox', { name: 'doc title input' });
await expect(input).toHaveText('');
const input = page.getByRole('textbox', { name: 'Document title' });
await expect(input).toHaveText('', { timeout: 10000 });
await expect(
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
@@ -1,3 +1,4 @@
/* eslint-disable playwright/no-conditional-expect */
import path from 'path';
import { chromium, expect, test } from '@playwright/test';
@@ -214,7 +215,6 @@ test.describe('Doc Editor', () => {
});
test('it saves the doc when we quit pages', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(browserName === 'webkit', 'This test is very flaky with webkit');
// Check the first doc
@@ -226,9 +226,13 @@ test.describe('Doc Editor', () => {
await editor.fill('Hello World Doc persisted 2');
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
await page.waitForTimeout(1000);
const urlDoc = page.url();
await page.goto(urlDoc);
// Wait for editor to load
await expect(editor).toBeVisible();
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
});
@@ -279,7 +283,7 @@ test.describe('Doc Editor', () => {
await expect(image).toBeVisible();
// Wait for the media-check to be processed
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1000);
// Check src of image
@@ -397,8 +401,6 @@ test.describe('Doc Editor', () => {
const editor = page.locator('.ProseMirror');
await editor.getByText('Hello').selectText();
/* eslint-disable playwright/no-conditional-expect */
/* eslint-disable playwright/no-conditional-in-test */
if (!ai_transform && !ai_translate) {
await expect(page.getByRole('button', { name: 'AI' })).toBeHidden();
return;
@@ -425,8 +427,6 @@ test.describe('Doc Editor', () => {
page.getByRole('menuitem', { name: 'Language' }),
).toBeHidden();
}
/* eslint-enable playwright/no-conditional-expect */
/* eslint-enable playwright/no-conditional-in-test */
});
});
@@ -467,12 +467,14 @@ test.describe('Doc Editor', () => {
await expect(
page.getByRole('button', {
name: 'Download',
exact: true,
}),
).toBeVisible();
void page
.getByRole('button', {
name: 'Download',
exact: true,
})
.click();
@@ -701,8 +703,20 @@ test.describe('Doc Editor', () => {
const emojiButton = calloutBlock.getByRole('button');
await expect(emojiButton).toHaveText('💡');
await emojiButton.click();
await page.locator('button[aria-label="⚠️"]').click();
await expect(emojiButton).toHaveText('⚠️');
// Group smiley
await expect(page.getByRole('button', { name: '🤠' })).toBeVisible();
// Group animals
await page.getByText('Animals & Nature').scrollIntoViewIfNeeded();
await expect(page.getByRole('button', { name: '🦆' })).toBeVisible();
// Group travel
await page.getByText('Travel & Places').scrollIntoViewIfNeeded();
await expect(page.getByRole('button', { name: '🚝' })).toBeVisible();
// Group objects
await page.getByText('Objects').scrollIntoViewIfNeeded();
await expect(page.getByRole('button', { name: '🪇' })).toBeVisible();
// Group symbol
await page.getByText('Symbols').scrollIntoViewIfNeeded();
await expect(page.getByRole('button', { name: '🛃' })).toBeVisible();
await page.locator('.bn-side-menu > button').last().click();
await page.locator('.mantine-Menu-dropdown > button').last().click();
@@ -29,12 +29,7 @@ test.describe('Doc Export', () => {
})
.click();
await expect(
page
.locator('div')
.filter({ hasText: /^Download$/ })
.first(),
).toBeVisible();
await expect(page.getByTestId('modal-export-title')).toBeVisible();
await expect(
page.getByText('Download your document in a .docx or .pdf format.'),
).toBeVisible();
@@ -43,9 +38,11 @@ test.describe('Doc Export', () => {
).toBeVisible();
await expect(page.getByRole('combobox', { name: 'Format' })).toBeVisible();
await expect(
page.getByRole('button', { name: 'Close the modal' }),
page.getByRole('button', {
name: 'Close the download modal',
}),
).toBeVisible();
await expect(page.getByRole('button', { name: 'Download' })).toBeVisible();
await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
});
test('it exports the doc with pdf line break', async ({
@@ -86,12 +83,7 @@ test.describe('Doc Export', () => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
void page
.getByRole('button', {
name: 'Download',
exact: true,
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -136,23 +128,13 @@ test.describe('Doc Export', () => {
await page.getByRole('combobox', { name: 'Format' }).click();
await page.getByRole('option', { name: 'Docx' }).click();
await expect(
page.getByRole('button', {
name: 'Download',
exact: true,
}),
).toBeVisible();
await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.docx`);
});
void page
.getByRole('button', {
name: 'Download',
exact: true,
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.docx`);
@@ -218,11 +200,7 @@ test.describe('Doc Export', () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
await expect(
page.getByRole('button', {
name: 'Download',
}),
).toBeVisible();
await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const responseCorsPromise = page.waitForResponse(
(response) =>
@@ -233,11 +211,7 @@ test.describe('Doc Export', () => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
void page
.getByRole('button', {
name: 'Download',
})
.click();
void page.getByTestId('doc-export-download-button').click();
const responseCors = await responseCorsPromise;
expect(responseCors.ok()).toBe(true);
@@ -279,21 +253,13 @@ test.describe('Doc Export', () => {
})
.click();
await expect(
page.getByRole('button', {
name: 'Download',
}),
).toBeVisible();
await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
void page
.getByRole('button', {
name: 'Download',
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -330,22 +296,14 @@ test.describe('Doc Export', () => {
.click();
await expect(
page.getByRole('button', {
name: 'Download',
exact: true,
}),
page.getByTestId('doc-open-modal-download-button'),
).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
void page
.getByRole('button', {
name: 'Download',
exact: true,
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -392,22 +350,14 @@ test.describe('Doc Export', () => {
.click();
await expect(
page.getByRole('button', {
name: 'Download',
exact: true,
}),
page.getByTestId('doc-open-modal-download-button'),
).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
void page
.getByRole('button', {
name: 'Download',
exact: true,
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -443,14 +393,9 @@ test.describe('Doc Export', () => {
})
.click();
await page.waitForURL('**/docs/**', {
timeout: 10000,
waitUntil: 'domcontentloaded',
});
const input = page.getByLabel('doc title input');
const input = page.locator('.--docs--doc-title-input[role="textbox"]');
await expect(input).toBeVisible();
await expect(input).toHaveText('');
await expect(input).toHaveText('', { timeout: 10000 });
await input.click();
await input.fill(randomDocFrench);
await input.blur();
@@ -461,7 +406,7 @@ test.describe('Doc Export', () => {
await page
.getByRole('button', {
name: 'Export the document',
name: 'Exporter le document',
})
.click();
@@ -469,12 +414,7 @@ test.describe('Doc Export', () => {
return download.suggestedFilename().includes(`${randomDocFrench}.pdf`);
});
void page
.getByRole('button', {
name: 'Télécharger',
exact: true,
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDocFrench}.pdf`);
@@ -509,19 +449,23 @@ test.describe('Doc Export', () => {
await page.locator('.bn-block-outer').last().fill('/');
await page.getByText('Link a doc').first().click();
await page
.locator(
"span[data-inline-content-type='interlinkingSearchInline'] input",
)
.fill('interlink-child');
const input = page.locator(
"span[data-inline-content-type='interlinkingSearchInline'] input",
);
const searchContainer = page.locator('.quick-search-container');
await page
.locator('.quick-search-container')
.getByText('interlink-child')
.click();
await input.fill('export-interlink');
const interlink = page.getByRole('link', {
name: 'interlink-child',
await expect(searchContainer).toBeVisible();
await expect(searchContainer.getByText(randomDoc)).toBeVisible();
// We are in docChild, we want to create a link to randomDoc (parent)
await searchContainer.getByText(randomDoc).click();
// Search the interlinking link in the editor (not in the document tree)
const editor = page.locator('.ProseMirror.bn-editor');
const interlink = editor.getByRole('link', {
name: randomDoc,
});
await expect(interlink).toBeVisible();
@@ -536,12 +480,7 @@ test.describe('Doc Export', () => {
})
.click();
void page
.getByRole('button', {
name: 'Download',
exact: true,
})
.click();
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${docChild}.pdf`);
@@ -549,6 +488,6 @@ test.describe('Doc Export', () => {
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
const pdfData = await pdf(pdfBuffer);
expect(pdfData.text).toContain('interlink-child'); // This is the pdf text
expect(pdfData.text).toContain(randomDoc);
});
});
@@ -36,9 +36,8 @@ test.describe('Doc grid dnd', () => {
expect(draggableBoundingBox).toBeDefined();
expect(dropZoneBoundingBox).toBeDefined();
// eslint-disable-next-line playwright/no-conditional-in-test
if (!draggableBoundingBox || !dropZoneBoundingBox) {
throw new Error('Impossible de déterminer la position des éléments');
throw new Error('Unable to determine the position of the elements');
}
await page.mouse.move(
@@ -86,9 +85,8 @@ test.describe('Doc grid dnd', () => {
const noDropAndNoDragBoundigBox = await noDropAndNoDrag.boundingBox();
// eslint-disable-next-line playwright/no-conditional-in-test
if (!canDropAndDragBoundigBox || !noDropAndNoDragBoundigBox) {
throw new Error('Impossible de déterminer la position des éléments');
throw new Error('Unable to determine the position of the elements');
}
await page.mouse.move(
@@ -137,9 +135,8 @@ test.describe('Doc grid dnd', () => {
const noDropAndNoDragBoundigBox = await noDropAndNoDrag.boundingBox();
// eslint-disable-next-line playwright/no-conditional-in-test
if (!canDropAndDragBoundigBox || !noDropAndNoDragBoundigBox) {
throw new Error('Impossible de déterminer la position des éléments');
throw new Error('Unable to determine the position of the elements');
}
await page.mouse.move(
@@ -80,9 +80,7 @@ test.describe('Documents Grid mobile', () => {
hasText: 'My mocked document',
});
await expect(
row.locator('[aria-describedby="doc-title"]').nth(0),
).toHaveText('My mocked document');
await expect(row.getByTestId('doc-title')).toHaveText('My mocked document');
});
});
@@ -149,7 +147,7 @@ test.describe('Document grid item options', () => {
await page
.getByRole('button', {
name: 'Confirm deletion',
name: 'Delete document',
})
.click();
@@ -295,7 +293,7 @@ test.describe('Documents Grid', () => {
docs = result.results as SmallDoc[];
await expect(page.getByTestId('grid-loader')).toBeHidden();
await expect(page.locator('h4').getByText('All docs')).toBeVisible();
await expect(page.locator('h2').getByText('All docs')).toBeVisible();
const thead = page.getByTestId('docs-grid-header');
await expect(thead.getByText(/Name/i)).toBeVisible();
@@ -25,7 +25,7 @@ test.describe('Doc Header', () => {
'It is the card information about the document.',
);
const docTitle = card.getByRole('textbox', { name: 'doc title input' });
const docTitle = card.getByRole('textbox', { name: 'Document title' });
await expect(docTitle).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
@@ -54,7 +54,7 @@ test.describe('Doc Header', () => {
test('it updates the title doc', async ({ page, browserName }) => {
await createDoc(page, 'doc-update', browserName, 1);
const docTitle = page.getByRole('textbox', { name: 'doc title input' });
const docTitle = page.getByRole('textbox', { name: 'Document title' });
await expect(docTitle).toBeVisible();
await docTitle.fill('Hello World');
await docTitle.blur();
@@ -66,7 +66,7 @@ test.describe('Doc Header', () => {
browserName,
}) => {
await createDoc(page, 'doc-update', browserName, 1);
const docTitle = page.getByRole('textbox', { name: 'doc title input' });
const docTitle = page.getByRole('textbox', { name: 'Document title' });
await expect(docTitle).toBeVisible();
await docTitle.fill('👍 Hello Emoji World');
await docTitle.blur();
@@ -100,7 +100,7 @@ test.describe('Doc Header', () => {
await page
.getByRole('button', {
name: 'Confirm deletion',
name: 'Delete document',
})
.click();
@@ -155,7 +155,9 @@ test.describe('Doc Header', () => {
await page.getByRole('button', { name: 'Share' }).click();
const shareModal = page.getByLabel('Share modal');
const shareModal = page.getByRole('dialog', {
name: 'Share modal content',
});
await expect(shareModal).toBeVisible();
await expect(page.getByText('Share the document')).toBeVisible();
@@ -226,23 +228,27 @@ test.describe('Doc Header', () => {
await page.getByRole('button', { name: 'Share' }).click();
const shareModal = page.getByLabel('Share modal');
const shareModal = page.getByRole('dialog', {
name: 'Share modal content',
});
await expect(shareModal).toBeVisible();
await expect(page.getByText('Share the document')).toBeVisible();
await expect(page.getByPlaceholder('Type a name or email')).toBeHidden();
const invitationCard = shareModal.getByLabel('List invitation card');
await expect(invitationCard).toBeVisible();
await expect(
invitationCard.getByText('test@invitation.test').first(),
).toBeVisible();
await expect(invitationCard.getByLabel('doc-role-text')).toBeVisible();
await expect(invitationCard.getByLabel('Document role text')).toBeVisible();
await expect(
invitationCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
const memberCard = shareModal.getByLabel('List members card');
await expect(memberCard.getByText('test@accesses.test')).toBeVisible();
await expect(memberCard.getByLabel('doc-role-text')).toBeVisible();
await expect(memberCard.getByLabel('Document role text')).toBeVisible();
await expect(
memberCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
@@ -294,17 +300,18 @@ test.describe('Doc Header', () => {
await expect(page.getByPlaceholder('Type a name or email')).toBeHidden();
const invitationCard = shareModal.getByLabel('List invitation card');
await expect(invitationCard).toBeVisible();
await expect(
invitationCard.getByText('test@invitation.test').first(),
).toBeVisible();
await expect(invitationCard.getByLabel('doc-role-text')).toBeVisible();
await expect(invitationCard.getByLabel('Document role text')).toBeVisible();
await expect(
invitationCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
const memberCard = shareModal.getByLabel('List members card');
await expect(memberCard.getByText('test@accesses.test')).toBeVisible();
await expect(memberCard.getByLabel('doc-role-text')).toBeVisible();
await expect(memberCard.getByLabel('Document role text')).toBeVisible();
await expect(
memberCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
@@ -314,7 +321,6 @@ test.describe('Doc Header', () => {
page,
browserName,
}) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
browserName === 'webkit',
'navigator.clipboard is not working with webkit and playwright',
@@ -349,7 +355,6 @@ test.describe('Doc Header', () => {
});
test('It checks the copy as HTML button', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
browserName === 'webkit',
'navigator.clipboard is not working with webkit and playwright',
@@ -384,7 +389,6 @@ test.describe('Doc Header', () => {
});
test('it checks the copy link button', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
browserName === 'webkit',
'navigator.clipboard is not working with webkit and playwright',
@@ -581,7 +585,10 @@ test.describe('Documents Header mobile', () => {
await page.getByLabel('Open the document options').click();
await page.getByLabel('Share').click();
await expect(page.getByLabel('Share modal')).toBeVisible();
const shareModal = page.getByRole('dialog', {
name: 'Share modal content',
});
await expect(shareModal).toBeVisible();
await page.getByRole('button', { name: 'close' }).click();
await expect(page.getByLabel('Share modal')).toBeHidden();
});
@@ -52,6 +52,12 @@ test.describe('Inherited share accesses', () => {
await expect(docVisibilityCard.getByText('Connected')).toBeVisible();
await expect(docVisibilityCard.getByText('Reading')).toBeVisible();
await docVisibilityCard.getByText('Reading').click();
await page.getByRole('menuitem', { name: 'Editing' }).click();
await expect(docVisibilityCard.getByText('Reading')).toBeHidden();
await expect(docVisibilityCard.getByText('Editing')).toBeVisible();
// Verify inherited link
await docVisibilityCard.getByText('Connected').click();
await expect(
@@ -61,17 +67,13 @@ test.describe('Inherited share accesses', () => {
// Update child link
await page.getByRole('menuitem', { name: 'Public' }).click();
await docVisibilityCard.getByText('Reading').click();
await page.getByRole('menuitem', { name: 'Editing' }).click();
await expect(docVisibilityCard.getByText('Connected')).toBeHidden();
await expect(docVisibilityCard.getByText('Reading')).toBeHidden();
await expect(
docVisibilityCard.getByText('Public', {
exact: true,
}),
).toBeVisible();
await expect(docVisibilityCard.getByText('Editing')).toBeVisible();
await expect(
docVisibilityCard.getByText(
'The link sharing rules differ from the parent document',
@@ -15,7 +15,7 @@ test.describe('Document create member', () => {
});
test('it selects 2 users and 1 invitation', async ({ page, browserName }) => {
const inputFill = 'user ';
const inputFill = 'user.test';
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes(`/users/?q=${encodeURIComponent(inputFill)}`) &&
@@ -201,7 +201,7 @@ test.describe('Document create member', () => {
await page.getByLabel('Reader').click();
const moreActions = userInvitation.getByRole('button', {
name: 'more_horiz',
name: 'Open invitation actions menu',
});
await moreActions.click();
@@ -151,7 +151,6 @@ test.describe('Document list members', () => {
await expect(soloOwner).toBeVisible();
await list.click({
// eslint-disable-next-line playwright/no-force-option
force: true, // Force click to close the dropdown
});
const newUserEmail = await addNewMember(page, 0, 'Owner');
@@ -163,13 +162,11 @@ test.describe('Document list members', () => {
await currentUserRole.click();
await expect(soloOwner).toBeHidden();
await list.click({
// eslint-disable-next-line playwright/no-force-option
force: true, // Force click to close the dropdown
});
await newUserRoles.click();
await list.click({
// eslint-disable-next-line playwright/no-force-option
force: true, // Force click to close the dropdown
});
@@ -40,7 +40,6 @@ test.describe('Doc Routing', () => {
});
test('checks 404 on docs/[id] page', async ({ page }) => {
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(300);
await page.goto('/docs/some-unknown-doc');
@@ -61,32 +60,39 @@ test.describe('Doc Routing', () => {
await page.locator('.ProseMirror.bn-editor').fill('Hello World');
const responsePromise = page.route(
/.*\/documents\/.*\/$|users\/me\/$/,
async (route) => {
const request = route.request();
// Wait for the doc link (via its dynamic title) to be visible
const docLink = page.getByRole('link', { name: docTitle });
await expect(docLink).toBeVisible();
if (
request.method().includes('PATCH') ||
request.method().includes('GET')
) {
await route.fulfill({
status: 401,
json: {
detail: 'Log in to access the document',
},
});
} else {
await route.continue();
}
},
// Intercept GET/PATCH requests to return 401
await page.route(/.*\/documents\/.*\/$|users\/me\/$/, async (route) => {
const request = route.request();
if (
request.method().includes('PATCH') ||
request.method().includes('GET')
) {
await route.fulfill({
status: 401,
json: { detail: 'Log in to access the document' },
});
} else {
await route.continue();
}
});
// Explicitly wait for a 401 response after clicking
const wait401 = page.waitForResponse(
(resp) =>
resp.status() === 401 &&
/\/(documents\/[^/]+\/|users\/me\/)$/.test(resp.url()),
);
await page.getByRole('link', { name: '401-doc-parent' }).click();
await docLink.click();
await wait401;
await responsePromise;
await expect(page.getByText('Log in to access the document')).toBeVisible();
await expect(page.getByText('Log in to access the document.')).toBeVisible({
timeout: 10000,
});
});
});
@@ -33,7 +33,7 @@ test.describe('Document search', () => {
).toBeVisible();
await expect(
page.getByLabel('Search modal').getByText('search'),
page.getByRole('heading', { name: 'Search docs' }),
).toBeVisible();
const inputSearch = page.getByPlaceholder('Type the name of a document');
@@ -79,7 +79,7 @@ test.describe('Document search', () => {
await page.keyboard.press('Control+k');
await expect(
page.getByLabel('Search modal').getByText('search'),
page.getByRole('heading', { name: 'Search docs' }),
).toBeVisible();
await page.keyboard.press('Escape');
@@ -173,12 +173,13 @@ test.describe('Document search', () => {
.getByRole('combobox', { name: 'Quick search input' })
.fill('sub page search');
// Expect to find the first doc
// Expect to find the first and second docs in the results list
const resultsList = page.getByRole('listbox');
await expect(
page.getByRole('presentation').getByLabel(firstDocTitle),
resultsList.getByRole('option', { name: firstDocTitle }),
).toBeVisible();
await expect(
page.getByRole('presentation').getByLabel(secondDocTitle),
resultsList.getByRole('option', { name: secondDocTitle }),
).toBeVisible();
await page.getByRole('button', { name: 'close' }).click();
@@ -195,14 +196,15 @@ test.describe('Document search', () => {
.fill('second');
// Now there is a sub page - expect to have the focus on the current doc
const updatedResultsList = page.getByRole('listbox');
await expect(
page.getByRole('presentation').getByLabel(secondDocTitle),
updatedResultsList.getByRole('option', { name: secondDocTitle }),
).toBeVisible();
await expect(
page.getByRole('presentation').getByLabel(secondChildDocTitle),
updatedResultsList.getByRole('option', { name: secondChildDocTitle }),
).toBeVisible();
await expect(
page.getByRole('presentation').getByLabel(firstDocTitle),
updatedResultsList.getByRole('option', { name: firstDocTitle }),
).toBeHidden();
});
});
@@ -1,4 +1,3 @@
/* eslint-disable playwright/no-conditional-in-test */
import { expect, test } from '@playwright/test';
import {
@@ -51,7 +50,7 @@ test.describe('Doc Tree', () => {
await expect(subPageItem).toBeVisible();
await subPageItem.click();
await verifyDocName(page, '');
const input = page.getByRole('textbox', { name: 'doc title input' });
const input = page.getByRole('textbox', { name: 'Document title' });
await input.click();
const [randomDocName] = randomName('doc-tree-test', browserName, 1);
await input.fill(randomDocName);
@@ -197,7 +196,7 @@ test.describe('Doc Tree', () => {
await page.getByText('Move to my docs').click();
await expect(
page.getByRole('textbox', { name: 'doc title input' }),
page.getByRole('textbox', { name: 'Document title' }),
).not.toHaveText(docChild);
const header = page.locator('header').first();
@@ -15,7 +15,6 @@ test.describe('Doc Visibility', () => {
});
test('It checks the copy link button', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
browserName === 'webkit',
'navigator.clipboard is not working with webkit and playwright',
@@ -119,8 +118,11 @@ test.describe('Doc Visibility: Restricted', () => {
.click();
const otherBrowser = BROWSERS.find((b) => b !== browserName);
if (!otherBrowser) {
throw new Error('No alternative browser found');
}
await keyCloakSignIn(page, otherBrowser!);
await keyCloakSignIn(page, otherBrowser);
await expect(page.getByTestId('header-logo-link')).toBeVisible({
timeout: 10000,
@@ -151,6 +153,9 @@ test.describe('Doc Visibility: Restricted', () => {
});
const otherBrowser = BROWSERS.find((b) => b !== browserName);
if (!otherBrowser) {
throw new Error('No alternative browser found');
}
const username = `user@${otherBrowser}.test`;
await inputSearch.fill(username);
await page.getByRole('option', { name: username }).click();
@@ -174,7 +179,7 @@ test.describe('Doc Visibility: Restricted', () => {
})
.click();
await keyCloakSignIn(page, otherBrowser!);
await keyCloakSignIn(page, otherBrowser);
await expect(page.getByTestId('header-logo-link')).toBeVisible();
@@ -449,7 +454,10 @@ test.describe('Doc Visibility: Authenticated', () => {
.click();
const otherBrowser = BROWSERS.find((b) => b !== browserName);
await keyCloakSignIn(page, otherBrowser!);
if (!otherBrowser) {
throw new Error('No alternative browser found');
}
await keyCloakSignIn(page, otherBrowser);
await expect(page.getByTestId('header-logo-link')).toBeVisible({
timeout: 10000,
@@ -538,7 +546,10 @@ test.describe('Doc Visibility: Authenticated', () => {
.click();
const otherBrowser = BROWSERS.find((b) => b !== browserName);
await keyCloakSignIn(page, otherBrowser!);
if (!otherBrowser) {
throw new Error('No alternative browser found');
}
await keyCloakSignIn(page, otherBrowser);
await expect(page.getByTestId('header-logo-link')).toBeVisible({
timeout: 10000,
@@ -76,7 +76,6 @@ test.describe('Header', () => {
* La gaufre load a js file from a remote server,
* it takes some time to load the file and have the interaction available
*/
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1500);
await header
@@ -27,7 +27,7 @@ export const CONFIG = {
export const overrideConfig = async (
page: Page,
newConfig: { [K in keyof typeof CONFIG]?: unknown },
newConfig: { [_K in keyof typeof CONFIG]?: unknown },
) =>
await page.route('**/api/v1.0/config/', async (route) => {
const request = route.request();
@@ -101,10 +101,9 @@ export const createDoc = async (
waitUntil: 'networkidle',
});
const input = page.getByLabel('doc title input');
const input = page.getByLabel('Document title');
await expect(input).toBeVisible();
await expect(input).toHaveText('');
await input.click();
await input.fill(randomDocs[i]);
await input.blur();
@@ -120,10 +119,11 @@ export const verifyDocName = async (page: Page, docName: string) => {
timeout: 10000,
});
/*replace toHaveText with toContainText to handle cases where emojis or other characters might be added*/
try {
await expect(
page.getByRole('textbox', { name: 'doc title input' }),
).toHaveText(docName);
page.getByRole('textbox', { name: 'Document title' }),
).toContainText(docName);
} catch {
await expect(page.getByRole('heading', { name: docName })).toBeVisible();
}
@@ -172,7 +172,7 @@ export const goToGridDoc = async (
await expect(row).toBeVisible();
const docTitleContent = row.locator('[aria-describedby="doc-title"]').first();
const docTitleContent = row.getByTestId('doc-title').first();
const docTitle = await docTitleContent.textContent();
expect(docTitle).toBeDefined();
@@ -182,9 +182,9 @@ export const goToGridDoc = async (
};
export const updateDocTitle = async (page: Page, title: string) => {
const input = page.getByLabel('doc title input');
await expect(input).toBeVisible();
const input = page.getByRole('textbox', { name: 'Document title' });
await expect(input).toHaveText('');
await expect(input).toBeVisible();
await input.click();
await input.fill(title);
await input.click();
@@ -8,7 +8,7 @@ export const addNewMember = async (
page: Page,
index: number,
role: 'Administrator' | 'Owner' | 'Editor' | 'Reader',
fillText: string = 'user ',
fillText: string = 'user.test',
) => {
const responsePromiseSearchUser = page.waitForResponse(
(response) =>
+20
View File
@@ -0,0 +1,20 @@
import { defineConfig } from '@eslint/config-helpers';
import docsPlugin from 'eslint-plugin-docs';
const eslintConfig = defineConfig([
{
files: ['**/*.ts', '**/*.mjs'],
plugins: {
docs: docsPlugin,
},
extends: ['docs/playwright'],
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
project: ['./tsconfig.json'],
},
},
},
]);
export default eslintConfig;
+8 -4
View File
@@ -1,9 +1,12 @@
{
"name": "app-e2e",
"version": "3.5.0",
"version": "3.7.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
"private": true,
"scripts": {
"lint": "eslint . --ext .ts",
"lint": "eslint",
"install-playwright": "playwright install --with-deps",
"test": "playwright test",
"test:ui": "yarn test --ui",
@@ -15,11 +18,12 @@
"@playwright/test": "1.55.0",
"@types/node": "*",
"@types/pdf-parse": "1.1.5",
"eslint-config-impress": "*",
"eslint-plugin-docs": "*",
"typescript": "*"
},
"dependencies": {
"convert-stream": "1.0.2",
"pdf-parse": "1.1.1"
}
},
"packageManager": "yarn@1.22.22"
}
+2 -2
View File
@@ -12,8 +12,8 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"incremental": true
},
"include": ["**/*.ts", "**/*.d.ts"],
"include": ["**/*.ts", "**/*.d.ts", "**/*.mjs"],
"exclude": ["node_modules"]
}
+1 -1
View File
@@ -1,5 +1,5 @@
declare module 'convert-stream' {
export function toBuffer(
readableStream: NodeJS.ReadableStream,
_readableStream: NodeJS.ReadableStream,
): Promise<Buffer>;
}
-14
View File
@@ -1,14 +0,0 @@
module.exports = {
root: true,
extends: ['impress/next'],
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
settings: {
next: {
rootDir: __dirname,
},
},
ignorePatterns: ['node_modules', '.eslintrc.js', 'service-worker.js'],
};
@@ -0,0 +1,24 @@
import { defineConfig } from '@eslint/config-helpers';
import docsPlugin from 'eslint-plugin-docs';
const eslintConfig = defineConfig([
{
plugins: {
docs: docsPlugin,
},
extends: ['docs/next'],
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
project: ['./tsconfig.json'],
},
},
settings: {
next: {
rootDir: import.meta.dirname,
},
},
},
]);
export default eslintConfig;
+1
View File
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
+18 -14
View File
@@ -1,6 +1,9 @@
{
"name": "app-impress",
"version": "3.5.0",
"version": "3.7.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
"private": true,
"scripts": {
"dev": "next dev",
@@ -33,22 +36,22 @@
"@hocuspocus/provider": "2.15.2",
"@openfun/cunningham-react": "3.2.3",
"@react-pdf/renderer": "4.3.0",
"@sentry/nextjs": "10.8.0",
"@tanstack/react-query": "5.85.6",
"@sentry/nextjs": "10.11.0",
"@tanstack/react-query": "5.87.4",
"canvg": "4.0.3",
"clsx": "2.1.1",
"cmdk": "1.1.1",
"crisp-sdk-web": "1.0.25",
"docx": "9.5.0",
"emoji-mart": "5.6.0",
"emoji-regex": "10.4.0",
"i18next": "25.4.2",
"emoji-regex": "10.5.0",
"i18next": "25.5.2",
"i18next-browser-languagedetector": "8.2.0",
"idb": "8.0.3",
"lodash": "4.17.21",
"luxon": "3.7.1",
"next": "15.4.7",
"posthog-js": "1.261.0",
"luxon": "3.7.2",
"next": "15.5.3",
"posthog-js": "1.264.2",
"react": "*",
"react-aria-components": "1.12.1",
"react-dom": "*",
@@ -56,14 +59,14 @@
"react-intersection-observer": "9.16.0",
"react-select": "5.10.2",
"styled-components": "6.1.19",
"use-debounce": "10.0.5",
"use-debounce": "10.0.6",
"y-protocols": "1.0.6",
"yjs": "*",
"zustand": "5.0.8"
},
"devDependencies": {
"@svgr/webpack": "8.1.0",
"@tanstack/react-query-devtools": "5.85.6",
"@tanstack/react-query-devtools": "5.87.4",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.8.0",
"@testing-library/react": "16.3.0",
@@ -75,13 +78,13 @@
"@types/react-dom": "*",
"@vitejs/plugin-react": "5.0.2",
"cross-env": "10.0.0",
"dotenv": "17.2.1",
"eslint-config-impress": "*",
"dotenv": "17.2.2",
"eslint-plugin-docs": "*",
"fetch-mock": "9.11.0",
"jsdom": "26.1.0",
"node-fetch": "2.7.0",
"prettier": "3.6.2",
"stylelint": "16.23.1",
"stylelint": "16.24.0",
"stylelint-config-standard": "39.0.0",
"stylelint-prettier": "5.0.3",
"typescript": "*",
@@ -89,5 +92,6 @@
"vitest": "3.2.4",
"webpack": "5.101.3",
"workbox-webpack-plugin": "7.1.0"
}
},
"packageManager": "yarn@1.22.22"
}
@@ -28,17 +28,18 @@ const StyledButton = styled(Button)<StyledButtonProps>`
border: none;
background: none;
outline: none;
transition: all 0.2s ease-in-out;
font-weight: 500;
font-size: 0.938rem;
padding: 0;
${({ $css }) => $css};
&:hover {
background-color: var(
--c--components--button--primary-text--background--color-hover
);
}
&:focus-visible {
outline: 2px solid var(--c--theme--colors--primary-500);
outline-offset: 2px;
box-shadow: 0 0 0 2px var(--c--theme--colors--primary-400);
border-radius: 4px;
transition: none;
}
`;
@@ -110,7 +110,6 @@ export const DropdownMenu = ({
$direction="row"
$align="center"
$position="relative"
aria-controls="menu"
>
<Box>{children}</Box>
<Icon
@@ -125,9 +124,7 @@ export const DropdownMenu = ({
/>
</Box>
) : (
<Box ref={blockButtonRef} aria-controls="menu">
{children}
</Box>
<Box ref={blockButtonRef}>{children}</Box>
)
}
>
@@ -207,14 +204,13 @@ export const DropdownMenu = ({
}
&:focus-visible {
outline: 2px solid var(--c--theme--colors--primary-500);
outline: 2px solid var(--c--theme--colors--primary-400);
outline-offset: -2px;
background-color: var(--c--theme--colors--greyscale-050);
}
${isFocused &&
css`
outline: 2px solid var(--c--theme--colors--primary-500);
outline-offset: -2px;
background-color: var(--c--theme--colors--greyscale-050);
`}
@@ -231,6 +227,7 @@ export const DropdownMenu = ({
$theme="greyscale"
$variation={isDisabled ? '400' : '1000'}
iconName={option.icon}
aria-hidden="true"
/>
)}
<Text $variation={isDisabled ? '400' : '1000'}>
@@ -239,7 +236,12 @@ export const DropdownMenu = ({
</Box>
{(option.isSelected ||
selectedValues?.includes(option.value ?? '')) && (
<Icon iconName="check" $size="20px" $theme="greyscale" />
<Icon
iconName="check"
$size="20px"
$theme="greyscale"
aria-hidden="true"
/>
)}
</BoxButton>
{option.showSeparator && (
@@ -32,7 +32,7 @@ export const useDropdownKeyboardNav = ({
.filter((index) => index !== -1);
switch (event.key) {
case 'ArrowDown':
case 'ArrowDown': {
event.preventDefault();
const nextIndex =
focusedIndex < enabledIndices.length - 1 ? focusedIndex + 1 : 0;
@@ -40,8 +40,9 @@ export const useDropdownKeyboardNav = ({
setFocusedIndex(nextIndex);
menuItemRefs.current[nextEnabledIndex]?.focus();
break;
}
case 'ArrowUp':
case 'ArrowUp': {
event.preventDefault();
const prevIndex =
focusedIndex > 0 ? focusedIndex - 1 : enabledIndices.length - 1;
@@ -49,9 +50,10 @@ export const useDropdownKeyboardNav = ({
setFocusedIndex(prevIndex);
menuItemRefs.current[prevEnabledIndex]?.focus();
break;
}
case 'Enter':
case ' ':
case ' ': {
event.preventDefault();
if (focusedIndex >= 0 && focusedIndex < enabledIndices.length) {
const selectedOptionIndex = enabledIndices[focusedIndex];
@@ -62,6 +64,7 @@ export const useDropdownKeyboardNav = ({
}
}
break;
}
case 'Escape':
event.preventDefault();
@@ -1,4 +1,4 @@
export * from './AlertModal';
export * from './modal/AlertModal';
export * from './Box';
export * from './BoxButton';
export * from './Card';
@@ -9,7 +9,7 @@ export * from './Icon';
export * from './InfiniteScroll';
export * from './Link';
export * from './Loading';
export * from './SideModal';
export * from './modal/SideModal';
export * from './separators';
export * from './Text';
export * from './TextErrors';
@@ -2,8 +2,8 @@ import { Button, Modal, ModalSize } from '@openfun/cunningham-react';
import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { Box } from './Box';
import { Text } from './Text';
import { Box } from '../Box';
import { Text } from '../Text';
export type AlertModalProps = {
description: ReactNode;
@@ -30,15 +30,23 @@ export const AlertModal = ({
isOpen={isOpen}
size={ModalSize.MEDIUM}
onClose={onClose}
aria-describedby="alert-modal-title"
title={
<Text $size="h6" $align="flex-start" $variation="1000">
<Text
$size="h6"
as="h1"
$margin="0"
id="alert-modal-title"
$align="flex-start"
$variation="1000"
>
{title}
</Text>
}
rightActions={
<>
<Button
aria-label={t('Close the modal')}
aria-label={`${t('Cancel')} - ${title}`}
color="secondary"
fullWidth
onClick={() => onClose()}
@@ -55,12 +63,11 @@ export const AlertModal = ({
</>
}
>
<Box
aria-label={t('Confirmation button')}
className="--docs--alert-modal"
>
<Box className="--docs--alert-modal">
<Box>
<Text $variation="600">{description}</Text>
<Text $variation="600" as="p">
{description}
</Text>
</Box>
</Box>
</Modal>
@@ -0,0 +1,22 @@
import { Button, type ButtonProps } from '@openfun/cunningham-react';
import React from 'react';
import { Box } from '@/components';
const ButtonCloseModal = (props: ButtonProps) => {
return (
<Button
type="button"
size="small"
color="primary-text"
icon={
<Box as="span" aria-hidden="true" className="material-icons-filled">
close
</Box>
}
{...props}
/>
);
};
export default ButtonCloseModal;
@@ -55,7 +55,6 @@ export const QuickSearchInput = ({
</div>
)}
<Command.Input
/* eslint-disable-next-line jsx-a11y/no-autofocus */
autoFocus={true}
aria-label={t('Quick search input')}
onClick={(e) => {
@@ -65,6 +64,7 @@ export const QuickSearchInput = ({
role="combobox"
placeholder={placeholder ?? t('Search')}
onValueChange={onFilter}
maxLength={254}
/>
</Box>
{separator && <HorizontalSeparator $withPadding={false} />}
-2
View File
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
declare module '*.svg' {
import * as React from 'react';
@@ -2,7 +2,8 @@ import { Button } from '@openfun/cunningham-react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { BoxButton } from '@/components';
import { Box, BoxButton } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import ProConnectImg from '../assets/button-proconnect.svg';
import { useAuth } from '../hooks';
@@ -11,6 +12,7 @@ import { gotoLogin, gotoLogout } from '../utils';
export const ButtonLogin = () => {
const { t } = useTranslation();
const { authenticated } = useAuth();
const { colorsTokens } = useCunninghamTheme();
if (!authenticated) {
return (
@@ -26,14 +28,23 @@ export const ButtonLogin = () => {
}
return (
<Button
onClick={gotoLogout}
color="primary-text"
aria-label={t('Logout')}
className="--docs--button-logout"
<Box
$css={css`
.--docs--button-logout:focus-visible {
box-shadow: 0 0 0 2px ${colorsTokens['primary-400']} !important;
border-radius: 4px;
}
`}
>
{t('Logout')}
</Button>
<Button
onClick={gotoLogout}
color="primary-text"
aria-label={t('Logout')}
className="--docs--button-logout"
>
{t('Logout')}
</Button>
</Box>
);
};
@@ -44,7 +44,6 @@ import {
} from './custom-inline-content';
import XLMultiColumn from './xl-multi-column';
const multiColumnDropCursor = XLMultiColumn?.multiColumnDropCursor;
const multiColumnLocales = XLMultiColumn?.locales;
const withMultiColumn = XLMultiColumn?.withMultiColumn;
@@ -157,7 +156,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
},
uploadFile,
schema: blockNoteSchema,
dropCursor: multiColumnDropCursor,
},
[collabName, lang, provider, uploadFile],
);
@@ -205,7 +205,7 @@ type ItemProps = Omit<ItemDefault, 'onClick'> & {
onClick: (e: React.MouseEvent) => void;
};
interface AIMenuItemTransform {
interface AIMenuItemTransformProps {
action: AITransformActions;
docId: string;
icon?: ReactNode;
@@ -216,7 +216,7 @@ const AIMenuItemTransform = ({
action,
children,
icon,
}: PropsWithChildren<AIMenuItemTransform>) => {
}: PropsWithChildren<AIMenuItemTransformProps>) => {
const { mutateAsync: requestAI, isPending } = useDocAITransform();
const editor = useBlockNoteEditor();
@@ -244,7 +244,7 @@ const AIMenuItemTransform = ({
);
};
interface AIMenuItemTranslate {
interface AIMenuItemTranslateProps {
language: string;
docId: string;
icon?: ReactNode;
@@ -255,7 +255,7 @@ const AIMenuItemTranslate = ({
docId,
icon,
language,
}: PropsWithChildren<AIMenuItemTranslate>) => {
}: PropsWithChildren<AIMenuItemTranslateProps>) => {
const { mutateAsync: requestAI, isPending } = useDocAITranslate();
const editor = useBlockNoteEditor();
@@ -19,10 +19,11 @@ export const ModalConfirmDownloadUnsafe = ({
isOpen
closeOnClickOutside
onClose={() => onClose()}
aria-describedby="modal-confirm-download-unsafe-title"
rightActions={
<>
<Button
aria-label={t('Close the modal')}
aria-label={t('Cancel the download')}
color="secondary"
onClick={() => onClose()}
>
@@ -31,6 +32,7 @@ export const ModalConfirmDownloadUnsafe = ({
<Button
aria-label={t('Download')}
color="danger"
data-testid="modal-download-unsafe-button"
onClick={() => {
if (onConfirm) {
void onConfirm();
@@ -45,11 +47,14 @@ export const ModalConfirmDownloadUnsafe = ({
size={ModalSize.SMALL}
title={
<Text
as="h1"
id="modal-confirm-download-unsafe-title"
$gap="0.7rem"
$size="h6"
$align="flex-start"
$variation="1000"
$direction="row"
$margin="0"
>
<Icon iconName="warning" $theme="warning" />
{t('Warning')}
@@ -7,14 +7,12 @@ import { Box } from '@/components';
interface EmojiPickerProps {
emojiData: EmojiMartData;
categories: string[];
onClickOutside: () => void;
onEmojiSelect: ({ native }: { native: string }) => void;
}
export const EmojiPicker = ({
emojiData,
categories,
onClickOutside,
onEmojiSelect,
}: EmojiPickerProps) => {
@@ -24,14 +22,11 @@ export const EmojiPicker = ({
<Box $position="absolute" $zIndex={1000} $margin="2rem 0 0 0">
<Picker
data={emojiData}
categories={categories}
locale={i18n.resolvedLanguage}
navPosition="none"
onClickOutside={onClickOutside}
onEmojiSelect={onEmojiSelect}
previewPosition="none"
skinTonePosition="none"
theme="light"
/>
</Box>
);
@@ -10,7 +10,7 @@ import { Box, BoxButton, Icon } from '@/components';
import { DocsBlockNoteEditor } from '../../types';
import { EmojiPicker } from '../EmojiPicker';
import InitEmojiCallout from './initEmojiCallout';
import emojidata from './initEmojiCallout';
export const CalloutBlock = createReactBlockSpec(
{
@@ -79,8 +79,7 @@ export const CalloutBlock = createReactBlockSpec(
{openEmojiPicker && (
<EmojiPicker
emojiData={InitEmojiCallout.emojidata}
categories={InitEmojiCallout.calloutCategories}
emojiData={emojidata}
onClickOutside={onClickOutside}
onEmojiSelect={onEmojiSelect}
/>
@@ -56,21 +56,4 @@ if (!emojidata.categories.some((c) => c.id === CALLOUT_ID)) {
void init({ data: emojidata });
const calloutCategories = [
'callout',
'people',
'nature',
'foods',
'activity',
'places',
'flags',
'objects',
'symbols',
];
const calloutEmojiData = {
emojidata,
calloutCategories,
};
export default calloutEmojiData;
export default emojidata;
@@ -1,4 +1,3 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { createReactInlineContentSpec } from '@blocknote/react';
import { TFunction } from 'i18next';
@@ -1,4 +1,3 @@
/* eslint-disable react-hooks/rules-of-hooks */
import {
PartialCustomInlineContentFromConfig,
StyleSchema,
@@ -7,6 +7,19 @@ export const cssEditor = (readonly: boolean) => css`
height: 100%;
padding-bottom: 2rem;
/**
* WCAG Accessibility contrast fixes for BlockNote editor
*/
.bn-block-content[data-is-empty-and-focused][data-content-type='paragraph']
.bn-inline-content:has(> .ProseMirror-trailingBreak:only-child)::before {
color: #767676 !important;
font-weight: 400;
}
.bn-side-menu .mantine-UnstyledButton-root svg {
color: #767676 !important;
}
img.bn-visual-media[src*='-unsafe'] {
pointer-events: none;
}
@@ -1,4 +1,3 @@
/* eslint-disable jsx-a11y/alt-text */
import { DefaultProps } from '@blocknote/core';
import { Image, Text, View } from '@react-pdf/renderer';
@@ -16,6 +16,7 @@ import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, Text } from '@/components';
import ButtonCloseModal from '@/components/modal/ButtonCloseModal';
import { useEditorStore } from '@/docs/doc-editor';
import { Doc, useTrans } from '@/docs/doc-management';
@@ -131,10 +132,12 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
isOpen
closeOnClickOutside
onClose={() => onClose()}
hideCloseButton
aria-describedby="modal-export-title"
rightActions={
<>
<Button
aria-label={t('Close the modal')}
aria-label={t('Cancel the download')}
color="secondary"
fullWidth
onClick={() => onClose()}
@@ -143,6 +146,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
{t('Cancel')}
</Button>
<Button
data-testid="doc-export-download-button"
aria-label={t('Download')}
color="primary"
fullWidth
@@ -155,9 +159,29 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
}
size={ModalSize.MEDIUM}
title={
<Text $size="h6" $variation="1000" $align="flex-start">
{t('Download')}
</Text>
<Box
$direction="row"
$justify="space-between"
$align="center"
$width="100%"
>
<Text
as="h1"
$margin="0"
id="modal-export-title"
$size="h6"
$variation="1000"
$align="flex-start"
data-testid="modal-export-title"
>
{t('Download')}
</Text>
<ButtonCloseModal
aria-label={t('Close the download modal')}
onClick={() => onClose()}
disabled={isExporting}
/>
</Box>
}
>
<Box
@@ -166,7 +190,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
$gap="1rem"
className="--docs--modal-export-content"
>
<Text $variation="600" $size="sm">
<Text $variation="600" $size="sm" as="p">
{t('Download your document in a .docx or .pdf format.')}
</Text>
<Select
@@ -1,4 +1,3 @@
/* eslint-disable jsx-a11y/alt-text */
import { Image, Link, Text } from '@react-pdf/renderer';
import DocSelectedIcon from '../assets/doc-selected.png';
@@ -1,5 +1,3 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
import { Tooltip } from '@openfun/cunningham-react';
import React, { useCallback, useEffect, useState } from 'react';
@@ -107,7 +105,7 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
}, [doc]);
return (
<Tooltip content={t('Rename')} placement="top">
<Tooltip content={t('Rename')} aria-hidden={true} placement="top">
<Box
as="span"
role="textbox"
@@ -116,7 +114,8 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label="doc title input"
aria-label={`${t('Document title')}`}
aria-multiline={false}
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
}
@@ -215,7 +215,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
>
<Button
color="tertiary"
aria-label="Share button"
aria-label={t('Share button')}
icon={
<Icon iconName="group" $theme="primary" $variation="800" />
}
@@ -233,9 +233,15 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
{!isSmallMobile && ModalExport && (
<Button
data-testid="doc-open-modal-download-button"
color="tertiary-text"
icon={
<Icon iconName="download" $theme="primary" $variation="800" />
<Icon
iconName="download"
$theme="primary"
$variation="800"
aria-hidden={true}
/>
}
onClick={() => {
setIsModalExportOpen(true);
@@ -244,8 +250,9 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
aria-label={t('Export the document')}
/>
)}
<DropdownMenu options={options}>
<DropdownMenu options={options} label={t('Open the document options')}>
<IconOptions
aria-hidden="true"
isHorizontal
$theme="primary"
$padding={{ all: 'xs' }}
@@ -261,7 +268,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
`
: ''}
`}
aria-label={t('Open the document options')}
/>
</DropdownMenu>
</Box>
@@ -65,24 +65,20 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) {
return useMutation<DuplicateDocResponse, APIError, DuplicateDocParams>({
mutationFn: async (variables) => {
try {
// Save the document if we can first, to ensure the latest state is duplicated
if (
variables.canSave &&
provider &&
provider.document.guid === variables.docId
) {
await updateDoc({
id: variables.docId,
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
});
}
// Save the document if we can first, to ensure the latest state is duplicated
const canSave =
variables.canSave &&
provider &&
provider.document.guid === variables.docId;
return await duplicateDoc(variables);
} catch (error) {
// If save fails, throw the error to prevent duplication
throw error;
if (canSave) {
await updateDoc({
id: variables.docId,
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
});
}
return await duplicateDoc(variables);
},
onSuccess: (data, variables, context) => {
void queryClient.resetQueries({
@@ -6,8 +6,8 @@ import { APIError, errorCauses, fetchAPI } from '@/api';
import { Doc, KEY_DOC } from '@/docs/doc-management';
import { useBroadcastStore } from '@/stores';
export type UpdateDocLinkParams = Pick<Doc, 'id'> &
Partial<Pick<Doc, 'link_role' | 'link_reach'>>;
export type UpdateDocLinkParams = Pick<Doc, 'id' | 'link_reach'> &
Partial<Pick<Doc, 'link_role'>>;
export const updateDocLink = async ({
id,
@@ -10,6 +10,7 @@ import { useRouter } from 'next/router';
import { Trans, useTranslation } from 'react-i18next';
import { Box, Text, TextErrors } from '@/components';
import ButtonCloseModal from '@/components/modal/ButtonCloseModal';
import { useRemoveDoc } from '../api/useRemoveDoc';
import { Doc } from '../types';
@@ -53,11 +54,13 @@ export const ModalRemoveDoc = ({
<Modal
isOpen
closeOnClickOutside
hideCloseButton
onClose={() => onClose()}
aria-describedby="modal-remove-doc-title"
rightActions={
<>
<Button
aria-label={t('Close the modal')}
aria-label={t('Cancel the deletion')}
color="secondary"
fullWidth
onClick={() => onClose()}
@@ -65,7 +68,7 @@ export const ModalRemoveDoc = ({
{t('Cancel')}
</Button>
<Button
aria-label={t('Confirm deletion')}
aria-label={t('Delete document')}
color="danger"
fullWidth
onClick={() =>
@@ -80,23 +83,32 @@ export const ModalRemoveDoc = ({
}
size={ModalSize.MEDIUM}
title={
<Text
$size="h6"
as="h6"
$margin={{ all: '0' }}
$align="flex-start"
$variation="1000"
<Box
$direction="row"
$justify="space-between"
$align="center"
$width="100%"
>
{t('Delete a doc')}
</Text>
<Text
$size="h6"
as="h1"
id="modal-remove-doc-title"
$margin="0"
$align="flex-start"
$variation="1000"
>
{t('Delete a doc')}
</Text>
<ButtonCloseModal
aria-label={t('Close the delete modal')}
onClick={() => onClose()}
/>
</Box>
}
>
<Box
aria-label={t('Content modal to delete document')}
className="--docs--modal-remove-doc"
>
<Box className="--docs--modal-remove-doc">
{!isError && (
<Text $size="sm" $variation="600" $display="inline-block">
<Text $size="sm" $variation="600" $display="inline-block" as="p">
<Trans t={t}>
This document and <strong>any sub-documents</strong> will be
permanently deleted. This action is irreversible.
@@ -82,12 +82,11 @@ export const SimpleDocItem = ({
</Box>
<Box $justify="center" $overflow="auto">
<Text
aria-describedby="doc-title"
aria-label={displayTitle}
$size="sm"
$variation="1000"
$weight="500"
$css={ItemTextCss}
data-testid="doc-title"
>
{displayTitle}
</Text>
@@ -5,7 +5,8 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDebouncedCallback } from 'use-debounce';
import { Box } from '@/components';
import { Box, Text } from '@/components';
import ButtonCloseModal from '@/components/modal/ButtonCloseModal';
import { QuickSearch } from '@/components/quick-search';
import { Doc, useDocUtils } from '@/docs/doc-management';
import { useResponsiveStore } from '@/stores';
@@ -63,6 +64,8 @@ const DocSearchModalGlobal = ({
{...modalProps}
closeOnClickOutside
size={isDesktop ? ModalSize.LARGE : ModalSize.FULL}
hideCloseButton
aria-describedby="doc-search-modal-title"
>
<Box
aria-label={t('Search modal')}
@@ -70,6 +73,22 @@ const DocSearchModalGlobal = ({
$justify="space-between"
className="--docs--doc-search-modal"
>
<Text
as="h1"
$margin="0"
id="doc-search-modal-title"
className="sr-only"
>
{t('Search docs')}
</Text>
<Box $position="absolute" $css="top: 12px; right: 12px;">
<ButtonCloseModal
aria-label={t('Close the search modal')}
onClick={modalProps.onClose}
size="small"
color="primary-text"
/>
</Box>
<QuickSearch
placeholder={t('Type the name of a document')}
loading={loading}

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