Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6be87ed477 | |||
| c96182b3e3 | |||
| e79d1d618a | |||
| 2691cdd4a2 | |||
| 05a1390bdc | |||
| dfe8ae14fe | |||
| 74165f6890 | |||
| 349cbf8eb3 | |||
| 12ef1a2450 | |||
| 9b2f7966f6 | |||
| 5ad30b404d | |||
| 12524f35b7 | |||
| f8a40cf8cc | |||
| c32fdb67ac | |||
| 7f2a21cdc9 | |||
| 4ad917906c | |||
| 9ca79688c9 | |||
| 7f0eb9117e | |||
| 2557c6bc77 | |||
| df173c3ce6 | |||
| b58c991c81 | |||
| 96f6aeea60 | |||
| 9465f1a6ec | |||
| 98f11ff8ac | |||
| b29daa2d77 | |||
| 5cdbdbf215 | |||
| 5268699d50 | |||
| cdafe6fd33 | |||
| 4307b4f433 | |||
| 3bf33d202a | |||
| 101cef7d70 | |||
| 419079ac69 |
+30
-2
@@ -8,10 +8,37 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [3.2.1] - 2025-05-06
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- 🐛(frontend) fix list copy paste #943
|
||||||
|
|
||||||
|
## [3.2.0] - 2025-05-05
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
- 🚸(backend) make document search on title accent-insensitive #874
|
||||||
- 🚩 add homepage feature flag #861
|
- 🚩 add homepage feature flag #861
|
||||||
|
- ✨(settings) Allow configuring PKCE for the SSO #886
|
||||||
|
- 🌐(i18n) activate chinese and spanish languages #884
|
||||||
|
- 🔧(backend) allow overwriting the data directory #893
|
||||||
|
- ➕(backend) add `django-lasuite` dependency #839
|
||||||
|
- ✨(frontend) advanced table features #908
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- ⚡️(frontend) reduce unblocking time for config #867
|
||||||
|
- ♻️(frontend) bind UI with ability access #900
|
||||||
|
- ♻️(frontend) use built-in Quote block #908
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- 🐛(nginx) fix 404 when accessing a doc #866
|
||||||
|
- 🔒️(drf) disable browsable HTML API renderer #919
|
||||||
|
- 🔒(frontend) enhance file download security #889
|
||||||
|
- 🐛(backend) race condition create doc #633
|
||||||
|
- 🐛(frontend) fix breaklines in custom blocks #908
|
||||||
|
|
||||||
## [3.1.0] - 2025-04-07
|
## [3.1.0] - 2025-04-07
|
||||||
|
|
||||||
@@ -140,7 +167,6 @@ and this project adheres to
|
|||||||
- ♻️(frontend) improve table pdf rendering
|
- ♻️(frontend) improve table pdf rendering
|
||||||
- 🐛(email) invitation emails in receivers language
|
- 🐛(email) invitation emails in receivers language
|
||||||
|
|
||||||
|
|
||||||
## [2.2.0] - 2025-02-10
|
## [2.2.0] - 2025-02-10
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
@@ -529,7 +555,9 @@ and this project adheres to
|
|||||||
- ✨(frontend) Coming Soon page (#67)
|
- ✨(frontend) Coming Soon page (#67)
|
||||||
- 🚀 Impress, project to manage your documents easily and collaboratively.
|
- 🚀 Impress, project to manage your documents easily and collaboratively.
|
||||||
|
|
||||||
[unreleased]: https://github.com/numerique-gouv/impress/compare/v3.1.0...main
|
[unreleased]: https://github.com/numerique-gouv/impress/compare/v3.2.1...main
|
||||||
|
[v3.2.1]: https://github.com/numerique-gouv/impress/releases/v3.2.1
|
||||||
|
[v3.2.0]: https://github.com/numerique-gouv/impress/releases/v3.2.0
|
||||||
[v3.1.0]: https://github.com/numerique-gouv/impress/releases/v3.1.0
|
[v3.1.0]: https://github.com/numerique-gouv/impress/releases/v3.1.0
|
||||||
[v3.0.0]: https://github.com/numerique-gouv/impress/releases/v3.0.0
|
[v3.0.0]: https://github.com/numerique-gouv/impress/releases/v3.0.0
|
||||||
[v2.6.0]: https://github.com/numerique-gouv/impress/releases/v2.6.0
|
[v2.6.0]: https://github.com/numerique-gouv/impress/releases/v2.6.0
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ Welcome to Docs! The open source document editor where your notes can become kno
|
|||||||
|
|
||||||
## Why use Docs ❓
|
## Why use Docs ❓
|
||||||
|
|
||||||
⚠️ **Note that Docs provides docs/pdf exporters by loading [two BlockNote packages](https://github.com/suitenumerique/docs/blob/main/src/frontend/apps/impress/package.json#L22C7-L23C53), which we use under the AGPL-3.0 licence. Until we comply with the terms of this license, we recommend that you don't run Docs as a commercial product, unless you are willing to sponsor [BlockNote](https://github.com/TypeCellOS/BlockNote).**
|
|
||||||
|
|
||||||
Docs is a collaborative text editor designed to address common challenges in knowledge building and sharing.
|
Docs is a collaborative text editor designed to address common challenges in knowledge building and sharing.
|
||||||
|
|
||||||
### Write
|
### Write
|
||||||
@@ -39,11 +37,13 @@ Docs is a collaborative text editor designed to address common challenges in kno
|
|||||||
* 🤝 Collaborate with your team in real time
|
* 🤝 Collaborate with your team in real time
|
||||||
* 🔒 Granular access control to ensure your information is secure and only shared with the right people
|
* 🔒 Granular access control to ensure your information is secure and only shared with the right people
|
||||||
* 📑 Professional document exports in multiple formats (.odt, .doc, .pdf) with customizable templates
|
* 📑 Professional document exports in multiple formats (.odt, .doc, .pdf) with customizable templates
|
||||||
* 📚 Built-in wiki functionality to turn your team's collaborative work into organized knowledge `ETA 02/2025`
|
* 📚 Built-in wiki functionality to turn your team's collaborative work into organized knowledge `ETA 05/2025`
|
||||||
|
|
||||||
### Self-host
|
### Self-host
|
||||||
* 🚀 Easy to install, scalable and secure alternative to Notion, Outline or Confluence
|
* 🚀 Easy to install, scalable and secure alternative to Notion, Outline or Confluence
|
||||||
|
|
||||||
|
⚠️ For the PDF and Docx export Docs relies on XL packages from BlockNote licenced in AGPL-3.0. Please make sure you fulfill your obligations regarding BlockNote licensing (see https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-pdf-exporter/LICENSE and https://www.blocknotejs.org/about#partner-with-us).
|
||||||
|
|
||||||
## Getting started 🔧
|
## Getting started 🔧
|
||||||
|
|
||||||
### Test it
|
### Test it
|
||||||
@@ -118,6 +118,7 @@ $ make run-backend
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Adding content**
|
**Adding content**
|
||||||
|
|
||||||
You can create a basic demo site by running:
|
You can create a basic demo site by running:
|
||||||
|
|
||||||
```shellscript
|
```shellscript
|
||||||
|
|||||||
@@ -185,15 +185,11 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: ./src/frontend/servers/y-provider/Dockerfile
|
dockerfile: ./src/frontend/servers/y-provider/Dockerfile
|
||||||
target: y-provider
|
target: y-provider
|
||||||
command: ["yarn", "workspace", "server-y-provider", "run", "dev"]
|
|
||||||
working_dir: /app/frontend
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- env.d/development/common
|
- env.d/development/common
|
||||||
ports:
|
ports:
|
||||||
- "4444:4444"
|
- "4444:4444"
|
||||||
volumes:
|
|
||||||
- ./src/frontend/:/app/frontend
|
|
||||||
|
|
||||||
kc_postgresql:
|
kc_postgresql:
|
||||||
image: postgres:14.3
|
image: postgres:14.3
|
||||||
|
|||||||
+3
-2
@@ -78,13 +78,14 @@ These are the environmental variables you can set for the impress-backend contai
|
|||||||
| OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION | faillback to email for identification | true |
|
| OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION | faillback to email for identification | true |
|
||||||
| OIDC_ALLOW_DUPLICATE_EMAILS | Allow dupplicate emails | false |
|
| OIDC_ALLOW_DUPLICATE_EMAILS | Allow dupplicate emails | false |
|
||||||
| USER_OIDC_ESSENTIAL_CLAIMS | essential claims in OIDC token | [] |
|
| USER_OIDC_ESSENTIAL_CLAIMS | essential claims in OIDC token | [] |
|
||||||
| USER_OIDC_FIELDS_TO_FULLNAME | OIDC token claims to create full name | ["first_name", "last_name"] |
|
| OIDC_USERINFO_FULLNAME_FIELDS | OIDC token claims to create full name | ["first_name", "last_name"] |
|
||||||
| USER_OIDC_FIELD_TO_SHORTNAME | OIDC token claims to create shortname | first_name |
|
| OIDC_USERINFO_SHORTNAME_FIELD | OIDC token claims to create shortname | first_name |
|
||||||
| ALLOW_LOGOUT_GET_METHOD | Allow get logout method | true |
|
| ALLOW_LOGOUT_GET_METHOD | Allow get logout method | true |
|
||||||
| AI_API_KEY | AI key to be used for AI Base url | |
|
| AI_API_KEY | AI key to be used for AI Base url | |
|
||||||
| AI_BASE_URL | OpenAI compatible AI base url | |
|
| AI_BASE_URL | OpenAI compatible AI base url | |
|
||||||
| AI_MODEL | AI Model to use | |
|
| AI_MODEL | AI Model to use | |
|
||||||
| AI_ALLOW_REACH_FROM | Users that can use AI must be this level. options are "public", "authenticated", "restricted" | authenticated |
|
| AI_ALLOW_REACH_FROM | Users that can use AI must be this level. options are "public", "authenticated", "restricted" | authenticated |
|
||||||
|
| AI_FEATURE_ENABLED | Enable AI options | false |
|
||||||
| Y_PROVIDER_API_KEY | Y provider API key | |
|
| Y_PROVIDER_API_KEY | Y provider API key | |
|
||||||
| Y_PROVIDER_API_BASE_URL | Y Provider url | |
|
| Y_PROVIDER_API_BASE_URL | Y Provider url | |
|
||||||
| CONVERSION_API_ENDPOINT | Conversion API endpoint | convert-markdown |
|
| CONVERSION_API_ENDPOINT | Conversion API endpoint | convert-markdown |
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ backend:
|
|||||||
OIDC_RP_SIGN_ALGO: RS256
|
OIDC_RP_SIGN_ALGO: RS256
|
||||||
OIDC_RP_SCOPES: "openid email"
|
OIDC_RP_SCOPES: "openid email"
|
||||||
OIDC_VERIFY_SSL: False
|
OIDC_VERIFY_SSL: False
|
||||||
USER_OIDC_FIELD_TO_SHORTNAME: "given_name"
|
OIDC_USERINFO_SHORTNAME_FIELD: "given_name"
|
||||||
USER_OIDC_FIELDS_TO_FULLNAME: "given_name,usual_name"
|
OIDC_USERINFO_FULLNAME_FIELDS: "given_name,usual_name"
|
||||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://impress.127.0.0.1.nip.io
|
OIDC_REDIRECT_ALLOWED_HOSTS: https://impress.127.0.0.1.nip.io
|
||||||
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
||||||
LOGIN_REDIRECT_URL: https://impress.127.0.0.1.nip.io
|
LOGIN_REDIRECT_URL: https://impress.127.0.0.1.nip.io
|
||||||
|
|||||||
@@ -64,6 +64,5 @@ COLLABORATION_WS_URL=ws://localhost:4444/collaboration/ws/
|
|||||||
|
|
||||||
# Frontend
|
# Frontend
|
||||||
FRONTEND_THEME=default
|
FRONTEND_THEME=default
|
||||||
FRONTEND_HOMEPAGE_FEATURE_ENABLED=True
|
|
||||||
FRONTEND_FOOTER_FEATURE_ENABLED=True
|
FRONTEND_FOOTER_FEATURE_ENABLED=True
|
||||||
FRONTEND_URL_JSON_FOOTER=http://frontend:3000/contents/footer-demo.json
|
FRONTEND_URL_JSON_FOOTER=http://frontend:3000/contents/footer-demo.json
|
||||||
|
|||||||
@@ -9,6 +9,18 @@
|
|||||||
"matchManagers": ["pep621"],
|
"matchManagers": ["pep621"],
|
||||||
"matchPackageNames": []
|
"matchPackageNames": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"groupName": "allowed django versions",
|
||||||
|
"matchManagers": ["pep621"],
|
||||||
|
"matchPackageNames": ["Django"],
|
||||||
|
"allowedVersions": "<5.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "allowed redis versions",
|
||||||
|
"matchManagers": ["pep621"],
|
||||||
|
"matchPackageNames": ["redis"],
|
||||||
|
"allowedVersions": "<6.0.0"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"groupName": "ignored js dependencies",
|
"groupName": "ignored js dependencies",
|
||||||
@@ -16,6 +28,7 @@
|
|||||||
"matchPackageNames": [
|
"matchPackageNames": [
|
||||||
"eslint",
|
"eslint",
|
||||||
"fetch-mock",
|
"fetch-mock",
|
||||||
|
"prosemirror-model",
|
||||||
"node",
|
"node",
|
||||||
"node-fetch",
|
"node-fetch",
|
||||||
"workbox-webpack-plugin"
|
"workbox-webpack-plugin"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"""API filters for Impress' core application."""
|
"""API filters for Impress' core application."""
|
||||||
|
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
import django_filters
|
import django_filters
|
||||||
@@ -7,13 +9,42 @@ import django_filters
|
|||||||
from core import models
|
from core import models
|
||||||
|
|
||||||
|
|
||||||
class DocumentFilter(django_filters.FilterSet):
|
def remove_accents(value):
|
||||||
|
"""Remove accents from a string (vélo -> velo)."""
|
||||||
|
return "".join(
|
||||||
|
c
|
||||||
|
for c in unicodedata.normalize("NFD", value)
|
||||||
|
if unicodedata.category(c) != "Mn"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AccentInsensitiveCharFilter(django_filters.CharFilter):
|
||||||
"""
|
"""
|
||||||
Custom filter for filtering documents.
|
A custom CharFilter that filters on the accent-insensitive value searched.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
title = django_filters.CharFilter(
|
def filter(self, qs, value):
|
||||||
field_name="title", lookup_expr="icontains", label=_("Title")
|
"""
|
||||||
|
Apply the filter to the queryset using the unaccented version of the field.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs: The queryset to filter.
|
||||||
|
value: The value to search for in the unaccented field.
|
||||||
|
Returns:
|
||||||
|
A filtered queryset.
|
||||||
|
"""
|
||||||
|
if value:
|
||||||
|
value = remove_accents(value)
|
||||||
|
return super().filter(qs, value)
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentFilter(django_filters.FilterSet):
|
||||||
|
"""
|
||||||
|
Custom filter for filtering documents on title (accent and case insensitive).
|
||||||
|
"""
|
||||||
|
|
||||||
|
title = AccentInsensitiveCharFilter(
|
||||||
|
field_name="title", lookup_expr="unaccent__icontains", label=_("Title")
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ from django.contrib.postgres.fields import ArrayField
|
|||||||
from django.contrib.postgres.search import TrigramSimilarity
|
from django.contrib.postgres.search import TrigramSimilarity
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
|
from django.db import connection, transaction
|
||||||
from django.db import models as db
|
from django.db import models as db
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models.expressions import RawSQL
|
from django.db.models.expressions import RawSQL
|
||||||
from django.db.models.functions import Left, Length
|
from django.db.models.functions import Left, Length
|
||||||
from django.http import Http404, StreamingHttpResponse
|
from django.http import Http404, StreamingHttpResponse
|
||||||
@@ -607,6 +607,14 @@ class DocumentViewSet(
|
|||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
"""Set the current user as creator and owner of the newly created object."""
|
"""Set the current user as creator and owner of the newly created object."""
|
||||||
|
|
||||||
|
# locks the table to ensure safe concurrent access
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
f'LOCK TABLE "{models.Document._meta.db_table}" ' # noqa: SLF001
|
||||||
|
"IN SHARE ROW EXCLUSIVE MODE;"
|
||||||
|
)
|
||||||
|
|
||||||
obj = models.Document.add_root(
|
obj = models.Document.add_root(
|
||||||
creator=self.request.user,
|
creator=self.request.user,
|
||||||
**serializer.validated_data,
|
**serializer.validated_data,
|
||||||
@@ -666,10 +674,19 @@ class DocumentViewSet(
|
|||||||
permission_classes=[],
|
permission_classes=[],
|
||||||
url_path="create-for-owner",
|
url_path="create-for-owner",
|
||||||
)
|
)
|
||||||
|
@transaction.atomic
|
||||||
def create_for_owner(self, request):
|
def create_for_owner(self, request):
|
||||||
"""
|
"""
|
||||||
Create a document on behalf of a specified owner (pre-existing user or invited).
|
Create a document on behalf of a specified owner (pre-existing user or invited).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# locks the table to ensure safe concurrent access
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
f'LOCK TABLE "{models.Document._meta.db_table}" ' # noqa: SLF001
|
||||||
|
"IN SHARE ROW EXCLUSIVE MODE;"
|
||||||
|
)
|
||||||
|
|
||||||
# Deserialize and validate the data
|
# Deserialize and validate the data
|
||||||
serializer = serializers.ServerCreateDocumentSerializer(data=request.data)
|
serializer = serializers.ServerCreateDocumentSerializer(data=request.data)
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
@@ -775,7 +792,12 @@ class DocumentViewSet(
|
|||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
child_document = document.add_child(
|
# "select_for_update" locks the table to ensure safe concurrent access
|
||||||
|
locked_parent = models.Document.objects.select_for_update().get(
|
||||||
|
pk=document.pk
|
||||||
|
)
|
||||||
|
|
||||||
|
child_document = locked_parent.add_child(
|
||||||
creator=request.user,
|
creator=request.user,
|
||||||
**serializer.validated_data,
|
**serializer.validated_data,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,130 +1,59 @@
|
|||||||
"""Authentication Backends for the Impress core app."""
|
"""Authentication Backends for the Impress core app."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
import requests
|
from lasuite.oidc_login.backends import (
|
||||||
from mozilla_django_oidc.auth import (
|
OIDCAuthenticationBackend as LaSuiteOIDCAuthenticationBackend,
|
||||||
OIDCAuthenticationBackend as MozillaOIDCAuthenticationBackend,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from core.models import DuplicateEmailError, User
|
from core.models import DuplicateEmailError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Settings renamed warnings
|
||||||
|
if os.environ.get("USER_OIDC_FIELDS_TO_FULLNAME"):
|
||||||
|
logger.warning(
|
||||||
|
"USER_OIDC_FIELDS_TO_FULLNAME has been renamed to "
|
||||||
|
"OIDC_USERINFO_FULLNAME_FIELDS please update your settings."
|
||||||
|
)
|
||||||
|
|
||||||
class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
|
if os.environ.get("USER_OIDC_FIELD_TO_SHORTNAME"):
|
||||||
|
logger.warning(
|
||||||
|
"USER_OIDC_FIELD_TO_SHORTNAME has been renamed to "
|
||||||
|
"OIDC_USERINFO_SHORTNAME_FIELD please update your settings."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OIDCAuthenticationBackend(LaSuiteOIDCAuthenticationBackend):
|
||||||
"""Custom OpenID Connect (OIDC) Authentication Backend.
|
"""Custom OpenID Connect (OIDC) Authentication Backend.
|
||||||
|
|
||||||
This class overrides the default OIDC Authentication Backend to accommodate differences
|
This class overrides the default OIDC Authentication Backend to accommodate differences
|
||||||
in the User and Identity models, and handles signed and/or encrypted UserInfo response.
|
in the User and Identity models, and handles signed and/or encrypted UserInfo response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_userinfo(self, access_token, id_token, payload):
|
def get_extra_claims(self, user_info):
|
||||||
"""Return user details dictionary.
|
"""
|
||||||
|
Return extra claims from user_info.
|
||||||
|
|
||||||
Parameters:
|
Args:
|
||||||
- access_token (str): The access token.
|
user_info (dict): The user information dictionary.
|
||||||
- id_token (str): The id token (unused).
|
|
||||||
- payload (dict): The token payload (unused).
|
|
||||||
|
|
||||||
Note: The id_token and payload parameters are unused in this implementation,
|
|
||||||
but were kept to preserve base method signature.
|
|
||||||
|
|
||||||
Note: It handles signed and/or encrypted UserInfo Response. It is required by
|
|
||||||
Agent Connect, which follows the OIDC standard. It forces us to override the
|
|
||||||
base method, which deal with 'application/json' response.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
- dict: User details dictionary obtained from the OpenID Connect user endpoint.
|
dict: A dictionary of extra claims.
|
||||||
"""
|
"""
|
||||||
|
return {
|
||||||
user_response = requests.get(
|
"full_name": self.compute_full_name(user_info),
|
||||||
self.OIDC_OP_USER_ENDPOINT,
|
"short_name": user_info.get(settings.OIDC_USERINFO_SHORTNAME_FIELD),
|
||||||
headers={"Authorization": f"Bearer {access_token}"},
|
|
||||||
verify=self.get_settings("OIDC_VERIFY_SSL", True),
|
|
||||||
timeout=self.get_settings("OIDC_TIMEOUT", None),
|
|
||||||
proxies=self.get_settings("OIDC_PROXY", None),
|
|
||||||
)
|
|
||||||
user_response.raise_for_status()
|
|
||||||
|
|
||||||
try:
|
|
||||||
userinfo = user_response.json()
|
|
||||||
except ValueError:
|
|
||||||
try:
|
|
||||||
userinfo = self.verify_token(user_response.text)
|
|
||||||
except Exception as e:
|
|
||||||
raise SuspiciousOperation(
|
|
||||||
_("Invalid response format or token verification failed")
|
|
||||||
) from e
|
|
||||||
|
|
||||||
return userinfo
|
|
||||||
|
|
||||||
def verify_claims(self, claims):
|
|
||||||
"""
|
|
||||||
Verify the presence of essential claims and the "sub" (which is mandatory as defined
|
|
||||||
by the OIDC specification) to decide if authentication should be allowed.
|
|
||||||
"""
|
|
||||||
essential_claims = settings.USER_OIDC_ESSENTIAL_CLAIMS
|
|
||||||
missing_claims = [claim for claim in essential_claims if claim not in claims]
|
|
||||||
|
|
||||||
if missing_claims:
|
|
||||||
logger.error("Missing essential claims: %s", missing_claims)
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_or_create_user(self, access_token, id_token, payload):
|
|
||||||
"""Return a User based on userinfo. Create a new user if no match is found."""
|
|
||||||
|
|
||||||
user_info = self.get_userinfo(access_token, id_token, payload)
|
|
||||||
|
|
||||||
if not self.verify_claims(user_info):
|
|
||||||
raise SuspiciousOperation("Claims verification failed.")
|
|
||||||
|
|
||||||
sub = user_info["sub"]
|
|
||||||
email = user_info.get("email")
|
|
||||||
|
|
||||||
# Get user's full name from OIDC fields defined in settings
|
|
||||||
full_name = self.compute_full_name(user_info)
|
|
||||||
short_name = user_info.get(settings.USER_OIDC_FIELD_TO_SHORTNAME)
|
|
||||||
|
|
||||||
claims = {
|
|
||||||
"email": email,
|
|
||||||
"full_name": full_name,
|
|
||||||
"short_name": short_name,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_existing_user(self, sub, email):
|
||||||
|
"""Fetch existing user by sub or email."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = User.objects.get_user_by_sub_or_email(sub, email)
|
return self.UserModel.objects.get_user_by_sub_or_email(sub, email)
|
||||||
except DuplicateEmailError as err:
|
except DuplicateEmailError as err:
|
||||||
raise SuspiciousOperation(err.message) from err
|
raise SuspiciousOperation(err.message) from err
|
||||||
|
|
||||||
if user:
|
|
||||||
if not user.is_active:
|
|
||||||
raise SuspiciousOperation(_("User account is disabled"))
|
|
||||||
self.update_user_if_needed(user, claims)
|
|
||||||
elif self.get_settings("OIDC_CREATE_USER", True):
|
|
||||||
user = User.objects.create(sub=sub, password="!", **claims) # noqa: S106
|
|
||||||
|
|
||||||
return user
|
|
||||||
|
|
||||||
def compute_full_name(self, user_info):
|
|
||||||
"""Compute user's full name based on OIDC fields in settings."""
|
|
||||||
name_fields = settings.USER_OIDC_FIELDS_TO_FULLNAME
|
|
||||||
full_name = " ".join(
|
|
||||||
user_info[field] for field in name_fields if user_info.get(field)
|
|
||||||
)
|
|
||||||
return full_name or None
|
|
||||||
|
|
||||||
def update_user_if_needed(self, user, claims):
|
|
||||||
"""Update user claims if they have changed."""
|
|
||||||
has_changed = any(
|
|
||||||
value and value != getattr(user, key) for key, value in claims.items()
|
|
||||||
)
|
|
||||||
if has_changed:
|
|
||||||
updated_claims = {key: value for key, value in claims.items() if value}
|
|
||||||
self.UserModel.objects.filter(id=user.id).update(**updated_claims)
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
"""Authentication URLs for the People core app."""
|
|
||||||
|
|
||||||
from django.urls import path
|
|
||||||
|
|
||||||
from mozilla_django_oidc.urls import urlpatterns as mozzila_oidc_urls
|
|
||||||
|
|
||||||
from .views import OIDCLogoutCallbackView, OIDCLogoutView
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
# Override the default 'logout/' path from Mozilla Django OIDC with our custom view.
|
|
||||||
path("logout/", OIDCLogoutView.as_view(), name="oidc_logout_custom"),
|
|
||||||
path(
|
|
||||||
"logout-callback/",
|
|
||||||
OIDCLogoutCallbackView.as_view(),
|
|
||||||
name="oidc_logout_callback",
|
|
||||||
),
|
|
||||||
*mozzila_oidc_urls,
|
|
||||||
]
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
"""Authentication Views for the People core app."""
|
|
||||||
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
|
|
||||||
from django.contrib import auth
|
|
||||||
from django.core.exceptions import SuspiciousOperation
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import crypto
|
|
||||||
|
|
||||||
from mozilla_django_oidc.utils import (
|
|
||||||
absolutify,
|
|
||||||
)
|
|
||||||
from mozilla_django_oidc.views import (
|
|
||||||
OIDCLogoutView as MozillaOIDCOIDCLogoutView,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OIDCLogoutView(MozillaOIDCOIDCLogoutView):
|
|
||||||
"""Custom logout view for handling OpenID Connect (OIDC) logout flow.
|
|
||||||
|
|
||||||
Adds support for handling logout callbacks from the identity provider (OP)
|
|
||||||
by initiating the logout flow if the user has an active session.
|
|
||||||
|
|
||||||
The Django session is retained during the logout process to persist the 'state' OIDC parameter.
|
|
||||||
This parameter is crucial for maintaining the integrity of the logout flow between this call
|
|
||||||
and the subsequent callback.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def persist_state(request, state):
|
|
||||||
"""Persist the given 'state' parameter in the session's 'oidc_states' dictionary
|
|
||||||
|
|
||||||
This method is used to store the OIDC state parameter in the session, according to the
|
|
||||||
structure expected by Mozilla Django OIDC's 'add_state_and_verifier_and_nonce_to_session'
|
|
||||||
utility function.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if "oidc_states" not in request.session or not isinstance(
|
|
||||||
request.session["oidc_states"], dict
|
|
||||||
):
|
|
||||||
request.session["oidc_states"] = {}
|
|
||||||
|
|
||||||
request.session["oidc_states"][state] = {}
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
def construct_oidc_logout_url(self, request):
|
|
||||||
"""Create the redirect URL for interfacing with the OIDC provider.
|
|
||||||
|
|
||||||
Retrieves the necessary parameters from the session and constructs the URL
|
|
||||||
required to initiate logout with the OpenID Connect provider.
|
|
||||||
|
|
||||||
If no ID token is found in the session, the logout flow will not be initiated,
|
|
||||||
and the method will return the default redirect URL.
|
|
||||||
|
|
||||||
The 'state' parameter is generated randomly and persisted in the session to ensure
|
|
||||||
its integrity during the subsequent callback.
|
|
||||||
"""
|
|
||||||
|
|
||||||
oidc_logout_endpoint = self.get_settings("OIDC_OP_LOGOUT_ENDPOINT")
|
|
||||||
|
|
||||||
if not oidc_logout_endpoint:
|
|
||||||
return self.redirect_url
|
|
||||||
|
|
||||||
reverse_url = reverse("oidc_logout_callback")
|
|
||||||
id_token = request.session.get("oidc_id_token", None)
|
|
||||||
|
|
||||||
if not id_token:
|
|
||||||
return self.redirect_url
|
|
||||||
|
|
||||||
query = {
|
|
||||||
"id_token_hint": id_token,
|
|
||||||
"state": crypto.get_random_string(self.get_settings("OIDC_STATE_SIZE", 32)),
|
|
||||||
"post_logout_redirect_uri": absolutify(request, reverse_url),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.persist_state(request, query["state"])
|
|
||||||
|
|
||||||
return f"{oidc_logout_endpoint}?{urlencode(query)}"
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
"""Handle user logout.
|
|
||||||
|
|
||||||
If the user is not authenticated, redirects to the default logout URL.
|
|
||||||
Otherwise, constructs the OIDC logout URL and redirects the user to start
|
|
||||||
the logout process.
|
|
||||||
|
|
||||||
If the user is redirected to the default logout URL, ensure her Django session
|
|
||||||
is terminated.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logout_url = self.redirect_url
|
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
|
||||||
logout_url = self.construct_oidc_logout_url(request)
|
|
||||||
|
|
||||||
# If the user is not redirected to the OIDC provider, ensure logout
|
|
||||||
if logout_url == self.redirect_url:
|
|
||||||
auth.logout(request)
|
|
||||||
|
|
||||||
return HttpResponseRedirect(logout_url)
|
|
||||||
|
|
||||||
|
|
||||||
class OIDCLogoutCallbackView(MozillaOIDCOIDCLogoutView):
|
|
||||||
"""Custom view for handling the logout callback from the OpenID Connect (OIDC) provider.
|
|
||||||
|
|
||||||
Handles the callback after logout from the identity provider (OP).
|
|
||||||
Verifies the state parameter and performs necessary logout actions.
|
|
||||||
|
|
||||||
The Django session is maintained during the logout process to ensure the integrity
|
|
||||||
of the logout flow initiated in the previous step.
|
|
||||||
"""
|
|
||||||
|
|
||||||
http_method_names = ["get"]
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
"""Handle the logout callback.
|
|
||||||
|
|
||||||
If the user is not authenticated, redirects to the default logout URL.
|
|
||||||
Otherwise, verifies the state parameter and performs necessary logout actions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not request.user.is_authenticated:
|
|
||||||
return HttpResponseRedirect(self.redirect_url)
|
|
||||||
|
|
||||||
state = request.GET.get("state")
|
|
||||||
|
|
||||||
if state not in request.session.get("oidc_states", {}):
|
|
||||||
msg = "OIDC callback state not found in session `oidc_states`!"
|
|
||||||
raise SuspiciousOperation(msg)
|
|
||||||
|
|
||||||
del request.session["oidc_states"][state]
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
auth.logout(request)
|
|
||||||
|
|
||||||
return HttpResponseRedirect(self.redirect_url)
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from django.contrib.postgres.operations import UnaccentExtension
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("core", "0020_remove_is_public_add_field_attachments_and_duplicated_from"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [UnaccentExtension()]
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
from logging import Logger
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import responses
|
import responses
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
from lasuite.oidc_login.backends import get_oidc_refresh_token
|
||||||
|
|
||||||
from core import models
|
from core import models
|
||||||
from core.authentication.backends import OIDCAuthenticationBackend
|
from core.authentication.backends import OIDCAuthenticationBackend
|
||||||
@@ -57,7 +57,7 @@ def test_authentication_getter_existing_user_via_email(
|
|||||||
|
|
||||||
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
||||||
|
|
||||||
with django_assert_num_queries(2):
|
with django_assert_num_queries(3): # user by sub, user by mail, update sub
|
||||||
user = klass.get_or_create_user(
|
user = klass.get_or_create_user(
|
||||||
access_token="test-token", id_token=None, payload=None
|
access_token="test-token", id_token=None, payload=None
|
||||||
)
|
)
|
||||||
@@ -288,7 +288,7 @@ def test_authentication_getter_new_user_no_email(monkeypatch):
|
|||||||
assert user.email is None
|
assert user.email is None
|
||||||
assert user.full_name is None
|
assert user.full_name is None
|
||||||
assert user.short_name is None
|
assert user.short_name is None
|
||||||
assert user.password == "!"
|
assert user.has_usable_password() is False
|
||||||
assert models.User.objects.count() == 1
|
assert models.User.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ def test_authentication_getter_new_user_with_email(monkeypatch):
|
|||||||
assert user.email == email
|
assert user.email == email
|
||||||
assert user.full_name == "John Doe"
|
assert user.full_name == "John Doe"
|
||||||
assert user.short_name == "John"
|
assert user.short_name == "John"
|
||||||
assert user.password == "!"
|
assert user.has_usable_password() is False
|
||||||
assert models.User.objects.count() == 1
|
assert models.User.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
@@ -345,11 +345,15 @@ def test_authentication_get_userinfo_json_response():
|
|||||||
|
|
||||||
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_authentication_get_userinfo_token_response(monkeypatch):
|
def test_authentication_get_userinfo_token_response(monkeypatch, settings):
|
||||||
"""Test get_userinfo method with a token response."""
|
"""Test get_userinfo method with a token response."""
|
||||||
|
settings.OIDC_RP_SIGN_ALGO = "HS256" # disable JWKS URL call
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET, re.compile(r".*/userinfo"), body="fake.jwt.token", status=200
|
responses.GET,
|
||||||
|
re.compile(r".*/userinfo"),
|
||||||
|
body="fake.jwt.token",
|
||||||
|
status=200,
|
||||||
|
content_type="application/jwt",
|
||||||
)
|
)
|
||||||
|
|
||||||
def mock_verify_token(self, token): # pylint: disable=unused-argument
|
def mock_verify_token(self, token): # pylint: disable=unused-argument
|
||||||
@@ -371,21 +375,25 @@ def test_authentication_get_userinfo_token_response(monkeypatch):
|
|||||||
|
|
||||||
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_authentication_get_userinfo_invalid_response():
|
def test_authentication_get_userinfo_invalid_response(settings):
|
||||||
"""
|
"""
|
||||||
Test get_userinfo method with an invalid JWT response that
|
Test get_userinfo method with an invalid JWT response that
|
||||||
causes verify_token to raise an error.
|
causes verify_token to raise an error.
|
||||||
"""
|
"""
|
||||||
|
settings.OIDC_RP_SIGN_ALGO = "HS256" # disable JWKS URL call
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET, re.compile(r".*/userinfo"), body="fake.jwt.token", status=200
|
responses.GET,
|
||||||
|
re.compile(r".*/userinfo"),
|
||||||
|
body="fake.jwt.token",
|
||||||
|
status=200,
|
||||||
|
content_type="application/jwt",
|
||||||
)
|
)
|
||||||
|
|
||||||
oidc_backend = OIDCAuthenticationBackend()
|
oidc_backend = OIDCAuthenticationBackend()
|
||||||
|
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
SuspiciousOperation,
|
SuspiciousOperation,
|
||||||
match="Invalid response format or token verification failed",
|
match="User info response was not valid JWT",
|
||||||
):
|
):
|
||||||
oidc_backend.get_userinfo("fake_access_token", None, None)
|
oidc_backend.get_userinfo("fake_access_token", None, None)
|
||||||
|
|
||||||
@@ -450,100 +458,54 @@ def test_authentication_getter_existing_disabled_user_via_email(
|
|||||||
assert models.User.objects.count() == 1
|
assert models.User.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
# Essential claims
|
@responses.activate
|
||||||
|
def test_authentication_session_tokens(
|
||||||
|
django_assert_num_queries, monkeypatch, rf, settings
|
||||||
def test_authentication_verify_claims_default(django_assert_num_queries, monkeypatch):
|
|
||||||
"""The sub claim should be mandatory by default."""
|
|
||||||
klass = OIDCAuthenticationBackend()
|
|
||||||
|
|
||||||
def get_userinfo_mocked(*args):
|
|
||||||
return {
|
|
||||||
"test": "123",
|
|
||||||
}
|
|
||||||
|
|
||||||
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
|
||||||
|
|
||||||
with (
|
|
||||||
django_assert_num_queries(0),
|
|
||||||
pytest.raises(
|
|
||||||
KeyError,
|
|
||||||
match="sub",
|
|
||||||
),
|
|
||||||
):
|
|
||||||
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
|
|
||||||
|
|
||||||
assert models.User.objects.exists() is False
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"essential_claims, missing_claims",
|
|
||||||
[
|
|
||||||
(["email", "sub"], ["email"]),
|
|
||||||
(["Email", "sub"], ["Email"]), # Case sensitivity
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
|
||||||
@mock.patch.object(Logger, "error")
|
|
||||||
def test_authentication_verify_claims_essential_missing(
|
|
||||||
mock_logger,
|
|
||||||
essential_claims,
|
|
||||||
missing_claims,
|
|
||||||
django_assert_num_queries,
|
|
||||||
monkeypatch,
|
|
||||||
):
|
):
|
||||||
"""Ensure SuspiciousOperation is raised if essential claims are missing."""
|
"""
|
||||||
|
Test that the session contains oidc_refresh_token and oidc_access_token after authentication.
|
||||||
|
"""
|
||||||
|
settings.OIDC_OP_TOKEN_ENDPOINT = "http://oidc.endpoint.test/token"
|
||||||
|
settings.OIDC_OP_USER_ENDPOINT = "http://oidc.endpoint.test/userinfo"
|
||||||
|
settings.OIDC_OP_JWKS_ENDPOINT = "http://oidc.endpoint.test/jwks"
|
||||||
|
settings.OIDC_STORE_ACCESS_TOKEN = True
|
||||||
|
settings.OIDC_STORE_REFRESH_TOKEN = True
|
||||||
|
settings.OIDC_STORE_REFRESH_TOKEN_KEY = Fernet.generate_key()
|
||||||
|
|
||||||
klass = OIDCAuthenticationBackend()
|
klass = OIDCAuthenticationBackend()
|
||||||
|
request = rf.get("/some-url", {"state": "test-state", "code": "test-code"})
|
||||||
|
request.session = {}
|
||||||
|
|
||||||
def get_userinfo_mocked(*args):
|
def verify_token_mocked(*args, **kwargs):
|
||||||
return {
|
return {"sub": "123", "email": "test@example.com"}
|
||||||
"sub": "123",
|
|
||||||
"last_name": "Doe",
|
|
||||||
}
|
|
||||||
|
|
||||||
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
monkeypatch.setattr(OIDCAuthenticationBackend, "verify_token", verify_token_mocked)
|
||||||
|
|
||||||
with (
|
responses.add(
|
||||||
django_assert_num_queries(0),
|
responses.POST,
|
||||||
pytest.raises(
|
re.compile(settings.OIDC_OP_TOKEN_ENDPOINT),
|
||||||
SuspiciousOperation,
|
json={
|
||||||
match="Claims verification failed",
|
"access_token": "test-access-token",
|
||||||
),
|
"refresh_token": "test-refresh-token",
|
||||||
override_settings(USER_OIDC_ESSENTIAL_CLAIMS=essential_claims),
|
},
|
||||||
):
|
status=200,
|
||||||
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
|
)
|
||||||
|
|
||||||
assert models.User.objects.exists() is False
|
responses.add(
|
||||||
mock_logger.assert_called_once_with("Missing essential claims: %s", missing_claims)
|
responses.GET,
|
||||||
|
re.compile(settings.OIDC_OP_USER_ENDPOINT),
|
||||||
|
json={"sub": "123", "email": "test@example.com"},
|
||||||
@override_settings(
|
status=200,
|
||||||
OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo",
|
)
|
||||||
USER_OIDC_ESSENTIAL_CLAIMS=["email", "last_name"],
|
|
||||||
)
|
|
||||||
def test_authentication_verify_claims_success(django_assert_num_queries, monkeypatch):
|
|
||||||
"""Ensure user is authenticated when all essential claims are present."""
|
|
||||||
|
|
||||||
klass = OIDCAuthenticationBackend()
|
|
||||||
|
|
||||||
def get_userinfo_mocked(*args):
|
|
||||||
return {
|
|
||||||
"email": "john.doe@example.com",
|
|
||||||
"last_name": "Doe",
|
|
||||||
"sub": "123",
|
|
||||||
}
|
|
||||||
|
|
||||||
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
|
||||||
|
|
||||||
with django_assert_num_queries(6):
|
with django_assert_num_queries(6):
|
||||||
user = klass.get_or_create_user(
|
user = klass.authenticate(
|
||||||
access_token="test-token", id_token=None, payload=None
|
request,
|
||||||
|
code="test-code",
|
||||||
|
nonce="test-nonce",
|
||||||
|
code_verifier="test-code-verifier",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert models.User.objects.filter(id=user.id).exists()
|
assert user is not None
|
||||||
|
assert request.session["oidc_access_token"] == "test-access-token"
|
||||||
assert user.sub == "123"
|
assert get_oidc_refresh_token(request.session) == "test-refresh-token"
|
||||||
assert user.full_name == "Doe"
|
|
||||||
assert user.short_name is None
|
|
||||||
assert user.email == "john.doe@example.com"
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
"""Unit tests for the Authentication URLs."""
|
|
||||||
|
|
||||||
from core.authentication.urls import urlpatterns
|
|
||||||
|
|
||||||
|
|
||||||
def test_urls_override_default_mozilla_django_oidc():
|
|
||||||
"""Custom URL patterns should override default ones from Mozilla Django OIDC."""
|
|
||||||
|
|
||||||
url_names = [u.name for u in urlpatterns]
|
|
||||||
assert url_names.index("oidc_logout_custom") < url_names.index("oidc_logout")
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
"""Unit tests for the Authentication Views."""
|
|
||||||
|
|
||||||
from unittest import mock
|
|
||||||
from urllib.parse import parse_qs, urlparse
|
|
||||||
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
|
||||||
from django.core.exceptions import SuspiciousOperation
|
|
||||||
from django.test import RequestFactory
|
|
||||||
from django.test.utils import override_settings
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import crypto
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from rest_framework.test import APIClient
|
|
||||||
|
|
||||||
from core import factories
|
|
||||||
from core.authentication.views import OIDCLogoutCallbackView, OIDCLogoutView
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(LOGOUT_REDIRECT_URL="/example-logout")
|
|
||||||
def test_view_logout_anonymous():
|
|
||||||
"""Anonymous users calling the logout url,
|
|
||||||
should be redirected to the specified LOGOUT_REDIRECT_URL."""
|
|
||||||
|
|
||||||
url = reverse("oidc_logout_custom")
|
|
||||||
response = APIClient().get(url)
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == "/example-logout"
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(
|
|
||||||
OIDCLogoutView, "construct_oidc_logout_url", return_value="/example-logout"
|
|
||||||
)
|
|
||||||
def test_view_logout(mocked_oidc_logout_url):
|
|
||||||
"""Authenticated users should be redirected to OIDC provider for logout."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
url = reverse("oidc_logout_custom")
|
|
||||||
response = client.get(url)
|
|
||||||
|
|
||||||
mocked_oidc_logout_url.assert_called_once()
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == "/example-logout"
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(LOGOUT_REDIRECT_URL="/default-redirect-logout")
|
|
||||||
@mock.patch.object(
|
|
||||||
OIDCLogoutView, "construct_oidc_logout_url", return_value="/default-redirect-logout"
|
|
||||||
)
|
|
||||||
def test_view_logout_no_oidc_provider(mocked_oidc_logout_url):
|
|
||||||
"""Authenticated users should be logged out when no OIDC provider is available."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
url = reverse("oidc_logout_custom")
|
|
||||||
|
|
||||||
with mock.patch("mozilla_django_oidc.views.auth.logout") as mock_logout:
|
|
||||||
response = client.get(url)
|
|
||||||
mocked_oidc_logout_url.assert_called_once()
|
|
||||||
mock_logout.assert_called_once()
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == "/default-redirect-logout"
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(LOGOUT_REDIRECT_URL="/example-logout")
|
|
||||||
def test_view_logout_callback_anonymous():
|
|
||||||
"""Anonymous users calling the logout callback url,
|
|
||||||
should be redirected to the specified LOGOUT_REDIRECT_URL."""
|
|
||||||
|
|
||||||
url = reverse("oidc_logout_callback")
|
|
||||||
response = APIClient().get(url)
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == "/example-logout"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"initial_oidc_states",
|
|
||||||
[{}, {"other_state": "foo"}],
|
|
||||||
)
|
|
||||||
def test_view_logout_persist_state(initial_oidc_states):
|
|
||||||
"""State value should be persisted in session's data."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
request = RequestFactory().request()
|
|
||||||
request.user = user
|
|
||||||
|
|
||||||
middleware = SessionMiddleware(get_response=lambda x: x)
|
|
||||||
middleware.process_request(request)
|
|
||||||
|
|
||||||
if initial_oidc_states:
|
|
||||||
request.session["oidc_states"] = initial_oidc_states
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
mocked_state = "mock_state"
|
|
||||||
|
|
||||||
OIDCLogoutView().persist_state(request, mocked_state)
|
|
||||||
|
|
||||||
assert "oidc_states" in request.session
|
|
||||||
assert request.session["oidc_states"] == {
|
|
||||||
"mock_state": {},
|
|
||||||
**initial_oidc_states,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(OIDC_OP_LOGOUT_ENDPOINT="/example-logout")
|
|
||||||
@mock.patch.object(OIDCLogoutView, "persist_state")
|
|
||||||
@mock.patch.object(crypto, "get_random_string", return_value="mocked_state")
|
|
||||||
def test_view_logout_construct_oidc_logout_url(
|
|
||||||
mocked_get_random_string, mocked_persist_state
|
|
||||||
):
|
|
||||||
"""Should construct the logout URL to initiate the logout flow with the OIDC provider."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
request = RequestFactory().request()
|
|
||||||
request.user = user
|
|
||||||
|
|
||||||
middleware = SessionMiddleware(get_response=lambda x: x)
|
|
||||||
middleware.process_request(request)
|
|
||||||
|
|
||||||
request.session["oidc_id_token"] = "mocked_oidc_id_token"
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
redirect_url = OIDCLogoutView().construct_oidc_logout_url(request)
|
|
||||||
|
|
||||||
mocked_persist_state.assert_called_once()
|
|
||||||
mocked_get_random_string.assert_called_once()
|
|
||||||
|
|
||||||
params = parse_qs(urlparse(redirect_url).query)
|
|
||||||
|
|
||||||
assert params["id_token_hint"][0] == "mocked_oidc_id_token"
|
|
||||||
assert params["state"][0] == "mocked_state"
|
|
||||||
|
|
||||||
url = reverse("oidc_logout_callback")
|
|
||||||
assert url in params["post_logout_redirect_uri"][0]
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(LOGOUT_REDIRECT_URL="/")
|
|
||||||
def test_view_logout_construct_oidc_logout_url_none_id_token():
|
|
||||||
"""If no ID token is available in the session,
|
|
||||||
the user should be redirected to the final URL."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
request = RequestFactory().request()
|
|
||||||
request.user = user
|
|
||||||
|
|
||||||
middleware = SessionMiddleware(get_response=lambda x: x)
|
|
||||||
middleware.process_request(request)
|
|
||||||
|
|
||||||
redirect_url = OIDCLogoutView().construct_oidc_logout_url(request)
|
|
||||||
|
|
||||||
assert redirect_url == "/"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"initial_state",
|
|
||||||
[None, {"other_state": "foo"}],
|
|
||||||
)
|
|
||||||
def test_view_logout_callback_wrong_state(initial_state):
|
|
||||||
"""Should raise an error if OIDC state doesn't match session data."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
request = RequestFactory().request()
|
|
||||||
request.user = user
|
|
||||||
|
|
||||||
middleware = SessionMiddleware(get_response=lambda x: x)
|
|
||||||
middleware.process_request(request)
|
|
||||||
|
|
||||||
if initial_state:
|
|
||||||
request.session["oidc_states"] = initial_state
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
callback_view = OIDCLogoutCallbackView.as_view()
|
|
||||||
|
|
||||||
with pytest.raises(SuspiciousOperation) as excinfo:
|
|
||||||
callback_view(request)
|
|
||||||
|
|
||||||
assert (
|
|
||||||
str(excinfo.value) == "OIDC callback state not found in session `oidc_states`!"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(LOGOUT_REDIRECT_URL="/example-logout")
|
|
||||||
def test_view_logout_callback():
|
|
||||||
"""If state matches, callback should clear OIDC state and redirects."""
|
|
||||||
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
request = RequestFactory().get("/logout-callback/", data={"state": "mocked_state"})
|
|
||||||
request.user = user
|
|
||||||
|
|
||||||
middleware = SessionMiddleware(get_response=lambda x: x)
|
|
||||||
middleware.process_request(request)
|
|
||||||
|
|
||||||
mocked_state = "mocked_state"
|
|
||||||
|
|
||||||
request.session["oidc_states"] = {mocked_state: {}}
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
callback_view = OIDCLogoutCallbackView.as_view()
|
|
||||||
|
|
||||||
with mock.patch("mozilla_django_oidc.views.auth.logout") as mock_logout:
|
|
||||||
|
|
||||||
def clear_user(request):
|
|
||||||
# Assert state is cleared prior to logout
|
|
||||||
assert request.session["oidc_states"] == {}
|
|
||||||
request.user = AnonymousUser()
|
|
||||||
|
|
||||||
mock_logout.side_effect = clear_user
|
|
||||||
response = callback_view(request)
|
|
||||||
mock_logout.assert_called_once()
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == "/example-logout"
|
|
||||||
@@ -304,7 +304,7 @@ def test_api_document_accesses_create_email_in_receivers_language(via, mock_user
|
|||||||
)
|
)
|
||||||
elif expected_language == "fr-fr":
|
elif expected_language == "fr-fr":
|
||||||
assert (
|
assert (
|
||||||
f"{user.full_name} a partagé un document avec vous: {document.title}".lower()
|
f"{user.full_name} a partagé un document avec vous : {document.title}".lower()
|
||||||
in email_subject.lower()
|
in email_subject.lower()
|
||||||
)
|
)
|
||||||
assert "docs/" + str(document.id) + "/" in email_content.lower()
|
assert "docs/" + str(document.id) + "/" in email_content.lower()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Tests for Documents API endpoint in impress's core app: children create
|
Tests for Documents API endpoint in impress's core app: children create
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -249,3 +250,41 @@ def test_api_documents_children_create_force_id_existing():
|
|||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"id": ["A document with this ID already exists. You cannot override it."]
|
"id": ["A document with this ID already exists. You cannot override it."]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db(transaction=True)
|
||||||
|
def test_api_documents_create_document_children_race_condition():
|
||||||
|
"""
|
||||||
|
It should be possible to create several documents at the same time
|
||||||
|
without causing any race conditions or data integrity issues.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = factories.UserFactory()
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
document = factories.DocumentFactory()
|
||||||
|
|
||||||
|
factories.UserDocumentAccessFactory(user=user, document=document, role="owner")
|
||||||
|
|
||||||
|
def create_document():
|
||||||
|
return client.post(
|
||||||
|
f"/api/v1.0/documents/{document.id}/children/",
|
||||||
|
{
|
||||||
|
"title": "my child",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||||
|
future1 = executor.submit(create_document)
|
||||||
|
future2 = executor.submit(create_document)
|
||||||
|
|
||||||
|
response1 = future1.result()
|
||||||
|
response2 = future2.result()
|
||||||
|
|
||||||
|
assert response1.status_code == 201
|
||||||
|
assert response2.status_code == 201
|
||||||
|
|
||||||
|
document.refresh_from_db()
|
||||||
|
assert document.numchild == 2
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Tests for Documents API endpoint in impress's core app: create
|
Tests for Documents API endpoint in impress's core app: create
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -51,6 +52,36 @@ def test_api_documents_create_authenticated_success():
|
|||||||
assert document.accesses.filter(role="owner", user=user).exists()
|
assert document.accesses.filter(role="owner", user=user).exists()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db(transaction=True)
|
||||||
|
def test_api_documents_create_document_race_condition():
|
||||||
|
"""
|
||||||
|
It should be possible to create several documents at the same time
|
||||||
|
without causing any race conditions or data integrity issues.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_document(title):
|
||||||
|
user = factories.UserFactory()
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
return client.post(
|
||||||
|
"/api/v1.0/documents/",
|
||||||
|
{
|
||||||
|
"title": title,
|
||||||
|
},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||||
|
future1 = executor.submit(create_document, "my document 1")
|
||||||
|
future2 = executor.submit(create_document, "my document 2")
|
||||||
|
|
||||||
|
response1 = future1.result()
|
||||||
|
response2 = future2.result()
|
||||||
|
|
||||||
|
assert response1.status_code == 201
|
||||||
|
assert response2.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
def test_api_documents_create_authenticated_title_null():
|
def test_api_documents_create_authenticated_title_null():
|
||||||
"""It should be possible to create several documents with a null title."""
|
"""It should be possible to create several documents with a null title."""
|
||||||
user = factories.UserFactory()
|
user = factories.UserFactory()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Tests for Documents API endpoint in impress's core app: create
|
|||||||
|
|
||||||
# pylint: disable=W0621
|
# pylint: disable=W0621
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
@@ -425,6 +426,36 @@ def test_api_documents_create_for_owner_new_user_no_sub_no_fallback_allow_duplic
|
|||||||
assert document.creator == user
|
assert document.creator == user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db(transaction=True)
|
||||||
|
def test_api_documents_create_document_race_condition():
|
||||||
|
"""
|
||||||
|
It should be possible to create several documents at the same time
|
||||||
|
without causing any race conditions or data integrity issues.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_document(title):
|
||||||
|
user = factories.UserFactory()
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
return client.post(
|
||||||
|
"/api/v1.0/documents/",
|
||||||
|
{
|
||||||
|
"title": title,
|
||||||
|
},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||||
|
future1 = executor.submit(create_document, "my document 1")
|
||||||
|
future2 = executor.submit(create_document, "my document 2")
|
||||||
|
|
||||||
|
response1 = future1.result()
|
||||||
|
response2 = future2.result()
|
||||||
|
|
||||||
|
assert response1.status_code == 201
|
||||||
|
assert response2.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
@patch.object(ServerCreateDocumentSerializer, "_send_email_notification")
|
@patch.object(ServerCreateDocumentSerializer, "_send_email_notification")
|
||||||
@override_settings(SERVER_TO_SERVER_API_TOKENS=["DummyToken"], LANGUAGE_CODE="de-de")
|
@override_settings(SERVER_TO_SERVER_API_TOKENS=["DummyToken"], LANGUAGE_CODE="de-de")
|
||||||
def test_api_documents_create_for_owner_with_default_language(
|
def test_api_documents_create_for_owner_with_default_language(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from faker import Faker
|
|||||||
from rest_framework.test import APIClient
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
from core import factories
|
from core import factories
|
||||||
|
from core.api.filters import remove_accents
|
||||||
|
|
||||||
fake = Faker()
|
fake = Faker()
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
@@ -49,14 +50,16 @@ def test_api_documents_descendants_filter_unknown_field():
|
|||||||
[
|
[
|
||||||
("Project Alpha", 1), # Exact match
|
("Project Alpha", 1), # Exact match
|
||||||
("project", 2), # Partial match (case-insensitive)
|
("project", 2), # Partial match (case-insensitive)
|
||||||
("Guide", 1), # Word match within a title
|
("Guide", 2), # Word match within a title
|
||||||
("Special", 0), # No match (nonexistent keyword)
|
("Special", 0), # No match (nonexistent keyword)
|
||||||
("2024", 2), # Match by numeric keyword
|
("2024", 2), # Match by numeric keyword
|
||||||
("", 5), # Empty string
|
("", 6), # Empty string
|
||||||
|
("velo", 1), # Accent-insensitive match (velo vs vélo)
|
||||||
|
("bêta", 1), # Accent-insensitive match (bêta vs beta)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_api_documents_descendants_filter_title(query, nb_results):
|
def test_api_documents_descendants_filter_title(query, nb_results):
|
||||||
"""Authenticated users should be able to search documents by their title."""
|
"""Authenticated users should be able to search documents by their unaccented title."""
|
||||||
user = factories.UserFactory()
|
user = factories.UserFactory()
|
||||||
client = APIClient()
|
client = APIClient()
|
||||||
client.force_login(user)
|
client.force_login(user)
|
||||||
@@ -70,6 +73,7 @@ def test_api_documents_descendants_filter_title(query, nb_results):
|
|||||||
"User Guide",
|
"User Guide",
|
||||||
"Financial Report 2024",
|
"Financial Report 2024",
|
||||||
"Annual Review 2024",
|
"Annual Review 2024",
|
||||||
|
"Guide du vélo urbain", # <-- Title with accent for accent-insensitive test
|
||||||
]
|
]
|
||||||
for title in titles:
|
for title in titles:
|
||||||
factories.DocumentFactory(title=title, parent=document)
|
factories.DocumentFactory(title=title, parent=document)
|
||||||
@@ -85,4 +89,7 @@ def test_api_documents_descendants_filter_title(query, nb_results):
|
|||||||
|
|
||||||
# Ensure all results contain the query in their title
|
# Ensure all results contain the query in their title
|
||||||
for result in results:
|
for result in results:
|
||||||
assert query.lower().strip() in result["title"].lower()
|
assert (
|
||||||
|
remove_accents(query).lower().strip()
|
||||||
|
in remove_accents(result["title"]).lower()
|
||||||
|
)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ pytestmark = pytest.mark.django_db
|
|||||||
COLLABORATION_WS_URL="http://testcollab/",
|
COLLABORATION_WS_URL="http://testcollab/",
|
||||||
CRISP_WEBSITE_ID="123",
|
CRISP_WEBSITE_ID="123",
|
||||||
FRONTEND_CSS_URL="http://testcss/",
|
FRONTEND_CSS_URL="http://testcss/",
|
||||||
FRONTEND_HOMEPAGE_FEATURE_ENABLED=True,
|
|
||||||
FRONTEND_FOOTER_FEATURE_ENABLED=True,
|
FRONTEND_FOOTER_FEATURE_ENABLED=True,
|
||||||
FRONTEND_THEME="test-theme",
|
FRONTEND_THEME="test-theme",
|
||||||
MEDIA_BASE_URL="http://testserver/",
|
MEDIA_BASE_URL="http://testserver/",
|
||||||
@@ -50,6 +49,7 @@ def test_api_config(is_authenticated):
|
|||||||
["fr-fr", "Français"],
|
["fr-fr", "Français"],
|
||||||
["de-de", "Deutsch"],
|
["de-de", "Deutsch"],
|
||||||
["nl-nl", "Nederlands"],
|
["nl-nl", "Nederlands"],
|
||||||
|
["es-es", "Español"],
|
||||||
],
|
],
|
||||||
"LANGUAGE_CODE": "en-us",
|
"LANGUAGE_CODE": "en-us",
|
||||||
"MEDIA_BASE_URL": "http://testserver/",
|
"MEDIA_BASE_URL": "http://testserver/",
|
||||||
|
|||||||
@@ -791,7 +791,7 @@ def test_models_documents__email_invitation__success_fr():
|
|||||||
|
|
||||||
assert (
|
assert (
|
||||||
f"Test Sender2 (sender2@example.com) vous a invité avec le rôle "propriétaire" "
|
f"Test Sender2 (sender2@example.com) vous a invité avec le rôle "propriétaire" "
|
||||||
f"sur le document suivant: {document.title}" in email_content
|
f"sur le document suivant : {document.title}" in email_content
|
||||||
)
|
)
|
||||||
assert f"docs/{document.id}/" in email_content
|
assert f"docs/{document.id}/" in email_content
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
|
from lasuite.oidc_login.urls import urlpatterns as oidc_urls
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
from core.api import viewsets
|
from core.api import viewsets
|
||||||
from core.authentication.urls import urlpatterns as oidc_urls
|
|
||||||
|
|
||||||
# - Main endpoints
|
# - Main endpoints
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from sentry_sdk.integrations.logging import ignore_logger
|
|||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
DATA_DIR = os.path.join("/", "data")
|
DATA_DIR = os.getenv("DATA_DIR", os.path.join("/", "data"))
|
||||||
|
|
||||||
|
|
||||||
def get_release():
|
def get_release():
|
||||||
@@ -239,6 +239,7 @@ class Base(Configuration):
|
|||||||
("fr-fr", "Français"),
|
("fr-fr", "Français"),
|
||||||
("de-de", "Deutsch"),
|
("de-de", "Deutsch"),
|
||||||
("nl-nl", "Nederlands"),
|
("nl-nl", "Nederlands"),
|
||||||
|
("es-es", "Español"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -332,6 +333,12 @@ class Base(Configuration):
|
|||||||
"rest_framework.parsers.JSONParser",
|
"rest_framework.parsers.JSONParser",
|
||||||
"nested_multipart_parser.drf.DrfNestedParser",
|
"nested_multipart_parser.drf.DrfNestedParser",
|
||||||
],
|
],
|
||||||
|
"DEFAULT_RENDERER_CLASSES": [
|
||||||
|
# 🔒️ Disable BrowsableAPIRenderer which provides forms allowing a user to
|
||||||
|
# see all the data in the database (ie a serializer with a ForeignKey field
|
||||||
|
# will generate a form with a field with all possible values of the FK).
|
||||||
|
"rest_framework.renderers.JSONRenderer",
|
||||||
|
],
|
||||||
"EXCEPTION_HANDLER": "core.api.exception_handler",
|
"EXCEPTION_HANDLER": "core.api.exception_handler",
|
||||||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
||||||
"PAGE_SIZE": 20,
|
"PAGE_SIZE": 20,
|
||||||
@@ -411,7 +418,7 @@ class Base(Configuration):
|
|||||||
None, environ_name="FRONTEND_THEME", environ_prefix=None
|
None, environ_name="FRONTEND_THEME", environ_prefix=None
|
||||||
)
|
)
|
||||||
FRONTEND_HOMEPAGE_FEATURE_ENABLED = values.BooleanValue(
|
FRONTEND_HOMEPAGE_FEATURE_ENABLED = values.BooleanValue(
|
||||||
default=False,
|
default=True,
|
||||||
environ_name="FRONTEND_HOMEPAGE_FEATURE_ENABLED",
|
environ_name="FRONTEND_HOMEPAGE_FEATURE_ENABLED",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
@@ -520,6 +527,28 @@ class Base(Configuration):
|
|||||||
environ_name="OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION",
|
environ_name="OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
|
OIDC_USE_PKCE = values.BooleanValue(
|
||||||
|
default=False, environ_name="OIDC_USE_PKCE", environ_prefix=None
|
||||||
|
)
|
||||||
|
OIDC_PKCE_CODE_CHALLENGE_METHOD = values.Value(
|
||||||
|
default="S256",
|
||||||
|
environ_name="OIDC_PKCE_CODE_CHALLENGE_METHOD",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
|
OIDC_PKCE_CODE_VERIFIER_SIZE = values.IntegerValue(
|
||||||
|
default=64, environ_name="OIDC_PKCE_CODE_VERIFIER_SIZE", environ_prefix=None
|
||||||
|
)
|
||||||
|
OIDC_STORE_ACCESS_TOKEN = values.BooleanValue(
|
||||||
|
default=False, environ_name="OIDC_STORE_ACCESS_TOKEN", environ_prefix=None
|
||||||
|
)
|
||||||
|
OIDC_STORE_REFRESH_TOKEN = values.BooleanValue(
|
||||||
|
default=False, environ_name="OIDC_STORE_REFRESH_TOKEN", environ_prefix=None
|
||||||
|
)
|
||||||
|
OIDC_STORE_REFRESH_TOKEN_KEY = values.Value(
|
||||||
|
default=None,
|
||||||
|
environ_name="OIDC_STORE_REFRESH_TOKEN_KEY",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
|
|
||||||
# WARNING: Enabling this setting allows multiple user accounts to share the same email
|
# WARNING: Enabling this setting allows multiple user accounts to share the same email
|
||||||
# address. This may cause security issues and is not recommended for production use when
|
# address. This may cause security issues and is not recommended for production use when
|
||||||
@@ -533,14 +562,23 @@ class Base(Configuration):
|
|||||||
USER_OIDC_ESSENTIAL_CLAIMS = values.ListValue(
|
USER_OIDC_ESSENTIAL_CLAIMS = values.ListValue(
|
||||||
default=[], environ_name="USER_OIDC_ESSENTIAL_CLAIMS", environ_prefix=None
|
default=[], environ_name="USER_OIDC_ESSENTIAL_CLAIMS", environ_prefix=None
|
||||||
)
|
)
|
||||||
USER_OIDC_FIELDS_TO_FULLNAME = values.ListValue(
|
|
||||||
default=["first_name", "last_name"],
|
OIDC_USERINFO_FULLNAME_FIELDS = values.ListValue(
|
||||||
environ_name="USER_OIDC_FIELDS_TO_FULLNAME",
|
default=values.ListValue( # retrocompatibility
|
||||||
|
default=["first_name", "last_name"],
|
||||||
|
environ_name="USER_OIDC_FIELDS_TO_FULLNAME",
|
||||||
|
environ_prefix=None,
|
||||||
|
),
|
||||||
|
environ_name="OIDC_USERINFO_FULLNAME_FIELDS",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
USER_OIDC_FIELD_TO_SHORTNAME = values.Value(
|
OIDC_USERINFO_SHORTNAME_FIELD = values.Value(
|
||||||
default="first_name",
|
default=values.Value( # retrocompatibility
|
||||||
environ_name="USER_OIDC_FIELD_TO_SHORTNAME",
|
default="first_name",
|
||||||
|
environ_name="USER_OIDC_FIELD_TO_SHORTNAME",
|
||||||
|
environ_prefix=None,
|
||||||
|
),
|
||||||
|
environ_name="OIDC_USERINFO_SHORTNAME_FIELD",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Breton\n"
|
||||||
|
"Language: br_FR\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=5; plural=(n%10==1 && (n%100!=11 || n%100!=71 || n%100!=91) ? 0 : n%10==2 && (n%100!=12 || n%100!=72 || n%100!=92) ? 1 : ((n%10>=3 && n%10<=4) || n%10==9) && ((n%100 < 10 || n%100 > 19) || (n%100 < 70 || n%100 > 79) || (n%100 < 90 || n%100 > 99)) ? 2 : (n!=0 && n%1;\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: br-FR\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 "Titouroù personel"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
||||||
|
#: core/admin.py:138
|
||||||
|
msgid "Permissions"
|
||||||
|
msgstr "Aotreoù"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:62 core/admin.py:62
|
||||||
|
msgid "Important dates"
|
||||||
|
msgstr "Deiziadoù a-bouez"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:148 core/admin.py:148
|
||||||
|
msgid "Tree structure"
|
||||||
|
msgstr "Gwezennadur"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "Titl"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
|
msgid "Creator is me"
|
||||||
|
msgstr "Me eo an aozer"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "Sinedoù"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
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:450 core/api/serializers.py:450
|
||||||
|
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:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr "Korf"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr "Doare korf"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "Stumm"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr "eilenn {title}"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr "Bugel kentañ"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr "Bugel diwezhañ"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr "Kleiz"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr "Dehoù"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "Lenner"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "Merour"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "Perc'henn"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Publik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr "id"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr "krouet d'ar/al"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "hizivaet d'ar/al"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "anv klok"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr "anv berr"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "yezh"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
msgid "The language in which the user wants to see the interface."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
|
msgid "The timezone in which the user wants to see times."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr "trevnad"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
msgid "Whether the user can log into this admin site."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr "implijer"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr "implijerien"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr "titl"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "css"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "publik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "Patrom"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "Patromoù"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "Digeriñ"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-04 13:46+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-04-16 16:32\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Chinese Simplified\n"
|
||||||
|
"Language: zh_CN\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: zh-CN\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:16 core/api/filters.py:16
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "标题"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:30 core/api/filters.py:30
|
||||||
|
msgid "Creator is me"
|
||||||
|
msgstr "创建者是我"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:33 core/api/filters.py:33
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "收藏"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr "已为您创建了一份新文档!"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
msgid "You have been granted ownership of a new document:"
|
||||||
|
msgstr "您已被授予新文档的所有权:"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr "正文"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr "正文类型"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "格式"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:944 core/api/viewsets.py:944
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr "{title} 的副本"
|
||||||
|
|
||||||
|
#: build/lib/core/authentication/backends.py:61
|
||||||
|
#: core/authentication/backends.py:61
|
||||||
|
msgid "Invalid response format or token verification failed"
|
||||||
|
msgstr "响应格式无效或令牌验证失败"
|
||||||
|
|
||||||
|
#: build/lib/core/authentication/backends.py:108
|
||||||
|
#: core/authentication/backends.py:108
|
||||||
|
msgid "User account is disabled"
|
||||||
|
msgstr "用户账户已被禁用"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr "第一个子项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr "最后一个子项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr "第一个同级项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr "最后一个同级项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr "左"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr "右"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "阅读者"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr "编辑者"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "超级管理员"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "所有者"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr "受限的"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "已验证"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "公开"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr "id"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr "记录的主密钥为 UUID"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr "创建时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr "记录的创建日期和时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "更新时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr "记录的最后更新时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr "请输入有效的 sub。该值只能包含字母、数字及 @/./+/-/_/: 字符。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr "sub"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
|
msgstr "必填。最多 255 个字符,仅允许字母、数字及 @/./+/-/_/: 字符。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "全名"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr "简称"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr "身份电子邮件地址"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr "管理员电子邮件地址"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "语言"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
msgid "The language in which the user wants to see the interface."
|
||||||
|
msgstr "用户希望看到的界面语言。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
|
msgid "The timezone in which the user wants to see times."
|
||||||
|
msgstr "用户查看时间希望的时区。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr "设备"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr "用户是设备还是真实用户。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr "员工状态"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
msgid "Whether the user can log into this admin site."
|
||||||
|
msgstr "用户是否可以登录该管理员站点。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr "激活"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
|
msgstr "是否应将此用户视为活跃用户。取消选择此选项而不是删除账户。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr "用户"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr "个用户"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr "标题"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr "摘要"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr "文档"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr "个文档"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr "未命名文档"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr "{name} 与您共享了一个文档!"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
|
msgstr "{name} 邀请您以“{role}”角色访问以下文档:"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr "{name} 与您共享了一个文档:{title}"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr "文档/用户链接跟踪"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr "个文档/用户链接跟踪"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr "此文档/用户的链接跟踪已存在。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr "文档收藏"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr "文档收藏夹"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr "该文档已被同一用户的收藏关系实例关联。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr "文档/用户关系"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr "文档/用户关系集"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr "该用户已在此文档中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr "该团队已在此文档中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr "必须设置用户或团队之一,不能同时设置两者。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr "说明"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr "代码"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "css"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "公开"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr "该模板是否公开供任何人使用。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "模板"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "模板"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr "模板/用户关系"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr "模板/用户关系集"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr "该用户已在此模板中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr "该团队已在此模板中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr "电子邮件地址"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr "文档邀请"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr "文档邀请"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr "此电子邮件已经与现有注册用户关联。"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr "徽标邮件"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "打开"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.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/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr " 由 %(brandname)s 倾力打造。 "
|
||||||
@@ -2,8 +2,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: lasuite-docs\n"
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-03-13 11:41+0000\n"
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
"PO-Revision-Date: 2025-03-17 13:58\n"
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
@@ -34,204 +34,199 @@ msgstr "Wichtige Daten"
|
|||||||
msgid "Tree structure"
|
msgid "Tree structure"
|
||||||
msgstr "Baumstruktur"
|
msgstr "Baumstruktur"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:30 core/api/filters.py:30
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
msgid "Creator is me"
|
msgid "Creator is me"
|
||||||
msgstr "Ersteller bin ich"
|
msgstr "Ersteller bin ich"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:33 core/api/filters.py:33
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
msgid "Favorite"
|
msgid "Favorite"
|
||||||
msgstr "Favorit"
|
msgstr "Favorit"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:354 core/api/serializers.py:354
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
msgid "A new document was created on your behalf!"
|
msgid "A new document was created on your behalf!"
|
||||||
msgstr "Ein neues Dokument wurde in Ihrem Namen erstellt!"
|
msgstr "Ein neues Dokument wurde in Ihrem Namen erstellt!"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:358 core/api/serializers.py:358
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
msgid "You have been granted ownership of a new document:"
|
msgid "You have been granted ownership of a new document:"
|
||||||
msgstr "Sie sind Besitzer eines neuen Dokuments:"
|
msgstr "Sie sind Besitzer eines neuen Dokuments:"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:473 core/api/serializers.py:473
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
msgid "Body"
|
msgid "Body"
|
||||||
msgstr "Inhalt"
|
msgstr "Inhalt"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:476 core/api/serializers.py:476
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
msgid "Body type"
|
msgid "Body type"
|
||||||
msgstr "Typ"
|
msgstr "Typ"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:482 core/api/serializers.py:482
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
msgid "Format"
|
msgid "Format"
|
||||||
msgstr ""
|
msgstr "Format"
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:61
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
#: core/authentication/backends.py:61
|
#, python-brace-format
|
||||||
msgid "Invalid response format or token verification failed"
|
msgid "copy of {title}"
|
||||||
msgstr "Ungültiges Antwortformat oder Token-Verifizierung fehlgeschlagen"
|
msgstr "Kopie von {title}"
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:108
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
#: core/authentication/backends.py:108
|
|
||||||
msgid "User account is disabled"
|
|
||||||
msgstr "Benutzerkonto ist deaktiviert"
|
|
||||||
|
|
||||||
#: build/lib/core/enums.py:19 core/enums.py:19
|
|
||||||
msgid "First child"
|
msgid "First child"
|
||||||
msgstr "Erstes Unterelement"
|
msgstr "Erstes Unterelement"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:20 core/enums.py:20
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
msgid "Last child"
|
msgid "Last child"
|
||||||
msgstr "Letztes Unterelement"
|
msgstr "Letztes Unterelement"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:21 core/enums.py:21
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
msgid "First sibling"
|
msgid "First sibling"
|
||||||
msgstr "Erstes Nebenelement"
|
msgstr "Erstes Nebenelement"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:22 core/enums.py:22
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
msgid "Last sibling"
|
msgid "Last sibling"
|
||||||
msgstr "Letztes Nebenelement"
|
msgstr "Letztes Nebenelement"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:23 core/enums.py:23
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
msgid "Left"
|
msgid "Left"
|
||||||
msgstr "Links"
|
msgstr "Links"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:24 core/enums.py:24
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
msgid "Right"
|
msgid "Right"
|
||||||
msgstr "Rechts"
|
msgstr "Rechts"
|
||||||
|
|
||||||
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
#: core/models.py:62
|
#: core/models.py:63
|
||||||
msgid "Reader"
|
msgid "Reader"
|
||||||
msgstr "Lesen"
|
msgstr "Lesen"
|
||||||
|
|
||||||
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
#: core/models.py:63
|
#: core/models.py:64
|
||||||
msgid "Editor"
|
msgid "Editor"
|
||||||
msgstr "Bearbeiten"
|
msgstr "Bearbeiten"
|
||||||
|
|
||||||
#: build/lib/core/models.py:64 core/models.py:64
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
msgid "Administrator"
|
msgid "Administrator"
|
||||||
msgstr "Administrator"
|
msgstr "Administrator"
|
||||||
|
|
||||||
#: build/lib/core/models.py:65 core/models.py:65
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
msgid "Owner"
|
msgid "Owner"
|
||||||
msgstr "Besitzer"
|
msgstr "Besitzer"
|
||||||
|
|
||||||
#: build/lib/core/models.py:76 core/models.py:76
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
msgid "Restricted"
|
msgid "Restricted"
|
||||||
msgstr "Beschränkt"
|
msgstr "Beschränkt"
|
||||||
|
|
||||||
#: build/lib/core/models.py:80 core/models.py:80
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
msgid "Authenticated"
|
msgid "Authenticated"
|
||||||
msgstr "Authentifiziert"
|
msgstr "Authentifiziert"
|
||||||
|
|
||||||
#: build/lib/core/models.py:82 core/models.py:82
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
msgid "Public"
|
msgid "Public"
|
||||||
msgstr "Öffentlich"
|
msgstr "Öffentlich"
|
||||||
|
|
||||||
#: build/lib/core/models.py:153 core/models.py:153
|
|
||||||
msgid "id"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:154 core/models.py:154
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr "id"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
msgid "primary key for the record as UUID"
|
msgid "primary key for the record as UUID"
|
||||||
msgstr "primärer Schlüssel für den Datensatz als UUID"
|
msgstr "primärer Schlüssel für den Datensatz als UUID"
|
||||||
|
|
||||||
#: build/lib/core/models.py:160 core/models.py:160
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
msgid "created on"
|
msgid "created on"
|
||||||
msgstr "Erstellt"
|
msgstr "Erstellt"
|
||||||
|
|
||||||
#: build/lib/core/models.py:161 core/models.py:161
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
msgid "date and time at which a record was created"
|
msgid "date and time at which a record was created"
|
||||||
msgstr "Datum und Uhrzeit, an dem ein Datensatz erstellt wurde"
|
msgstr "Datum und Uhrzeit, an dem ein Datensatz erstellt wurde"
|
||||||
|
|
||||||
#: build/lib/core/models.py:166 core/models.py:166
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
msgid "updated on"
|
msgid "updated on"
|
||||||
msgstr "Aktualisiert"
|
msgstr "Aktualisiert"
|
||||||
|
|
||||||
#: build/lib/core/models.py:167 core/models.py:167
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
msgid "date and time at which a record was last updated"
|
msgid "date and time at which a record was last updated"
|
||||||
msgstr "Datum und Uhrzeit, an dem zuletzt aktualisiert wurde"
|
msgstr "Datum und Uhrzeit, an dem zuletzt aktualisiert wurde"
|
||||||
|
|
||||||
#: build/lib/core/models.py:203 core/models.py:203
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
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."
|
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:216 core/models.py:216
|
#: build/lib/core/models.py:217 core/models.py:217
|
||||||
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
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."
|
msgstr "Geben Sie eine gültige Unterseite ein. Dieser Wert darf nur Buchstaben, Zahlen und die @/./+/-/_/: Zeichen enthalten."
|
||||||
|
|
||||||
#: build/lib/core/models.py:222 core/models.py:222
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
msgid "sub"
|
msgid "sub"
|
||||||
msgstr "unter"
|
msgstr "unter"
|
||||||
|
|
||||||
#: build/lib/core/models.py:224 core/models.py:224
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
msgstr "Erforderlich. 255 Zeichen oder weniger. Buchstaben, Zahlen und die Zeichen @/./+/-/_/:"
|
msgstr "Erforderlich. 255 Zeichen oder weniger. Buchstaben, Zahlen und die Zeichen @/./+/-/_/:"
|
||||||
|
|
||||||
#: build/lib/core/models.py:233 core/models.py:233
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
msgid "full name"
|
msgid "full name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: build/lib/core/models.py:234 core/models.py:234
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
msgid "short name"
|
msgid "short name"
|
||||||
msgstr "Kurzbezeichnung"
|
msgstr "Kurzbezeichnung"
|
||||||
|
|
||||||
#: build/lib/core/models.py:236 core/models.py:236
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
msgid "identity email address"
|
msgid "identity email address"
|
||||||
msgstr "Identitäts-E-Mail-Adresse"
|
msgstr "Identitäts-E-Mail-Adresse"
|
||||||
|
|
||||||
#: build/lib/core/models.py:241 core/models.py:241
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
msgid "admin email address"
|
msgid "admin email address"
|
||||||
msgstr "Admin E-Mail-Adresse"
|
msgstr "Admin E-Mail-Adresse"
|
||||||
|
|
||||||
#: build/lib/core/models.py:248 core/models.py:248
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
msgid "language"
|
msgid "language"
|
||||||
msgstr "Sprache"
|
msgstr "Sprache"
|
||||||
|
|
||||||
#: build/lib/core/models.py:249 core/models.py:249
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
msgid "The language in which the user wants to see the interface."
|
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."
|
msgstr "Die Sprache, in der der Benutzer die Benutzeroberfläche sehen möchte."
|
||||||
|
|
||||||
#: build/lib/core/models.py:257 core/models.py:257
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
msgid "The timezone in which the user wants to see times."
|
msgid "The timezone in which the user wants to see times."
|
||||||
msgstr "Die Zeitzone, in der der Nutzer Zeiten sehen möchte."
|
msgstr "Die Zeitzone, in der der Nutzer Zeiten sehen möchte."
|
||||||
|
|
||||||
#: build/lib/core/models.py:260 core/models.py:260
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
msgid "device"
|
msgid "device"
|
||||||
msgstr "Gerät"
|
msgstr "Gerät"
|
||||||
|
|
||||||
#: build/lib/core/models.py:262 core/models.py:262
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
msgid "Whether the user is a device or a real user."
|
msgid "Whether the user is a device or a real user."
|
||||||
msgstr "Ob der Benutzer ein Gerät oder ein echter Benutzer ist."
|
msgstr "Ob der Benutzer ein Gerät oder ein echter Benutzer ist."
|
||||||
|
|
||||||
#: build/lib/core/models.py:265 core/models.py:265
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
msgid "staff status"
|
msgid "staff status"
|
||||||
msgstr "Status des Teammitgliedes"
|
msgstr "Status des Teammitgliedes"
|
||||||
|
|
||||||
#: build/lib/core/models.py:267 core/models.py:267
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
msgid "Whether the user can log into this admin site."
|
msgid "Whether the user can log into this admin site."
|
||||||
msgstr "Gibt an, ob der Benutzer sich in diese Admin-Seite einloggen kann."
|
msgstr "Gibt an, ob der Benutzer sich in diese Admin-Seite einloggen kann."
|
||||||
|
|
||||||
#: build/lib/core/models.py:270 core/models.py:270
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr "aktiviert"
|
msgstr "aktiviert"
|
||||||
|
|
||||||
#: build/lib/core/models.py:273 core/models.py:273
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
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."
|
msgstr "Ob dieser Benutzer als aktiviert behandelt werden soll. Deaktivieren Sie diese Option, anstatt Konten zu löschen."
|
||||||
|
|
||||||
#: build/lib/core/models.py:285 core/models.py:285
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr "Benutzer"
|
msgstr "Benutzer"
|
||||||
|
|
||||||
#: build/lib/core/models.py:286 core/models.py:286
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
msgid "users"
|
msgid "users"
|
||||||
msgstr "Benutzer"
|
msgstr "Benutzer"
|
||||||
|
|
||||||
#: build/lib/core/models.py:470 build/lib/core/models.py:1074
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
#: core/models.py:470 core/models.py:1074
|
#: core/models.py:470 core/models.py:1154
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
@@ -239,136 +234,136 @@ msgstr "Titel"
|
|||||||
msgid "excerpt"
|
msgid "excerpt"
|
||||||
msgstr "Auszug"
|
msgstr "Auszug"
|
||||||
|
|
||||||
#: build/lib/core/models.py:504 core/models.py:504
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
msgid "Document"
|
msgid "Document"
|
||||||
msgstr "Dokument"
|
msgstr "Dokument"
|
||||||
|
|
||||||
#: build/lib/core/models.py:505 core/models.py:505
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
msgid "Documents"
|
msgid "Documents"
|
||||||
msgstr "Dokumente"
|
msgstr "Dokumente"
|
||||||
|
|
||||||
#: build/lib/core/models.py:517 build/lib/core/models.py:826 core/models.py:517
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
#: core/models.py:826
|
#: core/models.py:872
|
||||||
msgid "Untitled Document"
|
msgid "Untitled Document"
|
||||||
msgstr "Unbenanntes Dokument"
|
msgstr "Unbenanntes Dokument"
|
||||||
|
|
||||||
#: build/lib/core/models.py:861 core/models.py:861
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you!"
|
msgid "{name} shared a document with you!"
|
||||||
msgstr "{name} hat ein Dokument mit Ihnen geteilt!"
|
msgstr "{name} hat ein Dokument mit Ihnen geteilt!"
|
||||||
|
|
||||||
#: build/lib/core/models.py:865 core/models.py:865
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
msgstr "{name} hat Sie mit der Rolle \"{role}\" zu folgendem Dokument eingeladen:"
|
msgstr "{name} hat Sie mit der Rolle \"{role}\" zu folgendem Dokument eingeladen:"
|
||||||
|
|
||||||
#: build/lib/core/models.py:871 core/models.py:871
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you: {title}"
|
msgid "{name} shared a document with you: {title}"
|
||||||
msgstr "{name} hat ein Dokument mit Ihnen geteilt: {title}"
|
msgstr "{name} hat ein Dokument mit Ihnen geteilt: {title}"
|
||||||
|
|
||||||
#: build/lib/core/models.py:969 core/models.py:969
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
msgid "Document/user link trace"
|
msgid "Document/user link trace"
|
||||||
msgstr "Dokument/Benutzer Linkverfolgung"
|
msgstr "Dokument/Benutzer Linkverfolgung"
|
||||||
|
|
||||||
#: build/lib/core/models.py:970 core/models.py:970
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
msgid "Document/user link traces"
|
msgid "Document/user link traces"
|
||||||
msgstr "Dokument/Benutzer Linkverfolgung"
|
msgstr "Dokument/Benutzer Linkverfolgung"
|
||||||
|
|
||||||
#: build/lib/core/models.py:976 core/models.py:976
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
msgid "A link trace already exists for this document/user."
|
msgid "A link trace already exists for this document/user."
|
||||||
msgstr ""
|
msgstr "Für dieses Dokument/ diesen Benutzer ist bereits eine Linkverfolgung vorhanden."
|
||||||
|
|
||||||
#: build/lib/core/models.py:999 core/models.py:999
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
msgid "Document favorite"
|
msgid "Document favorite"
|
||||||
msgstr "Dokumentenfavorit"
|
msgstr "Dokumentenfavorit"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1000 core/models.py:1000
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
msgid "Document favorites"
|
msgid "Document favorites"
|
||||||
msgstr "Dokumentfavoriten"
|
msgstr "Dokumentfavoriten"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
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."
|
msgstr "Dieses Dokument ist bereits durch den gleichen Benutzer favorisiert worden."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1028 core/models.py:1028
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
msgid "Document/user relation"
|
msgid "Document/user relation"
|
||||||
msgstr "Dokument/Benutzerbeziehung"
|
msgstr "Dokument/Benutzerbeziehung"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1029 core/models.py:1029
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
msgid "Document/user relations"
|
msgid "Document/user relations"
|
||||||
msgstr "Dokument/Benutzerbeziehungen"
|
msgstr "Dokument/Benutzerbeziehungen"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
msgid "This user is already in this document."
|
msgid "This user is already in this document."
|
||||||
msgstr "Dieser Benutzer befindet sich bereits in diesem Dokument."
|
msgstr "Dieser Benutzer befindet sich bereits in diesem Dokument."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
msgid "This team is already in this document."
|
msgid "This team is already in this document."
|
||||||
msgstr "Dieses Team befindet sich bereits in diesem Dokument."
|
msgstr "Dieses Team befindet sich bereits in diesem Dokument."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1047 build/lib/core/models.py:1161
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
#: core/models.py:1047 core/models.py:1161
|
#: core/models.py:1093 core/models.py:1241
|
||||||
msgid "Either user or team must be set, not both."
|
msgid "Either user or team must be set, not both."
|
||||||
msgstr "Benutzer oder Team müssen gesetzt werden, nicht beides."
|
msgstr "Benutzer oder Team müssen gesetzt werden, nicht beides."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1075 core/models.py:1075
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr "Beschreibung"
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1076 core/models.py:1076
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
msgid "code"
|
msgid "code"
|
||||||
msgstr "Code"
|
msgstr "Code"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1077 core/models.py:1077
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
msgid "css"
|
msgid "css"
|
||||||
msgstr "CSS"
|
msgstr "CSS"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1079 core/models.py:1079
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
msgid "public"
|
msgid "public"
|
||||||
msgstr "öffentlich"
|
msgstr "öffentlich"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1081 core/models.py:1081
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
msgid "Whether this template is public for anyone to use."
|
msgid "Whether this template is public for anyone to use."
|
||||||
msgstr "Ob diese Vorlage für jedermann öffentlich ist."
|
msgstr "Ob diese Vorlage für jedermann öffentlich ist."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1087 core/models.py:1087
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr "Vorlage"
|
msgstr "Vorlage"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1088 core/models.py:1088
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
msgid "Templates"
|
msgid "Templates"
|
||||||
msgstr "Vorlagen"
|
msgstr "Vorlagen"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1142 core/models.py:1142
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
msgid "Template/user relation"
|
msgid "Template/user relation"
|
||||||
msgstr "Vorlage/Benutzer-Beziehung"
|
msgstr "Vorlage/Benutzer-Beziehung"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1143 core/models.py:1143
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
msgid "Template/user relations"
|
msgid "Template/user relations"
|
||||||
msgstr "Vorlage/Benutzerbeziehungen"
|
msgstr "Vorlage/Benutzerbeziehungen"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1149 core/models.py:1149
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
msgid "This user is already in this template."
|
msgid "This user is already in this template."
|
||||||
msgstr "Dieser Benutzer ist bereits in dieser Vorlage."
|
msgstr "Dieser Benutzer ist bereits in dieser Vorlage."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1155 core/models.py:1155
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
msgid "This team is already in this template."
|
msgid "This team is already in this template."
|
||||||
msgstr "Dieses Team ist bereits in diesem Template."
|
msgstr "Dieses Team ist bereits in diesem Template."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1178 core/models.py:1178
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
msgid "email address"
|
msgid "email address"
|
||||||
msgstr "E-Mail-Adresse"
|
msgstr "E-Mail-Adresse"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1197 core/models.py:1197
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
msgid "Document invitation"
|
msgid "Document invitation"
|
||||||
msgstr "Einladung zum Dokument"
|
msgstr "Einladung zum Dokument"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1198 core/models.py:1198
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
msgid "Document invitations"
|
msgid "Document invitations"
|
||||||
msgstr "Dokumenteinladungen"
|
msgstr "Dokumenteinladungen"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1218 core/models.py:1218
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
msgid "This email is already associated to a registered user."
|
msgid "This email is already associated to a registered user."
|
||||||
msgstr "Diese E-Mail ist bereits einem registrierten Benutzer zugeordnet."
|
msgstr "Diese E-Mail ist bereits einem registrierten Benutzer zugeordnet."
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: lasuite-docs\n"
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-03-13 11:41+0000\n"
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
"PO-Revision-Date: 2025-03-17 13:58\n"
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: English\n"
|
"Language-Team: English\n"
|
||||||
"Language: en_US\n"
|
"Language: en_US\n"
|
||||||
@@ -34,204 +34,199 @@ msgstr ""
|
|||||||
msgid "Tree structure"
|
msgid "Tree structure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:30 core/api/filters.py:30
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
msgid "Creator is me"
|
msgid "Creator is me"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:33 core/api/filters.py:33
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
msgid "Favorite"
|
msgid "Favorite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:354 core/api/serializers.py:354
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
msgid "A new document was created on your behalf!"
|
msgid "A new document was created on your behalf!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:358 core/api/serializers.py:358
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
msgid "You have been granted ownership of a new document:"
|
msgid "You have been granted ownership of a new document:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:473 core/api/serializers.py:473
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
msgid "Body"
|
msgid "Body"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:476 core/api/serializers.py:476
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
msgid "Body type"
|
msgid "Body type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:482 core/api/serializers.py:482
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
msgid "Format"
|
msgid "Format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:61
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
#: core/authentication/backends.py:61
|
#, python-brace-format
|
||||||
msgid "Invalid response format or token verification failed"
|
msgid "copy of {title}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:108
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
#: core/authentication/backends.py:108
|
|
||||||
msgid "User account is disabled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/enums.py:19 core/enums.py:19
|
|
||||||
msgid "First child"
|
msgid "First child"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/enums.py:20 core/enums.py:20
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
msgid "Last child"
|
msgid "Last child"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/enums.py:21 core/enums.py:21
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
msgid "First sibling"
|
msgid "First sibling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/enums.py:22 core/enums.py:22
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
msgid "Last sibling"
|
msgid "Last sibling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/enums.py:23 core/enums.py:23
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
msgid "Left"
|
msgid "Left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/enums.py:24 core/enums.py:24
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
msgid "Right"
|
msgid "Right"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
|
|
||||||
#: core/models.py:62
|
|
||||||
msgid "Reader"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
#: core/models.py:63
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
msgid "Editor"
|
msgid "Editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:64 core/models.py:64
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
msgid "Administrator"
|
msgid "Administrator"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:65 core/models.py:65
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
msgid "Owner"
|
msgid "Owner"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:76 core/models.py:76
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
msgid "Restricted"
|
msgid "Restricted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:80 core/models.py:80
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
msgid "Authenticated"
|
msgid "Authenticated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:82 core/models.py:82
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
msgid "Public"
|
msgid "Public"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:153 core/models.py:153
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
msgid "id"
|
msgid "id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:154 core/models.py:154
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
msgid "primary key for the record as UUID"
|
msgid "primary key for the record as UUID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:160 core/models.py:160
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
msgid "created on"
|
msgid "created on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:161 core/models.py:161
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
msgid "date and time at which a record was created"
|
msgid "date and time at which a record was created"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:166 core/models.py:166
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
msgid "updated on"
|
msgid "updated on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:167 core/models.py:167
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
msgid "date and time at which a record was last updated"
|
msgid "date and time at which a record was last updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:203 core/models.py:203
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:216 core/models.py:216
|
#: build/lib/core/models.py:217 core/models.py:217
|
||||||
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:222 core/models.py:222
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
msgid "sub"
|
msgid "sub"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:224 core/models.py:224
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:233 core/models.py:233
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
msgid "full name"
|
msgid "full name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:234 core/models.py:234
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
msgid "short name"
|
msgid "short name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:236 core/models.py:236
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
msgid "identity email address"
|
msgid "identity email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:241 core/models.py:241
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
msgid "admin email address"
|
msgid "admin email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:248 core/models.py:248
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
msgid "language"
|
msgid "language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:249 core/models.py:249
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
msgid "The language in which the user wants to see the interface."
|
msgid "The language in which the user wants to see the interface."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:257 core/models.py:257
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
msgid "The timezone in which the user wants to see times."
|
msgid "The timezone in which the user wants to see times."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:260 core/models.py:260
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
msgid "device"
|
msgid "device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:262 core/models.py:262
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
msgid "Whether the user is a device or a real user."
|
msgid "Whether the user is a device or a real user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:265 core/models.py:265
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
msgid "staff status"
|
msgid "staff status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:267 core/models.py:267
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
msgid "Whether the user can log into this admin site."
|
msgid "Whether the user can log into this admin site."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:270 core/models.py:270
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:273 core/models.py:273
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:285 core/models.py:285
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:286 core/models.py:286
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
msgid "users"
|
msgid "users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:470 build/lib/core/models.py:1074
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
#: core/models.py:470 core/models.py:1074
|
#: core/models.py:470 core/models.py:1154
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -239,136 +234,136 @@ msgstr ""
|
|||||||
msgid "excerpt"
|
msgid "excerpt"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:504 core/models.py:504
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
msgid "Document"
|
msgid "Document"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:505 core/models.py:505
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
msgid "Documents"
|
msgid "Documents"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:517 build/lib/core/models.py:826 core/models.py:517
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
#: core/models.py:826
|
#: core/models.py:872
|
||||||
msgid "Untitled Document"
|
msgid "Untitled Document"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:861 core/models.py:861
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you!"
|
msgid "{name} shared a document with you!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:865 core/models.py:865
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:871 core/models.py:871
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you: {title}"
|
msgid "{name} shared a document with you: {title}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:969 core/models.py:969
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
msgid "Document/user link trace"
|
msgid "Document/user link trace"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:970 core/models.py:970
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
msgid "Document/user link traces"
|
msgid "Document/user link traces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:976 core/models.py:976
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
msgid "A link trace already exists for this document/user."
|
msgid "A link trace already exists for this document/user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:999 core/models.py:999
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
msgid "Document favorite"
|
msgid "Document favorite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1000 core/models.py:1000
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
msgid "Document favorites"
|
msgid "Document favorites"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1028 core/models.py:1028
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
msgid "Document/user relation"
|
msgid "Document/user relation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1029 core/models.py:1029
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
msgid "Document/user relations"
|
msgid "Document/user relations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
msgid "This user is already in this document."
|
msgid "This user is already in this document."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
msgid "This team is already in this document."
|
msgid "This team is already in this document."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1047 build/lib/core/models.py:1161
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
#: core/models.py:1047 core/models.py:1161
|
#: core/models.py:1093 core/models.py:1241
|
||||||
msgid "Either user or team must be set, not both."
|
msgid "Either user or team must be set, not both."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1075 core/models.py:1075
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1076 core/models.py:1076
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
msgid "code"
|
msgid "code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1077 core/models.py:1077
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
msgid "css"
|
msgid "css"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1079 core/models.py:1079
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
msgid "public"
|
msgid "public"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1081 core/models.py:1081
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
msgid "Whether this template is public for anyone to use."
|
msgid "Whether this template is public for anyone to use."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1087 core/models.py:1087
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1088 core/models.py:1088
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
msgid "Templates"
|
msgid "Templates"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1142 core/models.py:1142
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
msgid "Template/user relation"
|
msgid "Template/user relation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1143 core/models.py:1143
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
msgid "Template/user relations"
|
msgid "Template/user relations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1149 core/models.py:1149
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
msgid "This user is already in this template."
|
msgid "This user is already in this template."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1155 core/models.py:1155
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
msgid "This team is already in this template."
|
msgid "This team is already in this template."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1178 core/models.py:1178
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
msgid "email address"
|
msgid "email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1197 core/models.py:1197
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
msgid "Document invitation"
|
msgid "Document invitation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1198 core/models.py:1198
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
msgid "Document invitations"
|
msgid "Document invitations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/lib/core/models.py:1218 core/models.py:1218
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
msgid "This email is already associated to a registered user."
|
msgid "This email is already associated to a registered user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Spanish\n"
|
||||||
|
"Language: es_ES\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: es-ES\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 "Información Personal"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
||||||
|
#: core/admin.py:138
|
||||||
|
msgid "Permissions"
|
||||||
|
msgstr "Permisos"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:62 core/admin.py:62
|
||||||
|
msgid "Important dates"
|
||||||
|
msgstr "Fechas importantes"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:148 core/admin.py:148
|
||||||
|
msgid "Tree structure"
|
||||||
|
msgstr "Estructura en árbol"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "Título"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
|
msgid "Creator is me"
|
||||||
|
msgstr "Yo soy el creador"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "Favorito"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr "¡Un nuevo documento se ha creado por ti!"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
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:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr "Cuerpo"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr "Tipo de Cuerpo"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "Formato"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr "copia de {title}"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr "Primer nodo"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr "Último nodo"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr "Primera relación"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr "Última relación"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr "Izquierda"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr "Derecha"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "Lector"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr "Editor"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "Administrador"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "Propietario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr "Restringido"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Autentificado"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Público"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr "id"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr "clave primaria para el registro como UUID"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr "creado el"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
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:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "actualizado el"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
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:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
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:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr "sub (UUID)"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
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:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "nombre completo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr "nombre abreviado"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr "correo electrónico de identidad"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr "correo electrónico del administrador"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "idioma"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
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:258 core/models.py:258
|
||||||
|
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:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr "dispositivo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
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:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr "rol en el equipo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
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:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr "activo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
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:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr "usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr "usuarios"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr "título"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr "resumen"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr "Documento"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr "Documentos"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr "Documento sin título"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr "¡{name} ha compartido un documento contigo!"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, 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:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr "{name} ha compartido un documento contigo: {title}"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr "Traza del enlace de documento/usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr "Trazas del enlace de documento/usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
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:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr "Documento favorito"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr "Documentos favoritos"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
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:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr "Relación documento/usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr "Relaciones documento/usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr "Este usuario ya forma parte del documento."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr "Este equipo ya forma parte del documento."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
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:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr "descripción"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr "código"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "css"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "público"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
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:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "Plantilla"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "Plantillas"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr "Relación plantilla/usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr "Relaciones plantilla/usuario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr "Este usuario ya forma parte de la plantilla."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr "Este equipo ya se encuentra en esta plantilla."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr "dirección de correo electrónico"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr "Invitación al documento"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr "Invitaciones a documentos"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr "Este correo electrónico está asociado a un usuario registrado."
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr "Logo de correo electrónico"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "Abrir"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr "Docs, su nueva herramienta esencial para organizar, compartir y colaborar en sus documentos como equipo."
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr " Presentado por %(brandname)s "
|
||||||
|
|
||||||
@@ -2,8 +2,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: lasuite-docs\n"
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-03-13 11:41+0000\n"
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
"PO-Revision-Date: 2025-03-17 13:58\n"
|
"PO-Revision-Date: 2025-05-05 09:05\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
"Language: fr_FR\n"
|
"Language: fr_FR\n"
|
||||||
@@ -24,7 +24,7 @@ msgstr "Infos Personnelles"
|
|||||||
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
||||||
#: core/admin.py:138
|
#: core/admin.py:138
|
||||||
msgid "Permissions"
|
msgid "Permissions"
|
||||||
msgstr ""
|
msgstr "Permissions"
|
||||||
|
|
||||||
#: build/lib/core/admin.py:62 core/admin.py:62
|
#: build/lib/core/admin.py:62 core/admin.py:62
|
||||||
msgid "Important dates"
|
msgid "Important dates"
|
||||||
@@ -32,345 +32,340 @@ msgstr "Dates importantes"
|
|||||||
|
|
||||||
#: build/lib/core/admin.py:148 core/admin.py:148
|
#: build/lib/core/admin.py:148 core/admin.py:148
|
||||||
msgid "Tree structure"
|
msgid "Tree structure"
|
||||||
msgstr ""
|
msgstr "Arborescence"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr "Titre"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:30 core/api/filters.py:30
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
msgid "Creator is me"
|
msgid "Creator is me"
|
||||||
msgstr ""
|
msgstr "Je suis l'auteur"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:33 core/api/filters.py:33
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
msgid "Favorite"
|
msgid "Favorite"
|
||||||
msgstr ""
|
msgstr "Favoris"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:354 core/api/serializers.py:354
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
msgid "A new document was created on your behalf!"
|
msgid "A new document was created on your behalf!"
|
||||||
msgstr "Un nouveau document a été créé pour vous !"
|
msgstr "Un nouveau document a été créé pour vous !"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:358 core/api/serializers.py:358
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
msgid "You have been granted ownership of a new document:"
|
msgid "You have been granted ownership of a new document:"
|
||||||
msgstr "Vous avez été déclaré propriétaire d'un nouveau document :"
|
msgstr "Vous avez été déclaré propriétaire d'un nouveau document :"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:473 core/api/serializers.py:473
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
msgid "Body"
|
msgid "Body"
|
||||||
msgstr ""
|
msgstr "Corps"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:476 core/api/serializers.py:476
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
msgid "Body type"
|
msgid "Body type"
|
||||||
msgstr ""
|
msgstr "Type de corps"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:482 core/api/serializers.py:482
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
msgid "Format"
|
msgid "Format"
|
||||||
msgstr ""
|
msgstr "Format"
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:61
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
#: core/authentication/backends.py:61
|
#, python-brace-format
|
||||||
msgid "Invalid response format or token verification failed"
|
msgid "copy of {title}"
|
||||||
msgstr ""
|
msgstr "copie de {title}"
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:108
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
#: core/authentication/backends.py:108
|
|
||||||
msgid "User account is disabled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/enums.py:19 core/enums.py:19
|
|
||||||
msgid "First child"
|
msgid "First child"
|
||||||
msgstr ""
|
msgstr "Premier enfant"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:20 core/enums.py:20
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
msgid "Last child"
|
msgid "Last child"
|
||||||
msgstr ""
|
msgstr "Dernier enfant"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:21 core/enums.py:21
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
msgid "First sibling"
|
msgid "First sibling"
|
||||||
msgstr ""
|
msgstr "Premier frère ou sœur"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:22 core/enums.py:22
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
msgid "Last sibling"
|
msgid "Last sibling"
|
||||||
msgstr ""
|
msgstr "Dernière relation"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:23 core/enums.py:23
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
msgid "Left"
|
msgid "Left"
|
||||||
msgstr ""
|
msgstr "Gauche"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:24 core/enums.py:24
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
msgid "Right"
|
msgid "Right"
|
||||||
msgstr ""
|
msgstr "Droite"
|
||||||
|
|
||||||
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
|
|
||||||
#: core/models.py:62
|
|
||||||
msgid "Reader"
|
|
||||||
msgstr "Lecteur"
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
#: core/models.py:63
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "Lecteur"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
msgid "Editor"
|
msgid "Editor"
|
||||||
msgstr "Éditeur"
|
msgstr "Éditeur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:64 core/models.py:64
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
msgid "Administrator"
|
msgid "Administrator"
|
||||||
msgstr "Administrateur"
|
msgstr "Administrateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:65 core/models.py:65
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
msgid "Owner"
|
msgid "Owner"
|
||||||
msgstr "Propriétaire"
|
msgstr "Propriétaire"
|
||||||
|
|
||||||
#: build/lib/core/models.py:76 core/models.py:76
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
msgid "Restricted"
|
msgid "Restricted"
|
||||||
msgstr "Restreint"
|
msgstr "Restreint"
|
||||||
|
|
||||||
#: build/lib/core/models.py:80 core/models.py:80
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
msgid "Authenticated"
|
msgid "Authenticated"
|
||||||
msgstr "Authentifié"
|
msgstr "Authentifié"
|
||||||
|
|
||||||
#: build/lib/core/models.py:82 core/models.py:82
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
msgid "Public"
|
msgid "Public"
|
||||||
msgstr ""
|
msgstr "Public"
|
||||||
|
|
||||||
#: build/lib/core/models.py:153 core/models.py:153
|
|
||||||
msgid "id"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:154 core/models.py:154
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
msgid "primary key for the record as UUID"
|
msgid "id"
|
||||||
msgstr ""
|
msgstr "identifiant/id"
|
||||||
|
|
||||||
#: build/lib/core/models.py:160 core/models.py:160
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
msgid "created on"
|
msgid "primary key for the record as UUID"
|
||||||
msgstr ""
|
msgstr "clé primaire pour l'enregistrement en tant que UUID"
|
||||||
|
|
||||||
#: build/lib/core/models.py:161 core/models.py:161
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
msgid "date and time at which a record was created"
|
msgid "created on"
|
||||||
msgstr ""
|
msgstr "créé le"
|
||||||
|
|
||||||
#: build/lib/core/models.py:166 core/models.py:166
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
msgid "updated on"
|
msgid "date and time at which a record was created"
|
||||||
msgstr ""
|
msgstr "date et heure de création de l'enregistrement"
|
||||||
|
|
||||||
#: build/lib/core/models.py:167 core/models.py:167
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "mis à jour le"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
msgid "date and time at which a record was last updated"
|
msgid "date and time at which a record was last updated"
|
||||||
msgstr ""
|
msgstr "date et heure de la dernière mise à jour de l'enregistrement"
|
||||||
|
|
||||||
#: build/lib/core/models.py:203 core/models.py:203
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||||
msgstr ""
|
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:216 core/models.py:216
|
#: build/lib/core/models.py:217 core/models.py:217
|
||||||
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
msgstr ""
|
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:222 core/models.py:222
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
msgid "sub"
|
msgid "sub"
|
||||||
msgstr ""
|
msgstr "sous-groupe"
|
||||||
|
|
||||||
#: build/lib/core/models.py:224 core/models.py:224
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
msgstr ""
|
msgstr "Obligatoire. 255 caractères ou moins. Lettres, chiffres et caractères @/./+/-/_/: uniquement."
|
||||||
|
|
||||||
#: build/lib/core/models.py:233 core/models.py:233
|
|
||||||
msgid "full name"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:234 core/models.py:234
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "nom complet"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
msgid "short name"
|
msgid "short name"
|
||||||
msgstr ""
|
msgstr "nom court"
|
||||||
|
|
||||||
#: build/lib/core/models.py:236 core/models.py:236
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
msgid "identity email address"
|
msgid "identity email address"
|
||||||
msgstr ""
|
msgstr "adresse e-mail d'identité"
|
||||||
|
|
||||||
#: build/lib/core/models.py:241 core/models.py:241
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
msgid "admin email address"
|
msgid "admin email address"
|
||||||
msgstr ""
|
msgstr "adresse e-mail de l'administrateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:248 core/models.py:248
|
|
||||||
msgid "language"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:249 core/models.py:249
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "langue"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
msgid "The language in which the user wants to see the interface."
|
msgid "The language in which the user wants to see the interface."
|
||||||
msgstr ""
|
msgstr "La langue dans laquelle l'utilisateur veut voir l'interface."
|
||||||
|
|
||||||
#: build/lib/core/models.py:257 core/models.py:257
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
msgid "The timezone in which the user wants to see times."
|
msgid "The timezone in which the user wants to see times."
|
||||||
msgstr ""
|
msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures."
|
||||||
|
|
||||||
#: build/lib/core/models.py:260 core/models.py:260
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
msgid "device"
|
msgid "device"
|
||||||
msgstr ""
|
msgstr "appareil"
|
||||||
|
|
||||||
#: build/lib/core/models.py:262 core/models.py:262
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
msgid "Whether the user is a device or a real user."
|
msgid "Whether the user is a device or a real user."
|
||||||
msgstr ""
|
msgstr "Si l'utilisateur est un appareil ou un utilisateur réel."
|
||||||
|
|
||||||
#: build/lib/core/models.py:265 core/models.py:265
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
msgid "staff status"
|
msgid "staff status"
|
||||||
msgstr ""
|
msgstr "statut d'équipe"
|
||||||
|
|
||||||
#: build/lib/core/models.py:267 core/models.py:267
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
msgid "Whether the user can log into this admin site."
|
msgid "Whether the user can log into this admin site."
|
||||||
msgstr ""
|
msgstr "Si l'utilisateur peut se connecter à ce site d'administration."
|
||||||
|
|
||||||
#: build/lib/core/models.py:270 core/models.py:270
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr ""
|
msgstr "actif"
|
||||||
|
|
||||||
#: build/lib/core/models.py:273 core/models.py:273
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
msgstr ""
|
msgstr "Si cet utilisateur doit être traité comme actif. Désélectionnez ceci au lieu de supprimer des comptes."
|
||||||
|
|
||||||
#: build/lib/core/models.py:285 core/models.py:285
|
|
||||||
msgid "user"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:286 core/models.py:286
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
msgid "users"
|
msgid "user"
|
||||||
msgstr ""
|
msgstr "utilisateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:470 build/lib/core/models.py:1074
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
#: core/models.py:470 core/models.py:1074
|
msgid "users"
|
||||||
|
msgstr "utilisateurs"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr ""
|
msgstr "titre"
|
||||||
|
|
||||||
#: build/lib/core/models.py:471 core/models.py:471
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
msgid "excerpt"
|
msgid "excerpt"
|
||||||
msgstr ""
|
msgstr "extrait"
|
||||||
|
|
||||||
#: build/lib/core/models.py:504 core/models.py:504
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
msgid "Document"
|
msgid "Document"
|
||||||
msgstr ""
|
msgstr "Document"
|
||||||
|
|
||||||
#: build/lib/core/models.py:505 core/models.py:505
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
msgid "Documents"
|
msgid "Documents"
|
||||||
msgstr ""
|
msgstr "Documents"
|
||||||
|
|
||||||
#: build/lib/core/models.py:517 build/lib/core/models.py:826 core/models.py:517
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
#: core/models.py:826
|
#: core/models.py:872
|
||||||
msgid "Untitled Document"
|
msgid "Untitled Document"
|
||||||
msgstr "Document sans titre"
|
msgstr "Document sans titre"
|
||||||
|
|
||||||
#: build/lib/core/models.py:861 core/models.py:861
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you!"
|
msgid "{name} shared a document with you!"
|
||||||
msgstr "{name} a partagé un document avec vous!"
|
msgstr "{name} a partagé un document avec vous!"
|
||||||
|
|
||||||
#: build/lib/core/models.py:865 core/models.py:865
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
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:"
|
msgstr "{name} vous a invité avec le rôle \"{role}\" sur le document suivant :"
|
||||||
|
|
||||||
#: build/lib/core/models.py:871 core/models.py:871
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you: {title}"
|
msgid "{name} shared a document with you: {title}"
|
||||||
msgstr "{name} a partagé un document avec vous: {title}"
|
msgstr "{name} a partagé un document avec vous : {title}"
|
||||||
|
|
||||||
#: build/lib/core/models.py:969 core/models.py:969
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
msgid "Document/user link trace"
|
msgid "Document/user link trace"
|
||||||
msgstr ""
|
msgstr "Trace du lien document/utilisateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:970 core/models.py:970
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
msgid "Document/user link traces"
|
msgid "Document/user link traces"
|
||||||
msgstr ""
|
msgstr "Traces du lien document/utilisateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:976 core/models.py:976
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
msgid "A link trace already exists for this document/user."
|
msgid "A link trace already exists for this document/user."
|
||||||
msgstr ""
|
msgstr "Une trace de lien existe déjà pour ce document/utilisateur."
|
||||||
|
|
||||||
#: build/lib/core/models.py:999 core/models.py:999
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
msgid "Document favorite"
|
msgid "Document favorite"
|
||||||
msgstr ""
|
msgstr "Document favori"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1000 core/models.py:1000
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
msgid "Document favorites"
|
msgid "Document favorites"
|
||||||
msgstr ""
|
msgstr "Documents favoris"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
msgstr ""
|
msgstr "Ce document est déjà un favori de cet utilisateur."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1028 core/models.py:1028
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
msgid "Document/user relation"
|
msgid "Document/user relation"
|
||||||
msgstr ""
|
msgstr "Relation document/utilisateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1029 core/models.py:1029
|
|
||||||
msgid "Document/user relations"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
|
||||||
msgid "This user is already in this document."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
|
||||||
msgid "This team is already in this document."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1047 build/lib/core/models.py:1161
|
|
||||||
#: core/models.py:1047 core/models.py:1161
|
|
||||||
msgid "Either user or team must be set, not both."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1075 core/models.py:1075
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
msgid "description"
|
msgid "Document/user relations"
|
||||||
msgstr ""
|
msgstr "Relations document/utilisateur"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1076 core/models.py:1076
|
|
||||||
msgid "code"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1077 core/models.py:1077
|
|
||||||
msgid "css"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1079 core/models.py:1079
|
|
||||||
msgid "public"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1081 core/models.py:1081
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
msgid "Whether this template is public for anyone to use."
|
msgid "This user is already in this document."
|
||||||
msgstr ""
|
msgstr "Cet utilisateur est déjà dans ce document."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1087 core/models.py:1087
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
msgid "Template"
|
msgid "This team is already in this document."
|
||||||
msgstr ""
|
msgstr "Cette équipe est déjà dans ce document."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1088 core/models.py:1088
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
msgid "Templates"
|
#: core/models.py:1093 core/models.py:1241
|
||||||
msgstr ""
|
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:1142 core/models.py:1142
|
|
||||||
msgid "Template/user relation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1143 core/models.py:1143
|
|
||||||
msgid "Template/user relations"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1149 core/models.py:1149
|
|
||||||
msgid "This user is already in this template."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: build/lib/core/models.py:1155 core/models.py:1155
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr "description"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr "code"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "CSS"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "public"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
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:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "Modèle"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "Modèles"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr "Relation modèle/utilisateur"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr "Relations modèle/utilisateur"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr "Cet utilisateur est déjà dans ce modèle."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
msgid "This team is already in this template."
|
msgid "This team is already in this template."
|
||||||
msgstr ""
|
msgstr "Cette équipe est déjà modèle."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1178 core/models.py:1178
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
msgid "email address"
|
msgid "email address"
|
||||||
msgstr ""
|
msgstr "adresse e-mail"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1197 core/models.py:1197
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
msgid "Document invitation"
|
msgid "Document invitation"
|
||||||
msgstr ""
|
msgstr "Invitation à un document"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1198 core/models.py:1198
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
msgid "Document invitations"
|
msgid "Document invitations"
|
||||||
msgstr ""
|
msgstr "Invitations à un document"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1218 core/models.py:1218
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
msgid "This email is already associated to a registered user."
|
msgid "This email is already associated to a registered user."
|
||||||
msgstr ""
|
msgstr "Cette adresse email est déjà associée à un utilisateur inscrit."
|
||||||
|
|
||||||
#: core/templates/mail/html/invitation.html:162
|
#: core/templates/mail/html/invitation.html:162
|
||||||
#: core/templates/mail/text/invitation.txt:3
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Italian\n"
|
||||||
|
"Language: it_IT\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: it\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 "Informazioni personali"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
||||||
|
#: core/admin.py:138
|
||||||
|
msgid "Permissions"
|
||||||
|
msgstr "Permessi"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:62 core/admin.py:62
|
||||||
|
msgid "Important dates"
|
||||||
|
msgstr "Date importanti"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:148 core/admin.py:148
|
||||||
|
msgid "Tree structure"
|
||||||
|
msgstr "Struttura ad albero"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "Titolo"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
|
msgid "Creator is me"
|
||||||
|
msgstr "Il creatore sono io"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "Preferiti"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr "Un nuovo documento è stato creato a tuo nome!"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
msgid "You have been granted ownership of a new document:"
|
||||||
|
msgstr "Sei ora proprietario di un nuovo documento:"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr "Corpo"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "Formato"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr "copia di {title}"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr "Sinistra"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr "Destra"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "Lettore"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr "Editor"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "Amministratore"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "Proprietario"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr "Limitato"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Autenticato"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Pubblico"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr "Id"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr "chiave primaria per il record come UUID"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr "creato il"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
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:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "aggiornato il"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr "data e ora in cui l’ultimo record è stato aggiornato"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
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:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "nome completo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr "nome"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr "indirizzo email di identità"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr "Indirizzo email dell'amministratore"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "lingua"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
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:258 core/models.py:258
|
||||||
|
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:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr "dispositivo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
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:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr "stato del personale"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
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:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr "attivo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
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:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr "utente"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr "utenti"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr "titolo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr "Documento"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr "Documenti"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr "Documento senza titolo"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr "{name} ha condiviso un documento con te!"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, 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:917 core/models.py:917
|
||||||
|
#, 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:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr "Documento preferito"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr "Documenti preferiti"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr "Questo utente è già presente in questo documento."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr "Questo team è già presente in questo documento."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr "descrizione"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr "code"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "css"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "pubblico"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr "Indica se questo modello è pubblico per chiunque."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "Modello"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "Modelli"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr "Questo utente è già in questo modello."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr "Questo team è già in questo modello."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr "indirizzo e-mail"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr "Invito al documento"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr "Inviti al documento"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr "Questa email è già associata a un utente registrato."
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr "Logo e-mail"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "Apri"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
@@ -2,8 +2,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: lasuite-docs\n"
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-03-13 11:41+0000\n"
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
"PO-Revision-Date: 2025-03-17 13:58\n"
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Dutch\n"
|
"Language-Team: Dutch\n"
|
||||||
"Language: nl_NL\n"
|
"Language: nl_NL\n"
|
||||||
@@ -34,204 +34,199 @@ msgstr "Belangrijke datums"
|
|||||||
msgid "Tree structure"
|
msgid "Tree structure"
|
||||||
msgstr "Document structuur"
|
msgstr "Document structuur"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:30 core/api/filters.py:30
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
msgid "Creator is me"
|
msgid "Creator is me"
|
||||||
msgstr "Ik ben Eigenaar"
|
msgstr "Ik ben Eigenaar"
|
||||||
|
|
||||||
#: build/lib/core/api/filters.py:33 core/api/filters.py:33
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
msgid "Favorite"
|
msgid "Favorite"
|
||||||
msgstr "Favoriete"
|
msgstr "Favoriete"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:354 core/api/serializers.py:354
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
msgid "A new document was created on your behalf!"
|
msgid "A new document was created on your behalf!"
|
||||||
msgstr "Een nieuw document was gecreëerd voor u!"
|
msgstr "Een nieuw document was gecreëerd voor u!"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:358 core/api/serializers.py:358
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
msgid "You have been granted ownership of a new document:"
|
msgid "You have been granted ownership of a new document:"
|
||||||
msgstr "U heeft eigenaarschap van een nieuw document:"
|
msgstr "U heeft eigenaarschap van een nieuw document:"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:473 core/api/serializers.py:473
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
msgid "Body"
|
msgid "Body"
|
||||||
msgstr "Text"
|
msgstr "Text"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:476 core/api/serializers.py:476
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
msgid "Body type"
|
msgid "Body type"
|
||||||
msgstr "Text type"
|
msgstr "Text type"
|
||||||
|
|
||||||
#: build/lib/core/api/serializers.py:482 core/api/serializers.py:482
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
msgid "Format"
|
msgid "Format"
|
||||||
msgstr "Formaat"
|
msgstr "Formaat"
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:61
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
#: core/authentication/backends.py:61
|
#, python-brace-format
|
||||||
msgid "Invalid response format or token verification failed"
|
msgid "copy of {title}"
|
||||||
msgstr "Invalide response formaat of token verificatie gefaald"
|
msgstr "kopie van {title}"
|
||||||
|
|
||||||
#: build/lib/core/authentication/backends.py:108
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
#: core/authentication/backends.py:108
|
|
||||||
msgid "User account is disabled"
|
|
||||||
msgstr "Gebruikersaccount is buiten gebruik gesteld"
|
|
||||||
|
|
||||||
#: build/lib/core/enums.py:19 core/enums.py:19
|
|
||||||
msgid "First child"
|
msgid "First child"
|
||||||
msgstr "Eerste node"
|
msgstr "Eerste node"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:20 core/enums.py:20
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
msgid "Last child"
|
msgid "Last child"
|
||||||
msgstr "Laatste node"
|
msgstr "Laatste node"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:21 core/enums.py:21
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
msgid "First sibling"
|
msgid "First sibling"
|
||||||
msgstr "Eerste naaste"
|
msgstr "Eerste naaste"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:22 core/enums.py:22
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
msgid "Last sibling"
|
msgid "Last sibling"
|
||||||
msgstr "Laatste naaste"
|
msgstr "Laatste naaste"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:23 core/enums.py:23
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
msgid "Left"
|
msgid "Left"
|
||||||
msgstr "Links"
|
msgstr "Links"
|
||||||
|
|
||||||
#: build/lib/core/enums.py:24 core/enums.py:24
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
msgid "Right"
|
msgid "Right"
|
||||||
msgstr "Rechts"
|
msgstr "Rechts"
|
||||||
|
|
||||||
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
#: core/models.py:62
|
#: core/models.py:63
|
||||||
msgid "Reader"
|
msgid "Reader"
|
||||||
msgstr "Lezer"
|
msgstr "Lezer"
|
||||||
|
|
||||||
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
#: core/models.py:63
|
#: core/models.py:64
|
||||||
msgid "Editor"
|
msgid "Editor"
|
||||||
msgstr "Bewerker"
|
msgstr "Bewerker"
|
||||||
|
|
||||||
#: build/lib/core/models.py:64 core/models.py:64
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
msgid "Administrator"
|
msgid "Administrator"
|
||||||
msgstr "Administrator"
|
msgstr "Administrator"
|
||||||
|
|
||||||
#: build/lib/core/models.py:65 core/models.py:65
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
msgid "Owner"
|
msgid "Owner"
|
||||||
msgstr "Eigenaar"
|
msgstr "Eigenaar"
|
||||||
|
|
||||||
#: build/lib/core/models.py:76 core/models.py:76
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
msgid "Restricted"
|
msgid "Restricted"
|
||||||
msgstr "Niet toegestaan"
|
msgstr "Niet toegestaan"
|
||||||
|
|
||||||
#: build/lib/core/models.py:80 core/models.py:80
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
msgid "Authenticated"
|
msgid "Authenticated"
|
||||||
msgstr "Geauthenticeerd"
|
msgstr "Geauthenticeerd"
|
||||||
|
|
||||||
#: build/lib/core/models.py:82 core/models.py:82
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
msgid "Public"
|
msgid "Public"
|
||||||
msgstr "Publiek"
|
msgstr "Publiek"
|
||||||
|
|
||||||
#: build/lib/core/models.py:153 core/models.py:153
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
msgid "id"
|
msgid "id"
|
||||||
msgstr "id"
|
msgstr "id"
|
||||||
|
|
||||||
#: build/lib/core/models.py:154 core/models.py:154
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
msgid "primary key for the record as UUID"
|
msgid "primary key for the record as UUID"
|
||||||
msgstr "primaire sleutel voor dossier als UUID"
|
msgstr "primaire sleutel voor dossier als UUID"
|
||||||
|
|
||||||
#: build/lib/core/models.py:160 core/models.py:160
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
msgid "created on"
|
msgid "created on"
|
||||||
msgstr "gemaakt op"
|
msgstr "gemaakt op"
|
||||||
|
|
||||||
#: build/lib/core/models.py:161 core/models.py:161
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
msgid "date and time at which a record was created"
|
msgid "date and time at which a record was created"
|
||||||
msgstr "datum en tijd wanneer dossier was gecreëerd"
|
msgstr "datum en tijd wanneer dossier was gecreëerd"
|
||||||
|
|
||||||
#: build/lib/core/models.py:166 core/models.py:166
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
msgid "updated on"
|
msgid "updated on"
|
||||||
msgstr "Laatst gewijzigd op"
|
msgstr "Laatst gewijzigd op"
|
||||||
|
|
||||||
#: build/lib/core/models.py:167 core/models.py:167
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
msgid "date and time at which a record was last updated"
|
msgid "date and time at which a record was last updated"
|
||||||
msgstr "datum en tijd waarop dossier laatst was gewijzigd"
|
msgstr "datum en tijd waarop dossier laatst was gewijzigd"
|
||||||
|
|
||||||
#: build/lib/core/models.py:203 core/models.py:203
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
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."
|
msgstr "Wij konden geen gebruiker vinden met deze id, maar de email is al geassocieerd met een geregistreerde gebruiker."
|
||||||
|
|
||||||
#: build/lib/core/models.py:216 core/models.py:216
|
#: build/lib/core/models.py:217 core/models.py:217
|
||||||
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
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."
|
msgstr ".Geef een valide id. De waarde mag alleen letters, nummers en @/./.+/-/_: karakters bevatten."
|
||||||
|
|
||||||
#: build/lib/core/models.py:222 core/models.py:222
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
msgid "sub"
|
msgid "sub"
|
||||||
msgstr "id"
|
msgstr "id"
|
||||||
|
|
||||||
#: build/lib/core/models.py:224 core/models.py:224
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
msgstr "Verplicht. 255 karakters of minder. Alleen letters, nummers en @/./+/-/_/: karakters zijn toegestaan."
|
msgstr "Verplicht. 255 karakters of minder. Alleen letters, nummers en @/./+/-/_/: karakters zijn toegestaan."
|
||||||
|
|
||||||
#: build/lib/core/models.py:233 core/models.py:233
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
msgid "full name"
|
msgid "full name"
|
||||||
msgstr "volledige naam"
|
msgstr "volledige naam"
|
||||||
|
|
||||||
#: build/lib/core/models.py:234 core/models.py:234
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
msgid "short name"
|
msgid "short name"
|
||||||
msgstr "gebruikersnaam"
|
msgstr "gebruikersnaam"
|
||||||
|
|
||||||
#: build/lib/core/models.py:236 core/models.py:236
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
msgid "identity email address"
|
msgid "identity email address"
|
||||||
msgstr "identiteit email adres"
|
msgstr "identiteit email adres"
|
||||||
|
|
||||||
#: build/lib/core/models.py:241 core/models.py:241
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
msgid "admin email address"
|
msgid "admin email address"
|
||||||
msgstr "admin email adres"
|
msgstr "admin email adres"
|
||||||
|
|
||||||
#: build/lib/core/models.py:248 core/models.py:248
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
msgid "language"
|
msgid "language"
|
||||||
msgstr "taal"
|
msgstr "taal"
|
||||||
|
|
||||||
#: build/lib/core/models.py:249 core/models.py:249
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
msgid "The language in which the user wants to see the interface."
|
msgid "The language in which the user wants to see the interface."
|
||||||
msgstr "De taal waarin de gebruiker de interface wilt zien."
|
msgstr "De taal waarin de gebruiker de interface wilt zien."
|
||||||
|
|
||||||
#: build/lib/core/models.py:257 core/models.py:257
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
msgid "The timezone in which the user wants to see times."
|
msgid "The timezone in which the user wants to see times."
|
||||||
msgstr "De tijdzone waarin de gebruiker de tijden wilt zien."
|
msgstr "De tijdzone waarin de gebruiker de tijden wilt zien."
|
||||||
|
|
||||||
#: build/lib/core/models.py:260 core/models.py:260
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
msgid "device"
|
msgid "device"
|
||||||
msgstr "apparaat"
|
msgstr "apparaat"
|
||||||
|
|
||||||
#: build/lib/core/models.py:262 core/models.py:262
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
msgid "Whether the user is a device or a real user."
|
msgid "Whether the user is a device or a real user."
|
||||||
msgstr "Of de gebruiker een apparaat is of een echte gebruiker."
|
msgstr "Of de gebruiker een apparaat is of een echte gebruiker."
|
||||||
|
|
||||||
#: build/lib/core/models.py:265 core/models.py:265
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
msgid "staff status"
|
msgid "staff status"
|
||||||
msgstr "beheerder status"
|
msgstr "beheerder status"
|
||||||
|
|
||||||
#: build/lib/core/models.py:267 core/models.py:267
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
msgid "Whether the user can log into this admin site."
|
msgid "Whether the user can log into this admin site."
|
||||||
msgstr "Of de gebruiker kan inloggen in het admin gedeelte."
|
msgstr "Of de gebruiker kan inloggen in het admin gedeelte."
|
||||||
|
|
||||||
#: build/lib/core/models.py:270 core/models.py:270
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr "actief"
|
msgstr "actief"
|
||||||
|
|
||||||
#: build/lib/core/models.py:273 core/models.py:273
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
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."
|
msgstr "Of een gebruiker als actief moet worden beschouwd. Deselecteer dit in plaats van het account te deleten."
|
||||||
|
|
||||||
#: build/lib/core/models.py:285 core/models.py:285
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr "gebruiker"
|
msgstr "gebruiker"
|
||||||
|
|
||||||
#: build/lib/core/models.py:286 core/models.py:286
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
msgid "users"
|
msgid "users"
|
||||||
msgstr "gebruikers"
|
msgstr "gebruikers"
|
||||||
|
|
||||||
#: build/lib/core/models.py:470 build/lib/core/models.py:1074
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
#: core/models.py:470 core/models.py:1074
|
#: core/models.py:470 core/models.py:1154
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "titel"
|
msgstr "titel"
|
||||||
|
|
||||||
@@ -239,136 +234,136 @@ msgstr "titel"
|
|||||||
msgid "excerpt"
|
msgid "excerpt"
|
||||||
msgstr "uittreksel"
|
msgstr "uittreksel"
|
||||||
|
|
||||||
#: build/lib/core/models.py:504 core/models.py:504
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
msgid "Document"
|
msgid "Document"
|
||||||
msgstr "Document"
|
msgstr "Document"
|
||||||
|
|
||||||
#: build/lib/core/models.py:505 core/models.py:505
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
msgid "Documents"
|
msgid "Documents"
|
||||||
msgstr "Documenten"
|
msgstr "Documenten"
|
||||||
|
|
||||||
#: build/lib/core/models.py:517 build/lib/core/models.py:826 core/models.py:517
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
#: core/models.py:826
|
#: core/models.py:872
|
||||||
msgid "Untitled Document"
|
msgid "Untitled Document"
|
||||||
msgstr "Naamloos Document"
|
msgstr "Naamloos Document"
|
||||||
|
|
||||||
#: build/lib/core/models.py:861 core/models.py:861
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you!"
|
msgid "{name} shared a document with you!"
|
||||||
msgstr "{name} heeft een document met gedeeld!"
|
msgstr "{name} heeft een document met gedeeld!"
|
||||||
|
|
||||||
#: build/lib/core/models.py:865 core/models.py:865
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
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:"
|
msgstr "{name} heeft u uitgenodigd met de rol \"{role}\" op het volgende document:"
|
||||||
|
|
||||||
#: build/lib/core/models.py:871 core/models.py:871
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} shared a document with you: {title}"
|
msgid "{name} shared a document with you: {title}"
|
||||||
msgstr "{name} heeft een document met u gedeeld: {title}"
|
msgstr "{name} heeft een document met u gedeeld: {title}"
|
||||||
|
|
||||||
#: build/lib/core/models.py:969 core/models.py:969
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
msgid "Document/user link trace"
|
msgid "Document/user link trace"
|
||||||
msgstr "Document/gebruiker url"
|
msgstr "Document/gebruiker url"
|
||||||
|
|
||||||
#: build/lib/core/models.py:970 core/models.py:970
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
msgid "Document/user link traces"
|
msgid "Document/user link traces"
|
||||||
msgstr "Document/gebruiker url"
|
msgstr "Document/gebruiker url"
|
||||||
|
|
||||||
#: build/lib/core/models.py:976 core/models.py:976
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
msgid "A link trace already exists for this document/user."
|
msgid "A link trace already exists for this document/user."
|
||||||
msgstr "Een url bestaat al voor dit document/deze gebruiker."
|
msgstr "Een url bestaat al voor dit document/deze gebruiker."
|
||||||
|
|
||||||
#: build/lib/core/models.py:999 core/models.py:999
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
msgid "Document favorite"
|
msgid "Document favorite"
|
||||||
msgstr "Document favoriet"
|
msgstr "Document favoriet"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1000 core/models.py:1000
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
msgid "Document favorites"
|
msgid "Document favorites"
|
||||||
msgstr "Document favorieten"
|
msgstr "Document favorieten"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
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."
|
msgstr "Dit document is al in gebruik als favoriete door dezelfde gebruiker."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1028 core/models.py:1028
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
msgid "Document/user relation"
|
msgid "Document/user relation"
|
||||||
msgstr "Document/gebruiker relatie"
|
msgstr "Document/gebruiker relatie"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1029 core/models.py:1029
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
msgid "Document/user relations"
|
msgid "Document/user relations"
|
||||||
msgstr "Document/gebruiker relaties"
|
msgstr "Document/gebruiker relaties"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
msgid "This user is already in this document."
|
msgid "This user is already in this document."
|
||||||
msgstr "De gebruiker is al in dit document."
|
msgstr "De gebruiker is al in dit document."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
msgid "This team is already in this document."
|
msgid "This team is already in this document."
|
||||||
msgstr "Het team is al in dit document."
|
msgstr "Het team is al in dit document."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1047 build/lib/core/models.py:1161
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
#: core/models.py:1047 core/models.py:1161
|
#: core/models.py:1093 core/models.py:1241
|
||||||
msgid "Either user or team must be set, not both."
|
msgid "Either user or team must be set, not both."
|
||||||
msgstr "Een gebruiker of team moet gekozen worden, maar niet beide."
|
msgstr "Een gebruiker of team moet gekozen worden, maar niet beide."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1075 core/models.py:1075
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr "omschrijving"
|
msgstr "omschrijving"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1076 core/models.py:1076
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
msgid "code"
|
msgid "code"
|
||||||
msgstr "code"
|
msgstr "code"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1077 core/models.py:1077
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
msgid "css"
|
msgid "css"
|
||||||
msgstr "css"
|
msgstr "css"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1079 core/models.py:1079
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
msgid "public"
|
msgid "public"
|
||||||
msgstr "publiek"
|
msgstr "publiek"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1081 core/models.py:1081
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
msgid "Whether this template is public for anyone to use."
|
msgid "Whether this template is public for anyone to use."
|
||||||
msgstr "Of dit template als publiek is en door iedereen te gebruiken is."
|
msgstr "Of dit template als publiek is en door iedereen te gebruiken is."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1087 core/models.py:1087
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr "Template"
|
msgstr "Template"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1088 core/models.py:1088
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
msgid "Templates"
|
msgid "Templates"
|
||||||
msgstr "Templates"
|
msgstr "Templates"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1142 core/models.py:1142
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
msgid "Template/user relation"
|
msgid "Template/user relation"
|
||||||
msgstr "Template/gebruiker relatie"
|
msgstr "Template/gebruiker relatie"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1143 core/models.py:1143
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
msgid "Template/user relations"
|
msgid "Template/user relations"
|
||||||
msgstr "Template/gebruiker relaties"
|
msgstr "Template/gebruiker relaties"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1149 core/models.py:1149
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
msgid "This user is already in this template."
|
msgid "This user is already in this template."
|
||||||
msgstr "De gebruiker bestaat al in dit template."
|
msgstr "De gebruiker bestaat al in dit template."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1155 core/models.py:1155
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
msgid "This team is already in this template."
|
msgid "This team is already in this template."
|
||||||
msgstr "Het team bestaat al in dit template."
|
msgstr "Het team bestaat al in dit template."
|
||||||
|
|
||||||
#: build/lib/core/models.py:1178 core/models.py:1178
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
msgid "email address"
|
msgid "email address"
|
||||||
msgstr "email adres"
|
msgstr "email adres"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1197 core/models.py:1197
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
msgid "Document invitation"
|
msgid "Document invitation"
|
||||||
msgstr "Document uitnodiging"
|
msgstr "Document uitnodiging"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1198 core/models.py:1198
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
msgid "Document invitations"
|
msgid "Document invitations"
|
||||||
msgstr "Document uitnodigingen"
|
msgstr "Document uitnodigingen"
|
||||||
|
|
||||||
#: build/lib/core/models.py:1218 core/models.py:1218
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
msgid "This email is already associated to a registered user."
|
msgid "This email is already associated to a registered user."
|
||||||
msgstr "Deze email is al geassocieerd met een geregistreerde gebruiker."
|
msgstr "Deze email is al geassocieerd met een geregistreerde gebruiker."
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Portuguese\n"
|
||||||
|
"Language: pt_PT\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: pt-PT\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 "Favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
msgid "You have been granted ownership of a new document:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
msgid "The language in which the user wants to see the interface."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
|
msgid "The timezone in which the user wants to see times."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
msgid "Whether the user can log into this admin site."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Slovenian\n"
|
||||||
|
"Language: sl_SI\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%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: sl\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 "Osebni podatki"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
||||||
|
#: core/admin.py:138
|
||||||
|
msgid "Permissions"
|
||||||
|
msgstr "Dovoljenja"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:62 core/admin.py:62
|
||||||
|
msgid "Important dates"
|
||||||
|
msgstr "Pomembni datumi"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:148 core/admin.py:148
|
||||||
|
msgid "Tree structure"
|
||||||
|
msgstr "Drevesna struktura"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "Naslov"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
|
msgid "Creator is me"
|
||||||
|
msgstr "Ustvaril sem jaz"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "Priljubljena"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
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:450 core/api/serializers.py:450
|
||||||
|
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:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr "Telo"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr "Vrsta telesa"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "Oblika"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr "Prvi otrok"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr "Zadnji otrok"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr "Prvi brat in sestra"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr "Zadnji brat in sestra"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr "Levo"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr "Desno"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "Bralec"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr "Urednik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "Skrbnik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "Lastnik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr "Omejeno"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Preverjeno"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Javno"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr "primarni ključ za zapis kot UUID"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr "ustvarjen na"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr "datum in čas, ko je bil zapis ustvarjen"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "posodobljeno dne"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
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:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
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:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
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:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "polno ime"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr "kratko ime"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr "elektronski naslov identitete"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr "elektronski naslov skrbnika"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "jezik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
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:258 core/models.py:258
|
||||||
|
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:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr "naprava"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr "Ali je uporabnik naprava ali pravi uporabnik."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr "kadrovski status"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
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:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr "aktivni"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
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:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr "uporabnik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr "uporabniki"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr "naslov"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr "odlomek"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr "Dokument"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr "Dokumenti"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr "Dokument brez naslova"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr "{name} je delil dokument z vami!"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, 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:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr "{name} je delil dokument z vami: {title}"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr "Dokument/sled povezave uporabnika"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr "Sledi povezav dokumenta/uporabnika"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr "Za ta dokument/uporabnika že obstaja sled povezave."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr "Priljubljeni dokument"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr "Priljubljeni dokumenti"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
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:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr "Odnos dokument/uporabnik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr "Odnosi dokument/uporabnik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr "Ta uporabnik je že v tem dokumentu."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr "Ta ekipa je že v tem dokumentu."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
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:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr "opis"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr "koda"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "css"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "javno"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr "Ali je ta predloga javna za uporabo."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "Predloga"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "Predloge"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr "Odnos predloga/uporabnik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr "Odnosi med predlogo in uporabnikom"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr "Ta uporabnik je že v tej predlogi."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr "Ta ekipa je že v tej predlogi."
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr "elektronski naslov"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr "Vabilo na dokument"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr "Vabila na dokument"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr "Ta e-poštni naslov je že povezan z registriranim uporabnikom."
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr "E-pošta z logotipom"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "Odpri"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr " Dokumenti, vaše novo bistveno orodje za organiziranje, skupno rabo in skupinsko sodelovanje pri dokumentih. "
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr " Pod okriljem %(brandname)s "
|
||||||
|
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Swedish\n"
|
||||||
|
"Language: sv_SE\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: sv-SE\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 "Personuppgifter"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
|
||||||
|
#: core/admin.py:138
|
||||||
|
msgid "Permissions"
|
||||||
|
msgstr "Behörigheter"
|
||||||
|
|
||||||
|
#: build/lib/core/admin.py:62 core/admin.py:62
|
||||||
|
msgid "Important dates"
|
||||||
|
msgstr "Viktiga datum"
|
||||||
|
|
||||||
|
#: 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 "Titel"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||||
|
msgid "Creator is me"
|
||||||
|
msgstr "Skaparen är jag"
|
||||||
|
|
||||||
|
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "Favoriter"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr "Ett nytt dokument skapades åt dig!"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
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:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "Format"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "Administratör"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Publik"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
msgid "The language in which the user wants to see the interface."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
|
msgid "The timezone in which the user wants to see times."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
msgid "Whether the user can log into this admin site."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr "aktiv"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr "e-postadress"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr "Bjud in dokument"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr "Inbjudningar dokument"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr "Denna e-postadress är redan associerad med en registrerad användare."
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr "Logotyp e-post"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "Öppna"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Turkish\n"
|
||||||
|
"Language: tr_TR\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: tr\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 "Favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
msgid "You have been granted ownership of a new document:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
msgid "The language in which the user wants to see the interface."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
|
msgid "The timezone in which the user wants to see times."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
msgid "Whether the user can log into this admin site."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.txt:14
|
||||||
|
msgid " Docs, your new essential tool for organizing, sharing and collaborating on your documents as a team. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: lasuite-docs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-04-29 11:48+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-05-05 07:07\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Chinese Simplified\n"
|
||||||
|
"Language: zh_CN\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
"X-Crowdin-Project: lasuite-docs\n"
|
||||||
|
"X-Crowdin-Project-ID: 754523\n"
|
||||||
|
"X-Crowdin-Language: zh-CN\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 "Favorite"
|
||||||
|
msgstr "收藏"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:446 core/api/serializers.py:446
|
||||||
|
msgid "A new document was created on your behalf!"
|
||||||
|
msgstr "已为您创建了一份新文档!"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:450 core/api/serializers.py:450
|
||||||
|
msgid "You have been granted ownership of a new document:"
|
||||||
|
msgstr "您已被授予新文档的所有权:"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:586 core/api/serializers.py:586
|
||||||
|
msgid "Body"
|
||||||
|
msgstr "正文"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:589 core/api/serializers.py:589
|
||||||
|
msgid "Body type"
|
||||||
|
msgstr "正文类型"
|
||||||
|
|
||||||
|
#: build/lib/core/api/serializers.py:595 core/api/serializers.py:595
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "格式"
|
||||||
|
|
||||||
|
#: build/lib/core/api/viewsets.py:966 core/api/viewsets.py:966
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "copy of {title}"
|
||||||
|
msgstr "{title} 的副本"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:35 core/enums.py:35
|
||||||
|
msgid "First child"
|
||||||
|
msgstr "第一个子项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:36 core/enums.py:36
|
||||||
|
msgid "Last child"
|
||||||
|
msgstr "最后一个子项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:37 core/enums.py:37
|
||||||
|
msgid "First sibling"
|
||||||
|
msgstr "第一个同级项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:38 core/enums.py:38
|
||||||
|
msgid "Last sibling"
|
||||||
|
msgstr "最后一个同级项"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:39 core/enums.py:39
|
||||||
|
msgid "Left"
|
||||||
|
msgstr "左"
|
||||||
|
|
||||||
|
#: build/lib/core/enums.py:40 core/enums.py:40
|
||||||
|
msgid "Right"
|
||||||
|
msgstr "右"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:56 build/lib/core/models.py:63 core/models.py:56
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Reader"
|
||||||
|
msgstr "阅读者"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:57 build/lib/core/models.py:64 core/models.py:57
|
||||||
|
#: core/models.py:64
|
||||||
|
msgid "Editor"
|
||||||
|
msgstr "编辑者"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:65 core/models.py:65
|
||||||
|
msgid "Administrator"
|
||||||
|
msgstr "超级管理员"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:66 core/models.py:66
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "所有者"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:77 core/models.py:77
|
||||||
|
msgid "Restricted"
|
||||||
|
msgstr "受限的"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:81 core/models.py:81
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "已验证"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:83 core/models.py:83
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "公开"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:154 core/models.py:154
|
||||||
|
msgid "id"
|
||||||
|
msgstr "id"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:155 core/models.py:155
|
||||||
|
msgid "primary key for the record as UUID"
|
||||||
|
msgstr "记录的主密钥为 UUID"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:161 core/models.py:161
|
||||||
|
msgid "created on"
|
||||||
|
msgstr "创建时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:162 core/models.py:162
|
||||||
|
msgid "date and time at which a record was created"
|
||||||
|
msgstr "记录的创建日期和时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:167 core/models.py:167
|
||||||
|
msgid "updated on"
|
||||||
|
msgstr "更新时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:168 core/models.py:168
|
||||||
|
msgid "date and time at which a record was last updated"
|
||||||
|
msgstr "记录的最后更新时间"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:204 core/models.py:204
|
||||||
|
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:217 core/models.py:217
|
||||||
|
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
|
||||||
|
msgstr "请输入有效的 sub。该值只能包含字母、数字及 @/./+/-/_/: 字符。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:223 core/models.py:223
|
||||||
|
msgid "sub"
|
||||||
|
msgstr "sub"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:225 core/models.py:225
|
||||||
|
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||||
|
msgstr "必填。最多 255 个字符,仅允许字母、数字及 @/./+/-/_/: 字符。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:234 core/models.py:234
|
||||||
|
msgid "full name"
|
||||||
|
msgstr "全名"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:235 core/models.py:235
|
||||||
|
msgid "short name"
|
||||||
|
msgstr "简称"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:237 core/models.py:237
|
||||||
|
msgid "identity email address"
|
||||||
|
msgstr "身份电子邮件地址"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:242 core/models.py:242
|
||||||
|
msgid "admin email address"
|
||||||
|
msgstr "管理员电子邮件地址"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:249 core/models.py:249
|
||||||
|
msgid "language"
|
||||||
|
msgstr "语言"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:250 core/models.py:250
|
||||||
|
msgid "The language in which the user wants to see the interface."
|
||||||
|
msgstr "用户希望看到的界面语言。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:258 core/models.py:258
|
||||||
|
msgid "The timezone in which the user wants to see times."
|
||||||
|
msgstr "用户查看时间希望的时区。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:261 core/models.py:261
|
||||||
|
msgid "device"
|
||||||
|
msgstr "设备"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:263 core/models.py:263
|
||||||
|
msgid "Whether the user is a device or a real user."
|
||||||
|
msgstr "用户是设备还是真实用户。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:266 core/models.py:266
|
||||||
|
msgid "staff status"
|
||||||
|
msgstr "员工状态"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:268 core/models.py:268
|
||||||
|
msgid "Whether the user can log into this admin site."
|
||||||
|
msgstr "用户是否可以登录该管理员站点。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:271 core/models.py:271
|
||||||
|
msgid "active"
|
||||||
|
msgstr "激活"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:274 core/models.py:274
|
||||||
|
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||||
|
msgstr "是否应将此用户视为活跃用户。取消选择此选项而不是删除账户。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:286 core/models.py:286
|
||||||
|
msgid "user"
|
||||||
|
msgstr "用户"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:287 core/models.py:287
|
||||||
|
msgid "users"
|
||||||
|
msgstr "个用户"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:470 build/lib/core/models.py:1154
|
||||||
|
#: core/models.py:470 core/models.py:1154
|
||||||
|
msgid "title"
|
||||||
|
msgstr "标题"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:471 core/models.py:471
|
||||||
|
msgid "excerpt"
|
||||||
|
msgstr "摘要"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:519 core/models.py:519
|
||||||
|
msgid "Document"
|
||||||
|
msgstr "文档"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:520 core/models.py:520
|
||||||
|
msgid "Documents"
|
||||||
|
msgstr "个文档"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:532 build/lib/core/models.py:872 core/models.py:532
|
||||||
|
#: core/models.py:872
|
||||||
|
msgid "Untitled Document"
|
||||||
|
msgstr "未命名文档"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:907 core/models.py:907
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you!"
|
||||||
|
msgstr "{name} 与您共享了一个文档!"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:911 core/models.py:911
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||||
|
msgstr "{name} 邀请您以“{role}”角色访问以下文档:"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:917 core/models.py:917
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} shared a document with you: {title}"
|
||||||
|
msgstr "{name} 与您共享了一个文档:{title}"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1015 core/models.py:1015
|
||||||
|
msgid "Document/user link trace"
|
||||||
|
msgstr "文档/用户链接跟踪"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1016 core/models.py:1016
|
||||||
|
msgid "Document/user link traces"
|
||||||
|
msgstr "个文档/用户链接跟踪"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1022 core/models.py:1022
|
||||||
|
msgid "A link trace already exists for this document/user."
|
||||||
|
msgstr "此文档/用户的链接跟踪已存在。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1045 core/models.py:1045
|
||||||
|
msgid "Document favorite"
|
||||||
|
msgstr "文档收藏"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1046 core/models.py:1046
|
||||||
|
msgid "Document favorites"
|
||||||
|
msgstr "文档收藏夹"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1052 core/models.py:1052
|
||||||
|
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||||
|
msgstr "该文档已被同一用户的收藏关系实例关联。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1074 core/models.py:1074
|
||||||
|
msgid "Document/user relation"
|
||||||
|
msgstr "文档/用户关系"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1075 core/models.py:1075
|
||||||
|
msgid "Document/user relations"
|
||||||
|
msgstr "文档/用户关系集"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1081 core/models.py:1081
|
||||||
|
msgid "This user is already in this document."
|
||||||
|
msgstr "该用户已在此文档中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1087 core/models.py:1087
|
||||||
|
msgid "This team is already in this document."
|
||||||
|
msgstr "该团队已在此文档中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1093 build/lib/core/models.py:1241
|
||||||
|
#: core/models.py:1093 core/models.py:1241
|
||||||
|
msgid "Either user or team must be set, not both."
|
||||||
|
msgstr "必须设置用户或团队之一,不能同时设置两者。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1155 core/models.py:1155
|
||||||
|
msgid "description"
|
||||||
|
msgstr "说明"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1156 core/models.py:1156
|
||||||
|
msgid "code"
|
||||||
|
msgstr "代码"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1157 core/models.py:1157
|
||||||
|
msgid "css"
|
||||||
|
msgstr "css"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1159 core/models.py:1159
|
||||||
|
msgid "public"
|
||||||
|
msgstr "公开"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1161 core/models.py:1161
|
||||||
|
msgid "Whether this template is public for anyone to use."
|
||||||
|
msgstr "该模板是否公开供任何人使用。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1167 core/models.py:1167
|
||||||
|
msgid "Template"
|
||||||
|
msgstr "模板"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1168 core/models.py:1168
|
||||||
|
msgid "Templates"
|
||||||
|
msgstr "模板"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1222 core/models.py:1222
|
||||||
|
msgid "Template/user relation"
|
||||||
|
msgstr "模板/用户关系"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1223 core/models.py:1223
|
||||||
|
msgid "Template/user relations"
|
||||||
|
msgstr "模板/用户关系集"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1229 core/models.py:1229
|
||||||
|
msgid "This user is already in this template."
|
||||||
|
msgstr "该用户已在此模板中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1235 core/models.py:1235
|
||||||
|
msgid "This team is already in this template."
|
||||||
|
msgstr "该团队已在此模板中。"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1258 core/models.py:1258
|
||||||
|
msgid "email address"
|
||||||
|
msgstr "电子邮件地址"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1277 core/models.py:1277
|
||||||
|
msgid "Document invitation"
|
||||||
|
msgstr "文档邀请"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||||
|
msgid "Document invitations"
|
||||||
|
msgstr "文档邀请"
|
||||||
|
|
||||||
|
#: build/lib/core/models.py:1298 core/models.py:1298
|
||||||
|
msgid "This email is already associated to a registered user."
|
||||||
|
msgstr "此电子邮件已经与现有注册用户关联。"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:162
|
||||||
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr "徽标邮件"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:209
|
||||||
|
#: core/templates/mail/text/invitation.txt:10
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "打开"
|
||||||
|
|
||||||
|
#: core/templates/mail/html/invitation.html:226
|
||||||
|
#: core/templates/mail/text/invitation.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/invitation.html:233
|
||||||
|
#: core/templates/mail/text/invitation.txt:16
|
||||||
|
#, python-format
|
||||||
|
msgid " Brought to you by %(brandname)s "
|
||||||
|
msgstr " 由 %(brandname)s 倾力打造。 "
|
||||||
|
|
||||||
+21
-20
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "impress"
|
name = "impress"
|
||||||
version = "3.1.0"
|
version = "3.2.1"
|
||||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
@@ -25,18 +25,18 @@ license = { file = "LICENSE" }
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"beautifulsoup4==4.13.3",
|
"beautifulsoup4==4.13.4",
|
||||||
"boto3==1.37.24",
|
"boto3==1.38.9",
|
||||||
"Brotli==1.1.0",
|
"Brotli==1.1.0",
|
||||||
"celery[redis]==5.5.0",
|
"celery[redis]==5.5.2",
|
||||||
"django-configurations==2.5.1",
|
"django-configurations==2.5.1",
|
||||||
"django-cors-headers==4.7.0",
|
"django-cors-headers==4.7.0",
|
||||||
"django-countries==7.6.1",
|
"django-countries==7.6.1",
|
||||||
"django-filter==25.1",
|
"django-filter==25.1",
|
||||||
|
"django-lasuite==0.0.7",
|
||||||
"django-parler==2.3",
|
"django-parler==2.3",
|
||||||
"redis==5.2.1",
|
|
||||||
"django-redis==5.4.0",
|
"django-redis==5.4.0",
|
||||||
"django-storages[s3]==1.14.5",
|
"django-storages[s3]==1.14.6",
|
||||||
"django-timezone-field>=5.1",
|
"django-timezone-field>=5.1",
|
||||||
"django==5.1.8",
|
"django==5.1.8",
|
||||||
"django-treebeard==4.7.1",
|
"django-treebeard==4.7.1",
|
||||||
@@ -47,17 +47,18 @@ dependencies = [
|
|||||||
"factory_boy==3.3.3",
|
"factory_boy==3.3.3",
|
||||||
"gunicorn==23.0.0",
|
"gunicorn==23.0.0",
|
||||||
"jsonschema==4.23.0",
|
"jsonschema==4.23.0",
|
||||||
"lxml==5.3.1",
|
"lxml==5.4.0",
|
||||||
"markdown==3.7",
|
"markdown==3.8",
|
||||||
"mozilla-django-oidc==4.0.1",
|
"mozilla-django-oidc==4.0.1",
|
||||||
"nested-multipart-parser==1.5.0",
|
"nested-multipart-parser==1.5.0",
|
||||||
"openai==1.70.0",
|
"openai==1.77.0",
|
||||||
"psycopg[binary]==3.2.6",
|
"psycopg[binary]==3.2.7",
|
||||||
"pycrdt==0.12.10",
|
"pycrdt==0.12.15",
|
||||||
"PyJWT==2.10.1",
|
"PyJWT==2.10.1",
|
||||||
"python-magic==0.4.27",
|
"python-magic==0.4.27",
|
||||||
|
"redis<6.0.0",
|
||||||
"requests==2.32.3",
|
"requests==2.32.3",
|
||||||
"sentry-sdk==2.25.0",
|
"sentry-sdk==2.27.0",
|
||||||
"whitenoise==6.9.0",
|
"whitenoise==6.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -69,22 +70,22 @@ dependencies = [
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"django-extensions==3.2.3",
|
"django-extensions==4.1",
|
||||||
"django-test-migrations==1.4.0",
|
"django-test-migrations==1.5.0",
|
||||||
"drf-spectacular-sidecar==2025.3.1",
|
"drf-spectacular-sidecar==2025.5.1",
|
||||||
"freezegun==1.5.1",
|
"freezegun==1.5.1",
|
||||||
"ipdb==0.13.13",
|
"ipdb==0.13.13",
|
||||||
"ipython==9.0.2",
|
"ipython==9.2.0",
|
||||||
"pyfakefs==5.8.0",
|
"pyfakefs==5.8.0",
|
||||||
"pylint-django==2.6.1",
|
"pylint-django==2.6.1",
|
||||||
"pylint==3.3.6",
|
"pylint==3.3.7",
|
||||||
"pytest-cov==6.0.0",
|
"pytest-cov==6.1.1",
|
||||||
"pytest-django==4.10.0",
|
"pytest-django==4.11.1",
|
||||||
"pytest==8.3.5",
|
"pytest==8.3.5",
|
||||||
"pytest-icdiff==0.9",
|
"pytest-icdiff==0.9",
|
||||||
"pytest-xdist==3.6.1",
|
"pytest-xdist==3.6.1",
|
||||||
"responses==0.25.7",
|
"responses==0.25.7",
|
||||||
"ruff==0.11.2",
|
"ruff==0.11.8",
|
||||||
"types-requests==2.32.0.20250328",
|
"types-requests==2.32.0.20250328",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: ["impress/playwright"],
|
extends: ['impress/playwright'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
project: ["./tsconfig.json"],
|
project: ['./tsconfig.json'],
|
||||||
},
|
},
|
||||||
ignorePatterns: ["node_modules"],
|
ignorePatterns: ['node_modules'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const CONFIG = {
|
|||||||
['fr-fr', 'Français'],
|
['fr-fr', 'Français'],
|
||||||
['de-de', 'Deutsch'],
|
['de-de', 'Deutsch'],
|
||||||
['nl-nl', 'Nederlands'],
|
['nl-nl', 'Nederlands'],
|
||||||
|
['es-es', 'Español'],
|
||||||
],
|
],
|
||||||
LANGUAGE_CODE: 'en-us',
|
LANGUAGE_CODE: 'en-us',
|
||||||
POSTHOG_KEY: {},
|
POSTHOG_KEY: {},
|
||||||
@@ -78,6 +79,7 @@ export const createDoc = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const input = page.getByLabel('doc title input');
|
const input = page.getByLabel('doc title input');
|
||||||
|
await expect(input).toBeVisible();
|
||||||
await expect(input).toHaveText('');
|
await expect(input).toHaveText('');
|
||||||
await input.click();
|
await input.click();
|
||||||
|
|
||||||
|
|||||||
@@ -79,11 +79,14 @@ test.describe('Config', () => {
|
|||||||
|
|
||||||
test('it checks that collaboration server is configured from config endpoint', async ({
|
test('it checks that collaboration server is configured from config endpoint', async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
|
||||||
}) => {
|
}) => {
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
|
|
||||||
void createDoc(page, 'doc-collaboration', browserName, 1);
|
void page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'New doc',
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
const webSocket = await page.waitForEvent('websocket', (webSocket) => {
|
const webSocket = await page.waitForEvent('websocket', (webSocket) => {
|
||||||
return webSocket.url().includes('ws://localhost:4444/collaboration/ws/');
|
return webSocket.url().includes('ws://localhost:4444/collaboration/ws/');
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ test.describe('Doc Export', () => {
|
|||||||
})
|
})
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page
|
void page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Download',
|
name: 'Download',
|
||||||
})
|
})
|
||||||
@@ -129,7 +129,7 @@ test.describe('Doc Export', () => {
|
|||||||
await page.getByRole('combobox', { name: 'Format' }).click();
|
await page.getByRole('combobox', { name: 'Format' }).click();
|
||||||
await page.getByRole('option', { name: 'Docx' }).click();
|
await page.getByRole('option', { name: 'Docx' }).click();
|
||||||
|
|
||||||
await page
|
void page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Download',
|
name: 'Download',
|
||||||
})
|
})
|
||||||
@@ -206,7 +206,7 @@ test.describe('Doc Export', () => {
|
|||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
await page
|
void page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Download',
|
name: 'Download',
|
||||||
})
|
})
|
||||||
@@ -235,7 +235,7 @@ test.describe('Doc Export', () => {
|
|||||||
// Trigger slash menu to show menu
|
// Trigger slash menu to show menu
|
||||||
await editor.click();
|
await editor.click();
|
||||||
await editor.fill('/');
|
await editor.fill('/');
|
||||||
await page.getByText('Add a quote block').click();
|
await page.getByText('Quote or excerpt').click();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
editor.locator('.bn-block-content[data-content-type="quote"]'),
|
editor.locator('.bn-block-content[data-content-type="quote"]'),
|
||||||
@@ -254,7 +254,7 @@ test.describe('Doc Export', () => {
|
|||||||
})
|
})
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page
|
void page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Download',
|
name: 'Download',
|
||||||
})
|
})
|
||||||
@@ -298,7 +298,7 @@ test.describe('Doc Export', () => {
|
|||||||
})
|
})
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page
|
void page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Download',
|
name: 'Download',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -152,12 +152,7 @@ test.describe('Document list members', () => {
|
|||||||
await expect(soloOwner).toBeHidden();
|
await expect(soloOwner).toBeHidden();
|
||||||
await list.click();
|
await list.click();
|
||||||
|
|
||||||
const otherOwner = page.getByText(
|
|
||||||
`You cannot update the role or remove other owner.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await newUserRoles.click();
|
await newUserRoles.click();
|
||||||
await expect(otherOwner).toBeVisible();
|
|
||||||
await list.click();
|
await list.click();
|
||||||
|
|
||||||
await currentUserRole.click();
|
await currentUserRole.click();
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -101,8 +103,9 @@ test.describe('Doc Routing: Not loggued', () => {
|
|||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
}) => {
|
}) => {
|
||||||
await mockedDocument(page, { link_reach: 'public' });
|
const uuid = crypto.randomUUID();
|
||||||
await page.goto('/docs/mocked-document-id/');
|
await mockedDocument(page, { link_reach: 'public', id: uuid });
|
||||||
|
await page.goto(`/docs/${uuid}/`);
|
||||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||||
await page.getByRole('button', { name: 'Login' }).click();
|
await page.getByRole('button', { name: 'Login' }).click();
|
||||||
await keyCloakSignIn(page, browserName, false);
|
await keyCloakSignIn(page, browserName, false);
|
||||||
|
|||||||
@@ -54,18 +54,11 @@ test.describe.serial('Language', () => {
|
|||||||
}) => {
|
}) => {
|
||||||
// Helper function to intercept and assert 404 response
|
// Helper function to intercept and assert 404 response
|
||||||
const check404Response = async (expectedDetail: string) => {
|
const check404Response = async (expectedDetail: string) => {
|
||||||
const expectedBackendResponse = page.waitForResponse(
|
const interceptedBackendResponse = await page.request.get(
|
||||||
(response) =>
|
'http://localhost:8071/api/v1.0/documents/non-existent-doc-uuid/',
|
||||||
response.url().includes('/api') &&
|
|
||||||
response.url().includes('non-existent-doc-uuid') &&
|
|
||||||
response.status() === 404,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Trigger the specific 404 XHR response by navigating to a non-existent document
|
|
||||||
await page.goto('/docs/non-existent-doc-uuid');
|
|
||||||
|
|
||||||
// Assert that the intercepted error message is in the expected language
|
// Assert that the intercepted error message is in the expected language
|
||||||
const interceptedBackendResponse = await expectedBackendResponse;
|
|
||||||
expect(await interceptedBackendResponse.json()).toStrictEqual({
|
expect(await interceptedBackendResponse.json()).toStrictEqual({
|
||||||
detail: expectedDetail,
|
detail: expectedDetail,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app-e2e",
|
"name": "app-e2e",
|
||||||
"version": "3.1.0",
|
"version": "3.2.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts",
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
"test:ui::chromium": "yarn test:ui --project=chromium"
|
"test:ui::chromium": "yarn test:ui --project=chromium"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.50.1",
|
"@playwright/test": "1.52.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/pdf-parse": "1.1.4",
|
"@types/pdf-parse": "1.1.5",
|
||||||
"eslint-config-impress": "*",
|
"eslint-config-impress": "*",
|
||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ server {
|
|||||||
try_files $uri index.html $uri/ =404;
|
try_files $uri index.html $uri/ =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /docs/ {
|
location ~ "^/docs/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/?$" {
|
||||||
error_page 404 /docs/[id]/;
|
try_files $uri /docs/[id]/index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_page 404 /404.html;
|
error_page 404 /404.html;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app-impress",
|
"name": "app-impress",
|
||||||
"version": "3.1.0",
|
"version": "3.2.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -15,70 +15,71 @@
|
|||||||
"test:watch": "jest --watch"
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ag-media/react-pdf-table": "2.0.1",
|
"@ag-media/react-pdf-table": "2.0.2",
|
||||||
"@blocknote/core": "0.23.2-hotfix.0",
|
"@blocknote/code-block": "0.29.1",
|
||||||
"@blocknote/mantine": "0.23.2-hotfix.0",
|
"@blocknote/core": "0.29.1",
|
||||||
"@blocknote/react": "0.23.2-hotfix.0",
|
"@blocknote/mantine": "0.29.1",
|
||||||
"@blocknote/xl-docx-exporter": "0.23.2-hotfix.0",
|
"@blocknote/react": "0.29.1",
|
||||||
"@blocknote/xl-pdf-exporter": "0.23.2-hotfix.0",
|
"@blocknote/xl-docx-exporter": "0.29.1",
|
||||||
|
"@blocknote/xl-pdf-exporter": "0.29.1",
|
||||||
"@fontsource/material-icons": "5.2.5",
|
"@fontsource/material-icons": "5.2.5",
|
||||||
"@gouvfr-lasuite/integration": "1.0.2",
|
"@gouvfr-lasuite/integration": "1.0.3",
|
||||||
"@gouvfr-lasuite/ui-kit": "0.1.3",
|
"@gouvfr-lasuite/ui-kit": "0.4.1",
|
||||||
"@hocuspocus/provider": "2.15.2",
|
"@hocuspocus/provider": "2.15.2",
|
||||||
"@openfun/cunningham-react": "3.0.0",
|
"@openfun/cunningham-react": "3.0.0",
|
||||||
"@react-pdf/renderer": "4.1.6",
|
"@react-pdf/renderer": "4.3.0",
|
||||||
"@sentry/nextjs": "9.3.0",
|
"@sentry/nextjs": "9.15.0",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.75.4",
|
||||||
"canvg": "4.0.3",
|
"canvg": "4.0.3",
|
||||||
"clsx": "2.1.1",
|
"clsx": "2.1.1",
|
||||||
"cmdk": "1.0.4",
|
"cmdk": "1.1.1",
|
||||||
"crisp-sdk-web": "1.0.25",
|
"crisp-sdk-web": "1.0.25",
|
||||||
"docx": "9.1.1",
|
"docx": "9.4.1",
|
||||||
"i18next": "24.2.2",
|
"i18next": "25.1.1",
|
||||||
"i18next-browser-languagedetector": "8.0.4",
|
"i18next-browser-languagedetector": "8.1.0",
|
||||||
"idb": "8.0.2",
|
"idb": "8.0.2",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"luxon": "3.5.0",
|
"luxon": "3.6.1",
|
||||||
"next": "15.2.4",
|
"next": "15.3.1",
|
||||||
"posthog-js": "1.227.0",
|
"posthog-js": "1.239.1",
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-aria-components": "1.6.0",
|
"react-aria-components": "1.8.0",
|
||||||
"react-dom": "*",
|
"react-dom": "*",
|
||||||
"react-i18next": "15.4.1",
|
"react-i18next": "15.5.1",
|
||||||
"react-intersection-observer": "9.15.1",
|
"react-intersection-observer": "9.16.0",
|
||||||
"react-select": "5.10.1",
|
"react-select": "5.10.1",
|
||||||
"styled-components": "6.1.15",
|
"styled-components": "6.1.17",
|
||||||
"use-debounce": "10.0.4",
|
"use-debounce": "10.0.4",
|
||||||
"y-protocols": "1.0.6",
|
"y-protocols": "1.0.6",
|
||||||
"yjs": "*",
|
"yjs": "*",
|
||||||
"zustand": "5.0.3"
|
"zustand": "5.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@svgr/webpack": "8.1.0",
|
"@svgr/webpack": "8.1.0",
|
||||||
"@tanstack/react-query-devtools": "5.67.1",
|
"@tanstack/react-query-devtools": "5.75.4",
|
||||||
"@testing-library/dom": "10.4.0",
|
"@testing-library/dom": "10.4.0",
|
||||||
"@testing-library/jest-dom": "6.6.3",
|
"@testing-library/jest-dom": "6.6.3",
|
||||||
"@testing-library/react": "16.2.0",
|
"@testing-library/react": "16.3.0",
|
||||||
"@testing-library/user-event": "14.6.1",
|
"@testing-library/user-event": "14.6.1",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
"@types/lodash": "4.17.16",
|
"@types/lodash": "4.17.16",
|
||||||
"@types/luxon": "3.4.2",
|
"@types/luxon": "3.6.2",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-dom": "*",
|
"@types/react-dom": "*",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"dotenv": "16.4.7",
|
"dotenv": "16.5.0",
|
||||||
"eslint-config-impress": "*",
|
"eslint-config-impress": "*",
|
||||||
"fetch-mock": "9.11.0",
|
"fetch-mock": "9.11.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-environment-jsdom": "29.7.0",
|
"jest-environment-jsdom": "29.7.0",
|
||||||
"node-fetch": "2.7.0",
|
"node-fetch": "2.7.0",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"stylelint": "16.15.0",
|
"stylelint": "16.19.1",
|
||||||
"stylelint-config-standard": "37.0.0",
|
"stylelint-config-standard": "38.0.0",
|
||||||
"stylelint-prettier": "5.0.3",
|
"stylelint-prettier": "5.0.3",
|
||||||
"typescript": "*",
|
"typescript": "*",
|
||||||
"webpack": "5.98.0",
|
"webpack": "5.99.7",
|
||||||
"workbox-webpack-plugin": "7.1.0"
|
"workbox-webpack-plugin": "7.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const Card = ({
|
|||||||
$background="white"
|
$background="white"
|
||||||
$radius="4px"
|
$radius="4px"
|
||||||
$css={css`
|
$css={css`
|
||||||
border: 1px solid ${colorsTokens()['greyscale-200']};
|
border: 1px solid ${colorsTokens['greyscale-200']};
|
||||||
${$css}
|
${$css}
|
||||||
`}
|
`}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ export const DropdownMenu = ({
|
|||||||
label,
|
label,
|
||||||
topMessage,
|
topMessage,
|
||||||
}: PropsWithChildren<DropdownMenuProps>) => {
|
}: PropsWithChildren<DropdownMenuProps>) => {
|
||||||
const theme = useCunninghamTheme();
|
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||||
const spacings = theme.spacingsTokens();
|
|
||||||
const colors = theme.colorsTokens();
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const blockButtonRef = useRef<HTMLDivElement>(null);
|
const blockButtonRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -120,11 +118,11 @@ export const DropdownMenu = ({
|
|||||||
key={option.label}
|
key={option.label}
|
||||||
$align="center"
|
$align="center"
|
||||||
$justify="space-between"
|
$justify="space-between"
|
||||||
$background={colors['greyscale-000']}
|
$background={colorsTokens['greyscale-000']}
|
||||||
$color={colors['primary-600']}
|
$color={colorsTokens['primary-600']}
|
||||||
$padding={{ vertical: 'xs', horizontal: 'base' }}
|
$padding={{ vertical: 'xs', horizontal: 'base' }}
|
||||||
$width="100%"
|
$width="100%"
|
||||||
$gap={spacings['base']}
|
$gap={spacingsTokens['base']}
|
||||||
$css={css`
|
$css={css`
|
||||||
border: none;
|
border: none;
|
||||||
${index === 0 &&
|
${index === 0 &&
|
||||||
@@ -148,7 +146,11 @@ export const DropdownMenu = ({
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<Box $direction="row" $align="center" $gap={spacings['base']}>
|
<Box
|
||||||
|
$direction="row"
|
||||||
|
$align="center"
|
||||||
|
$gap={spacingsTokens['base']}
|
||||||
|
>
|
||||||
{option.icon && (
|
{option.icon && (
|
||||||
<Icon
|
<Icon
|
||||||
$size="20px"
|
$size="20px"
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export const QuickSearchInput = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
|
|
||||||
if (children) {
|
if (children) {
|
||||||
return (
|
return (
|
||||||
@@ -44,7 +43,7 @@ export const QuickSearchInput = ({
|
|||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
className="quick-search-input"
|
className="quick-search-input"
|
||||||
$gap={spacing['2xs']}
|
$gap={spacingsTokens['2xs']}
|
||||||
$padding={{ all: 'base' }}
|
$padding={{ all: 'base' }}
|
||||||
>
|
>
|
||||||
{!loading && <Icon iconName="search" $variation="600" />}
|
{!loading && <Icon iconName="search" $variation="600" />}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export const QuickSearchItemContent = ({
|
|||||||
right,
|
right,
|
||||||
}: QuickSearchItemContentProps) => {
|
}: QuickSearchItemContentProps) => {
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
|
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ export const QuickSearchItemContent = ({
|
|||||||
<Box
|
<Box
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['2xs']}
|
$gap={spacingsTokens['2xs']}
|
||||||
$width="100%"
|
$width="100%"
|
||||||
>
|
>
|
||||||
{left}
|
{left}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const HorizontalSeparator = ({
|
|||||||
$background={
|
$background={
|
||||||
variant === SeparatorVariant.DARK
|
variant === SeparatorVariant.DARK
|
||||||
? '#e5e5e533'
|
? '#e5e5e533'
|
||||||
: colorsTokens()['greyscale-100']
|
: colorsTokens['greyscale-100']
|
||||||
}
|
}
|
||||||
className="--docs--horizontal-separator"
|
className="--docs--horizontal-separator"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -13,17 +13,15 @@ export const SeparatedSection = ({
|
|||||||
showSeparator = true,
|
showSeparator = true,
|
||||||
children,
|
children,
|
||||||
}: PropsWithChildren<Props>) => {
|
}: PropsWithChildren<Props>) => {
|
||||||
const theme = useCunninghamTheme();
|
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const colors = theme.colorsTokens();
|
|
||||||
const spacings = theme.spacingsTokens();
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
$css={css`
|
$css={css`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: ${spacings['sm']} 0;
|
padding: ${spacingsTokens['sm']} 0;
|
||||||
${showSeparator &&
|
${showSeparator &&
|
||||||
css`
|
css`
|
||||||
border-bottom: 1px solid ${colors?.['greyscale-200']};
|
border-bottom: 1px solid ${colorsTokens['greyscale-200']};
|
||||||
`}
|
`}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,6 +19,21 @@ interface ConfigResponse {
|
|||||||
SENTRY_DSN?: string;
|
SENTRY_DSN?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LOCAL_STORAGE_KEY = 'docs_config';
|
||||||
|
|
||||||
|
function getCachedTranslation() {
|
||||||
|
try {
|
||||||
|
const jsonString = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
|
return jsonString ? (JSON.parse(jsonString) as ConfigResponse) : undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCachedTranslation(translations: ConfigResponse) {
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(translations));
|
||||||
|
}
|
||||||
|
|
||||||
export const getConfig = async (): Promise<ConfigResponse> => {
|
export const getConfig = async (): Promise<ConfigResponse> => {
|
||||||
const response = await fetchAPI(`config/`);
|
const response = await fetchAPI(`config/`);
|
||||||
|
|
||||||
@@ -26,15 +41,25 @@ export const getConfig = async (): Promise<ConfigResponse> => {
|
|||||||
throw new APIError('Failed to get the doc', await errorCauses(response));
|
throw new APIError('Failed to get the doc', await errorCauses(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json() as Promise<ConfigResponse>;
|
const config = response.json() as Promise<ConfigResponse>;
|
||||||
|
setCachedTranslation(await config);
|
||||||
|
|
||||||
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const KEY_CONFIG = 'config';
|
export const KEY_CONFIG = 'config';
|
||||||
|
|
||||||
export function useConfig() {
|
export function useConfig() {
|
||||||
return useQuery<ConfigResponse, APIError, ConfigResponse>({
|
const cachedData = getCachedTranslation();
|
||||||
|
const oneHour = 1000 * 60 * 60;
|
||||||
|
|
||||||
|
const response = useQuery<ConfigResponse, APIError, ConfigResponse>({
|
||||||
queryKey: [KEY_CONFIG],
|
queryKey: [KEY_CONFIG],
|
||||||
queryFn: () => getConfig(),
|
queryFn: () => getConfig(),
|
||||||
staleTime: Infinity,
|
initialData: cachedData,
|
||||||
|
staleTime: oneHour,
|
||||||
|
initialDataUpdatedAt: Date.now() - oneHour, // Force initial data to be considered stale
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ describe('<useCunninghamTheme />', () => {
|
|||||||
it('has the logo correctly set', () => {
|
it('has the logo correctly set', () => {
|
||||||
const { themeTokens, setTheme } = useCunninghamTheme.getState();
|
const { themeTokens, setTheme } = useCunninghamTheme.getState();
|
||||||
setTheme('default');
|
setTheme('default');
|
||||||
const logo = themeTokens().logo;
|
const logo = themeTokens.logo;
|
||||||
expect(logo?.src).toBe('/assets/logo-gouv.svg');
|
expect(logo?.src).toBe('/assets/logo-gouv.svg');
|
||||||
expect(logo?.widthHeader).toBe('110px');
|
expect(logo?.widthHeader).toBe('110px');
|
||||||
expect(logo?.widthFooter).toBe('220px');
|
expect(logo?.widthFooter).toBe('220px');
|
||||||
|
|||||||
@@ -3,39 +3,53 @@ import { create } from 'zustand';
|
|||||||
|
|
||||||
import { tokens } from './cunningham-tokens';
|
import { tokens } from './cunningham-tokens';
|
||||||
|
|
||||||
type Tokens = typeof tokens.themes.default;
|
type Tokens = typeof tokens.themes.default &
|
||||||
|
Partial<(typeof tokens.themes)[keyof typeof tokens.themes]>;
|
||||||
type ColorsTokens = Tokens['theme']['colors'];
|
type ColorsTokens = Tokens['theme']['colors'];
|
||||||
type FontSizesTokens = Tokens['theme']['font']['sizes'];
|
type FontSizesTokens = Tokens['theme']['font']['sizes'];
|
||||||
type SpacingsTokens = Tokens['theme']['spacings'];
|
type SpacingsTokens = Tokens['theme']['spacings'];
|
||||||
type ComponentTokens = Tokens['components'];
|
type ComponentTokens = Tokens['components'];
|
||||||
export type Theme = keyof typeof tokens.themes;
|
export type Theme = keyof typeof tokens.themes;
|
||||||
|
|
||||||
interface AuthStore {
|
interface ThemeStore {
|
||||||
theme: string;
|
theme: Theme;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
themeTokens: () => Partial<Tokens['theme']>;
|
themeTokens: Partial<Tokens['theme']>;
|
||||||
colorsTokens: () => Partial<ColorsTokens>;
|
colorsTokens: Partial<ColorsTokens>;
|
||||||
fontSizesTokens: () => Partial<FontSizesTokens>;
|
fontSizesTokens: Partial<FontSizesTokens>;
|
||||||
spacingsTokens: () => Partial<SpacingsTokens>;
|
spacingsTokens: Partial<SpacingsTokens>;
|
||||||
componentTokens: () => ComponentTokens;
|
componentTokens: ComponentTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCunninghamTheme = create<AuthStore>((set, get) => {
|
const getMergedTokens = (theme: Theme) => {
|
||||||
const currentTheme = () =>
|
return merge({}, tokens.themes['default'], tokens.themes[theme]);
|
||||||
merge(
|
};
|
||||||
tokens.themes['default'],
|
|
||||||
tokens.themes[get().theme as keyof typeof tokens.themes],
|
|
||||||
) as Tokens;
|
|
||||||
|
|
||||||
return {
|
const DEFAULT_THEME: Theme = 'default';
|
||||||
theme: 'default',
|
const defaultTokens = getMergedTokens(DEFAULT_THEME);
|
||||||
themeTokens: () => currentTheme().theme,
|
|
||||||
colorsTokens: () => currentTheme().theme.colors,
|
const initialState: ThemeStore = {
|
||||||
componentTokens: () => currentTheme().components,
|
theme: DEFAULT_THEME,
|
||||||
spacingsTokens: () => currentTheme().theme.spacings,
|
setTheme: () => {},
|
||||||
fontSizesTokens: () => currentTheme().theme.font.sizes,
|
themeTokens: defaultTokens.theme,
|
||||||
setTheme: (theme: Theme) => {
|
colorsTokens: defaultTokens.theme.colors,
|
||||||
set({ theme });
|
componentTokens: defaultTokens.components,
|
||||||
},
|
spacingsTokens: defaultTokens.theme.spacings,
|
||||||
};
|
fontSizesTokens: defaultTokens.theme.font.sizes,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
export const useCunninghamTheme = create<ThemeStore>((set) => ({
|
||||||
|
...initialState,
|
||||||
|
setTheme: (theme: Theme) => {
|
||||||
|
const newTokens = getMergedTokens(theme);
|
||||||
|
|
||||||
|
set({
|
||||||
|
theme,
|
||||||
|
themeTokens: newTokens.theme,
|
||||||
|
colorsTokens: newTokens.theme.colors,
|
||||||
|
componentTokens: newTokens.components,
|
||||||
|
spacingsTokens: newTokens.theme.spacings,
|
||||||
|
fontSizesTokens: newTokens.theme.font.sizes,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|||||||
+11
-5
@@ -1,11 +1,11 @@
|
|||||||
|
import { codeBlock } from '@blocknote/code-block';
|
||||||
import {
|
import {
|
||||||
BlockNoteSchema,
|
BlockNoteSchema,
|
||||||
Dictionary,
|
|
||||||
defaultBlockSpecs,
|
defaultBlockSpecs,
|
||||||
locales,
|
|
||||||
withPageBreak,
|
withPageBreak,
|
||||||
} from '@blocknote/core';
|
} from '@blocknote/core';
|
||||||
import '@blocknote/core/fonts/inter.css';
|
import '@blocknote/core/fonts/inter.css';
|
||||||
|
import * as locales from '@blocknote/core/locales';
|
||||||
import { BlockNoteView } from '@blocknote/mantine';
|
import { BlockNoteView } from '@blocknote/mantine';
|
||||||
import '@blocknote/mantine/style.css';
|
import '@blocknote/mantine/style.css';
|
||||||
import { useCreateBlockNote } from '@blocknote/react';
|
import { useCreateBlockNote } from '@blocknote/react';
|
||||||
@@ -27,14 +27,13 @@ import { randomColor } from '../utils';
|
|||||||
|
|
||||||
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
||||||
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
||||||
import { DividerBlock, QuoteBlock } from './custom-blocks';
|
import { DividerBlock } from './custom-blocks';
|
||||||
|
|
||||||
export const blockNoteSchema = withPageBreak(
|
export const blockNoteSchema = withPageBreak(
|
||||||
BlockNoteSchema.create({
|
BlockNoteSchema.create({
|
||||||
blockSpecs: {
|
blockSpecs: {
|
||||||
...defaultBlockSpecs,
|
...defaultBlockSpecs,
|
||||||
divider: DividerBlock,
|
divider: DividerBlock,
|
||||||
quote: QuoteBlock,
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -63,6 +62,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
|
|
||||||
const editor = useCreateBlockNote(
|
const editor = useCreateBlockNote(
|
||||||
{
|
{
|
||||||
|
codeBlock,
|
||||||
collaboration: {
|
collaboration: {
|
||||||
provider,
|
provider,
|
||||||
fragment: provider.document.getXmlFragment('document-store'),
|
fragment: provider.document.getXmlFragment('document-store'),
|
||||||
@@ -112,7 +112,13 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
},
|
},
|
||||||
showCursorLabels: showCursorLabels as 'always' | 'activity',
|
showCursorLabels: showCursorLabels as 'always' | 'activity',
|
||||||
},
|
},
|
||||||
dictionary: locales[lang as keyof typeof locales] as Dictionary,
|
dictionary: locales[lang as keyof typeof locales],
|
||||||
|
tables: {
|
||||||
|
splitCells: true,
|
||||||
|
cellBackgroundColor: true,
|
||||||
|
cellTextColor: true,
|
||||||
|
headers: true,
|
||||||
|
},
|
||||||
uploadFile,
|
uploadFile,
|
||||||
schema: blockNoteSchema,
|
schema: blockNoteSchema,
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-5
@@ -11,10 +11,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { DocsBlockSchema } from '../types';
|
import { DocsBlockSchema } from '../types';
|
||||||
|
|
||||||
import {
|
import { getDividerReactSlashMenuItems } from './custom-blocks';
|
||||||
getDividerReactSlashMenuItems,
|
|
||||||
getQuoteReactSlashMenuItems,
|
|
||||||
} from './custom-blocks';
|
|
||||||
|
|
||||||
export const BlockNoteSuggestionMenu = () => {
|
export const BlockNoteSuggestionMenu = () => {
|
||||||
const editor = useBlockNoteEditor<DocsBlockSchema>();
|
const editor = useBlockNoteEditor<DocsBlockSchema>();
|
||||||
@@ -28,7 +25,6 @@ export const BlockNoteSuggestionMenu = () => {
|
|||||||
combineByGroup(
|
combineByGroup(
|
||||||
getDefaultReactSlashMenuItems(editor),
|
getDefaultReactSlashMenuItems(editor),
|
||||||
getPageBreakReactSlashMenuItems(editor),
|
getPageBreakReactSlashMenuItems(editor),
|
||||||
getQuoteReactSlashMenuItems(editor, t, basicBlocksName),
|
|
||||||
getDividerReactSlashMenuItems(editor, t, basicBlocksName),
|
getDividerReactSlashMenuItems(editor, t, basicBlocksName),
|
||||||
),
|
),
|
||||||
query,
|
query,
|
||||||
|
|||||||
+1
-6
@@ -6,12 +6,9 @@ import {
|
|||||||
useDictionary,
|
useDictionary,
|
||||||
} from '@blocknote/react';
|
} from '@blocknote/react';
|
||||||
import React, { JSX, useCallback, useMemo, useState } from 'react';
|
import React, { JSX, useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { useConfig } from '@/core/config/api';
|
import { useConfig } from '@/core/config/api';
|
||||||
|
|
||||||
import { getQuoteFormattingToolbarItems } from '../custom-blocks';
|
|
||||||
|
|
||||||
import { AIGroupButton } from './AIButton';
|
import { AIGroupButton } from './AIButton';
|
||||||
import { FileDownloadButton } from './FileDownloadButton';
|
import { FileDownloadButton } from './FileDownloadButton';
|
||||||
import { MarkdownButton } from './MarkdownButton';
|
import { MarkdownButton } from './MarkdownButton';
|
||||||
@@ -21,13 +18,11 @@ export const BlockNoteToolbar = () => {
|
|||||||
const dict = useDictionary();
|
const dict = useDictionary();
|
||||||
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
||||||
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
|
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
|
||||||
const { t } = useTranslation();
|
|
||||||
const { data: conf } = useConfig();
|
const { data: conf } = useConfig();
|
||||||
|
|
||||||
const toolbarItems = useMemo(() => {
|
const toolbarItems = useMemo(() => {
|
||||||
const toolbarItems = getFormattingToolbarItems([
|
const toolbarItems = getFormattingToolbarItems([
|
||||||
...blockTypeSelectItems(dict),
|
...blockTypeSelectItems(dict),
|
||||||
getQuoteFormattingToolbarItems(t),
|
|
||||||
]);
|
]);
|
||||||
const fileDownloadButtonIndex = toolbarItems.findIndex(
|
const fileDownloadButtonIndex = toolbarItems.findIndex(
|
||||||
(item) =>
|
(item) =>
|
||||||
@@ -51,7 +46,7 @@ export const BlockNoteToolbar = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return toolbarItems as JSX.Element[];
|
return toolbarItems as JSX.Element[];
|
||||||
}, [dict, t]);
|
}, [dict]);
|
||||||
|
|
||||||
const formattingToolbar = useCallback(() => {
|
const formattingToolbar = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
+6
-1
@@ -15,6 +15,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
import { RiDownload2Fill } from 'react-icons/ri';
|
import { RiDownload2Fill } from 'react-icons/ri';
|
||||||
|
|
||||||
import { downloadFile, exportResolveFileUrl } from '@/docs/doc-export';
|
import { downloadFile, exportResolveFileUrl } from '@/docs/doc-export';
|
||||||
|
import { isSafeUrl } from '@/utils/url';
|
||||||
|
|
||||||
export const FileDownloadButton = ({
|
export const FileDownloadButton = ({
|
||||||
open,
|
open,
|
||||||
@@ -59,7 +60,11 @@ export const FileDownloadButton = ({
|
|||||||
*/
|
*/
|
||||||
if (!url.includes(window.location.hostname) && !url.includes('base64')) {
|
if (!url.includes(window.location.hostname) && !url.includes('base64')) {
|
||||||
if (!editor.resolveFileUrl) {
|
if (!editor.resolveFileUrl) {
|
||||||
window.open(url);
|
if (!isSafeUrl(url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer');
|
||||||
} else {
|
} else {
|
||||||
void editor
|
void editor
|
||||||
.resolveFileUrl(url)
|
.resolveFileUrl(url)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
$background={colorsTokens()['primary-bg']}
|
$background={colorsTokens['primary-bg']}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$width="100%"
|
$width="100%"
|
||||||
$css="overflow-x: clip; flex: 1;"
|
$css="overflow-x: clip; flex: 1;"
|
||||||
|
|||||||
+2
-2
@@ -22,9 +22,9 @@ export const DividerBlock = createReactBlockSpec(
|
|||||||
<Box
|
<Box
|
||||||
as="hr"
|
as="hr"
|
||||||
$width="100%"
|
$width="100%"
|
||||||
$background={colorsTokens()['greyscale-300']}
|
$background={colorsTokens['greyscale-300']}
|
||||||
$margin="1rem 0"
|
$margin="1rem 0"
|
||||||
$css={`border: 1px solid ${colorsTokens()['greyscale-300']};`}
|
$css={`border: 1px solid ${colorsTokens['greyscale-300']};`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
-68
@@ -1,68 +0,0 @@
|
|||||||
import { defaultProps, insertOrUpdateBlock } from '@blocknote/core';
|
|
||||||
import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react';
|
|
||||||
import { TFunction } from 'i18next';
|
|
||||||
|
|
||||||
import { Box, Icon } from '@/components';
|
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
|
||||||
|
|
||||||
import { DocsBlockNoteEditor } from '../../types';
|
|
||||||
|
|
||||||
export const QuoteBlock = createReactBlockSpec(
|
|
||||||
{
|
|
||||||
type: 'quote',
|
|
||||||
propSchema: {
|
|
||||||
textAlignment: defaultProps.textAlignment,
|
|
||||||
},
|
|
||||||
content: 'inline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
render: (props) => {
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
const { colorsTokens } = useCunninghamTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
as="blockquote"
|
|
||||||
className="inline-content"
|
|
||||||
$margin="0 0 1rem 0"
|
|
||||||
$padding="0.5rem 1rem"
|
|
||||||
style={{
|
|
||||||
borderLeft: `4px solid ${colorsTokens()['greyscale-300']}`,
|
|
||||||
fontStyle: 'italic',
|
|
||||||
flexGrow: 1,
|
|
||||||
}}
|
|
||||||
$color="var(--c--theme--colors--greyscale-500)"
|
|
||||||
ref={props.contentRef}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getQuoteReactSlashMenuItems = (
|
|
||||||
editor: DocsBlockNoteEditor,
|
|
||||||
t: TFunction<'translation', undefined>,
|
|
||||||
group: string,
|
|
||||||
) => [
|
|
||||||
{
|
|
||||||
title: t('Quote'),
|
|
||||||
onItemClick: () => {
|
|
||||||
insertOrUpdateBlock(editor, {
|
|
||||||
type: 'quote',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
aliases: ['quote', 'blockquote', 'citation'],
|
|
||||||
group,
|
|
||||||
icon: <Icon iconName="format_quote" $size="18px" />,
|
|
||||||
subtext: t('Add a quote block'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const getQuoteFormattingToolbarItems = (
|
|
||||||
t: TFunction<'translation', undefined>,
|
|
||||||
): BlockTypeSelectItem => ({
|
|
||||||
name: t('Quote'),
|
|
||||||
type: 'quote',
|
|
||||||
icon: () => <Icon iconName="format_quote" $size="16px" />,
|
|
||||||
isSelected: (block) => block.type === 'quote',
|
|
||||||
});
|
|
||||||
-1
@@ -1,2 +1 @@
|
|||||||
export * from './DividerBlock';
|
export * from './DividerBlock';
|
||||||
export * from './QuoteBlock';
|
|
||||||
|
|||||||
@@ -57,9 +57,6 @@ export const cssEditor = (readonly: boolean) => css`
|
|||||||
.bn-side-menu[data-block-type='heading'][data-level='3'] {
|
.bn-side-menu[data-block-type='heading'][data-level='3'] {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
}
|
}
|
||||||
.bn-side-menu[data-block-type='quote'] {
|
|
||||||
height: 46px;
|
|
||||||
}
|
|
||||||
.bn-side-menu[data-block-type='divider'] {
|
.bn-side-menu[data-block-type='divider'] {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
}
|
}
|
||||||
@@ -84,11 +81,19 @@ export const cssEditor = (readonly: boolean) => css`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bn-editor {
|
& .bn-editor {
|
||||||
color: var(--c--theme--colors--greyscale-700);
|
color: var(--c--theme--colors--greyscale-700);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quotes
|
||||||
|
*/
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid var(--c--theme--colors--greyscale-300);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bn-block-outer:not(:first-child) {
|
& .bn-block-outer:not(:first-child) {
|
||||||
&:has(h1) {
|
&:has(h1) {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
}
|
}
|
||||||
@@ -105,12 +110,6 @@ export const cssEditor = (readonly: boolean) => css`
|
|||||||
padding: 2px;
|
padding: 2px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
& .bn-inline-content {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.bn-block-content[data-content-type='checkListItem'] > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (width <= 768px) {
|
@media screen and (width <= 768px) {
|
||||||
& .bn-editor {
|
& .bn-editor {
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ export const blockMappingDividerDocx: DocsExporterDocx['mappings']['blockMapping
|
|||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
top: {
|
top: {
|
||||||
color: colorsTokens()['greyscale-300'],
|
color: colorsTokens['greyscale-300'],
|
||||||
size: 1,
|
size: 1,
|
||||||
style: 'single',
|
style: 'single',
|
||||||
space: 1,
|
space: 1,
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ export const blockMappingDividerPDF: DocsExporterPDF['mappings']['blockMapping']
|
|||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
marginVertical: 10,
|
marginVertical: 10,
|
||||||
backgroundColor: colorsTokens()['greyscale-300'],
|
backgroundColor: colorsTokens['greyscale-300'],
|
||||||
height: '2px',
|
height: '2px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+103
-29
@@ -1,46 +1,120 @@
|
|||||||
import { TD, TH, TR, Table } from '@ag-media/react-pdf-table';
|
/**
|
||||||
import { View } from '@react-pdf/renderer';
|
* We use mainly the Blocknotes code, mixed with @ag-media/react-pdf-table
|
||||||
|
* to have a better Table support.
|
||||||
|
* See:
|
||||||
|
* https://github.com/TypeCellOS/BlockNote/blob/004c0bf720fe1415c497ad56449015c5f4dd7ba0/packages/xl-pdf-exporter/src/pdf/util/table/Table.tsx
|
||||||
|
*
|
||||||
|
* We succeded to manage the colspan, but rowspan is not supported yet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TD, TR, Table } from '@ag-media/react-pdf-table';
|
||||||
|
import { mapTableCell } from '@blocknote/core';
|
||||||
|
import { StyleSheet, Text } from '@react-pdf/renderer';
|
||||||
|
|
||||||
import { DocsExporterPDF } from '../types';
|
import { DocsExporterPDF } from '../types';
|
||||||
|
const PIXELS_PER_POINT = 0.75;
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
tableContainer: {
|
||||||
|
border: '1px solid #ddd',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
paddingHorizontal: 5 * PIXELS_PER_POINT,
|
||||||
|
paddingTop: 3 * PIXELS_PER_POINT,
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
},
|
||||||
|
headerCell: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const blockMappingTablePDF: DocsExporterPDF['mappings']['blockMapping']['table'] =
|
export const blockMappingTablePDF: DocsExporterPDF['mappings']['blockMapping']['table'] =
|
||||||
(block, exporter) => {
|
(block, exporter) => {
|
||||||
|
const { options } = exporter;
|
||||||
|
const blockContent = block.content;
|
||||||
|
|
||||||
|
// If headerRows is 1, then the first row is a header row
|
||||||
|
const headerRows = new Array(blockContent.headerRows ?? 0).fill(
|
||||||
|
true,
|
||||||
|
) as boolean[];
|
||||||
|
// If headerCols is 1, then the first column is a header column
|
||||||
|
const headerCols = new Array(blockContent.headerCols ?? 0).fill(
|
||||||
|
true,
|
||||||
|
) as boolean[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the table scale based on the column widths.
|
||||||
|
*/
|
||||||
|
const columnWidths = blockContent.columnWidths.map((w) => w || 120);
|
||||||
|
const fullWidth = 730;
|
||||||
|
const totalWidth = Math.min(
|
||||||
|
columnWidths.reduce((sum, w) => sum + w, 0),
|
||||||
|
fullWidth,
|
||||||
|
);
|
||||||
|
const tableScale = (totalWidth * 100) / fullWidth;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table style={[styles.tableContainer, { width: `${tableScale}%` }]}>
|
||||||
{block.content.rows.map((row, index) => {
|
{blockContent.rows.map((row, rowIndex) => {
|
||||||
if (index === 0) {
|
const isHeaderRow = headerRows[rowIndex];
|
||||||
return (
|
|
||||||
<TH key={index}>
|
|
||||||
{row.cells.map((cell, index) => {
|
|
||||||
// Make empty cells are rendered.
|
|
||||||
if (cell.length === 0) {
|
|
||||||
cell.push({
|
|
||||||
styles: {},
|
|
||||||
text: ' ',
|
|
||||||
type: 'text',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TD key={index}>{exporter.transformInlineContent(cell)}</TD>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TH>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<TR key={index}>
|
<TR key={rowIndex}>
|
||||||
{row.cells.map((cell, index) => {
|
{row.cells.map((c, colIndex) => {
|
||||||
// Make empty cells are rendered.
|
const formatCell = mapTableCell(c);
|
||||||
if (cell.length === 0) {
|
|
||||||
|
const isHeaderCol = headerCols[colIndex];
|
||||||
|
|
||||||
|
const cell = formatCell.content;
|
||||||
|
const cellProps = formatCell.props;
|
||||||
|
|
||||||
|
// Make empty cells rendered.
|
||||||
|
if (Array.isArray(cell) && cell.length === 0) {
|
||||||
cell.push({
|
cell.push({
|
||||||
styles: {},
|
styles: {},
|
||||||
text: ' ',
|
text: ' ',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const weight = columnWidths
|
||||||
|
.slice(colIndex, colIndex + (cellProps.colspan || 1))
|
||||||
|
.reduce((sum, w) => sum + w, 0);
|
||||||
|
|
||||||
|
const flexCell = {
|
||||||
|
flex: `${weight} ${weight} 0%`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const arrayStyle = [
|
||||||
|
isHeaderRow || isHeaderCol ? styles.headerCell : {},
|
||||||
|
flexCell,
|
||||||
|
{
|
||||||
|
color:
|
||||||
|
cellProps.textColor === 'default'
|
||||||
|
? undefined
|
||||||
|
: options.colors[
|
||||||
|
cellProps.textColor as keyof typeof options.colors
|
||||||
|
].text,
|
||||||
|
backgroundColor:
|
||||||
|
cellProps.backgroundColor === 'default'
|
||||||
|
? undefined
|
||||||
|
: options.colors[
|
||||||
|
cellProps.backgroundColor as keyof typeof options.colors
|
||||||
|
].background,
|
||||||
|
textAlign: cellProps.textAlignment,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TD key={index}>
|
<TD key={colIndex} style={arrayStyle}>
|
||||||
<View>{exporter.transformInlineContent(cell)}</View>
|
<Text style={styles.cell}>
|
||||||
|
{exporter.transformInlineContent(cell)}
|
||||||
|
</Text>
|
||||||
</TD>
|
</TD>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
VariantType,
|
VariantType,
|
||||||
useToastProvider,
|
useToastProvider,
|
||||||
} from '@openfun/cunningham-react';
|
} from '@openfun/cunningham-react';
|
||||||
import { pdf } from '@react-pdf/renderer';
|
import { DocumentProps, pdf } from '@react-pdf/renderer';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { css } from 'styled-components';
|
import { css } from 'styled-components';
|
||||||
@@ -92,7 +92,10 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
|
|||||||
const exporter = new PDFExporter(editor.schema, pdfDocsSchemaMappings, {
|
const exporter = new PDFExporter(editor.schema, pdfDocsSchemaMappings, {
|
||||||
resolveFileUrl: async (url) => exportCorsResolveFileUrl(doc.id, url),
|
resolveFileUrl: async (url) => exportCorsResolveFileUrl(doc.id, url),
|
||||||
});
|
});
|
||||||
const pdfDocument = await exporter.toReactPDFDocument(exportDocument);
|
const pdfDocument = (await exporter.toReactPDFDocument(
|
||||||
|
exportDocument,
|
||||||
|
)) as React.ReactElement<DocumentProps>;
|
||||||
|
|
||||||
blobExport = await pdf(pdfDocument).toBlob();
|
blobExport = await pdf(pdfDocument).toBlob();
|
||||||
} else {
|
} else {
|
||||||
const exporter = new DOCXExporter(editor.schema, docxDocsSchemaMappings, {
|
const exporter = new DOCXExporter(editor.schema, docxDocsSchemaMappings, {
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ interface DocHeaderProps {
|
|||||||
export const DocHeader = ({ doc }: DocHeaderProps) => {
|
export const DocHeader = ({ doc }: DocHeaderProps) => {
|
||||||
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const colors = colorsTokens();
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const docIsPublic = doc.link_reach === LinkReach.PUBLIC;
|
const docIsPublic = doc.link_reach === LinkReach.PUBLIC;
|
||||||
@@ -36,21 +34,21 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
|
|||||||
<Box
|
<Box
|
||||||
$width="100%"
|
$width="100%"
|
||||||
$padding={{ top: isDesktop ? '4xl' : 'md' }}
|
$padding={{ top: isDesktop ? '4xl' : 'md' }}
|
||||||
$gap={spacings['base']}
|
$gap={spacingsTokens['base']}
|
||||||
aria-label={t('It is the card information about the document.')}
|
aria-label={t('It is the card information about the document.')}
|
||||||
className="--docs--doc-header"
|
className="--docs--doc-header"
|
||||||
>
|
>
|
||||||
{(docIsPublic || docIsAuth) && (
|
{(docIsPublic || docIsAuth) && (
|
||||||
<Box
|
<Box
|
||||||
aria-label={t('Public document')}
|
aria-label={t('Public document')}
|
||||||
$color={colors['primary-800']}
|
$color={colorsTokens['primary-800']}
|
||||||
$background={colors['primary-050']}
|
$background={colorsTokens['primary-050']}
|
||||||
$radius={spacings['3xs']}
|
$radius={spacingsTokens['3xs']}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$padding="xs"
|
$padding="xs"
|
||||||
$flex={1}
|
$flex={1}
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$css={css`
|
$css={css`
|
||||||
border: 1px solid var(--c--theme--colors--primary-300, #e3e3fd);
|
border: 1px solid var(--c--theme--colors--primary-300, #e3e3fd);
|
||||||
`}
|
`}
|
||||||
@@ -82,7 +80,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
|
|||||||
$align="center"
|
$align="center"
|
||||||
$maxWidth="100%"
|
$maxWidth="100%"
|
||||||
>
|
>
|
||||||
<Box $gap={spacings['3xs']} $overflow="auto">
|
<Box $gap={spacingsTokens['3xs']} $overflow="auto">
|
||||||
<DocTitle doc={doc} />
|
<DocTitle doc={doc} />
|
||||||
|
|
||||||
<Box $direction="row">
|
<Box $direction="row">
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
|||||||
onBlurCapture={(event) =>
|
onBlurCapture={(event) =>
|
||||||
handleTitleSubmit(event.target.textContent || '')
|
handleTitleSubmit(event.target.textContent || '')
|
||||||
}
|
}
|
||||||
$color={colorsTokens()['greyscale-1000']}
|
$color={colorsTokens['greyscale-1000']}
|
||||||
$minHeight="40px"
|
$minHeight="40px"
|
||||||
$padding={{ right: 'big' }}
|
$padding={{ right: 'big' }}
|
||||||
$css={css`
|
$css={css`
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
|
|
||||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const colors = colorsTokens();
|
|
||||||
|
|
||||||
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
||||||
const [isModalExportOpen, setIsModalExportOpen] = useState(false);
|
const [isModalExportOpen, setIsModalExportOpen] = useState(false);
|
||||||
const selectHistoryModal = useModal();
|
const selectHistoryModal = useModal();
|
||||||
@@ -182,7 +179,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$margin={{ left: 'auto' }}
|
$margin={{ left: 'auto' }}
|
||||||
$gap={spacings['2xs']}
|
$gap={spacingsTokens['2xs']}
|
||||||
>
|
>
|
||||||
{!isSmallMobile && (
|
{!isSmallMobile && (
|
||||||
<>
|
<>
|
||||||
@@ -245,12 +242,12 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
$css={css`
|
$css={css`
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${colors['greyscale-100']};
|
background-color: ${colorsTokens['greyscale-100']};
|
||||||
}
|
}
|
||||||
${isSmallMobile
|
${isSmallMobile
|
||||||
? css`
|
? css`
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid ${colors['greyscale-300']};
|
border: 1px solid ${colorsTokens['greyscale-300']};
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
`}
|
`}
|
||||||
|
|||||||
+1
-2
@@ -12,7 +12,6 @@ interface DocVersionHeaderProps {
|
|||||||
export const DocVersionHeader = ({ title }: DocVersionHeaderProps) => {
|
export const DocVersionHeader = ({ title }: DocVersionHeaderProps) => {
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -20,7 +19,7 @@ export const DocVersionHeader = ({ title }: DocVersionHeaderProps) => {
|
|||||||
<Box
|
<Box
|
||||||
$width="100%"
|
$width="100%"
|
||||||
$padding={{ vertical: 'base' }}
|
$padding={{ vertical: 'base' }}
|
||||||
$gap={spacings['base']}
|
$gap={spacingsTokens['base']}
|
||||||
aria-label={t('It is the document title')}
|
aria-label={t('It is the document title')}
|
||||||
className="--docs--doc-version-header"
|
className="--docs--doc-version-header"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -12,34 +12,11 @@ export const useTrans = () => {
|
|||||||
[Role.OWNER]: t('Owner'),
|
[Role.OWNER]: t('Owner'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNotAllowedMessage = (
|
|
||||||
canUpdate: boolean,
|
|
||||||
isLastOwner: boolean,
|
|
||||||
isOtherOwner: boolean,
|
|
||||||
) => {
|
|
||||||
if (!canUpdate) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLastOwner) {
|
|
||||||
return t(
|
|
||||||
'You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOtherOwner) {
|
|
||||||
return t('You cannot update the role or remove other owner.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transRole: (role: Role) => {
|
transRole: (role: Role) => {
|
||||||
return translatedRoles[role];
|
return translatedRoles[role];
|
||||||
},
|
},
|
||||||
untitledDocument: t('Untitled document'),
|
untitledDocument: t('Untitled document'),
|
||||||
translatedRoles,
|
translatedRoles,
|
||||||
getNotAllowedMessage,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+22
-25
@@ -3,32 +3,22 @@ import { css } from 'styled-components';
|
|||||||
import { DropdownMenu, DropdownMenuOption, Text } from '@/components';
|
import { DropdownMenu, DropdownMenuOption, Text } from '@/components';
|
||||||
import { Role, useTrans } from '@/docs/doc-management/';
|
import { Role, useTrans } from '@/docs/doc-management/';
|
||||||
|
|
||||||
type Props = {
|
type DocRoleDropdownProps = {
|
||||||
currentRole: Role;
|
|
||||||
onSelectRole?: (role: Role) => void;
|
|
||||||
canUpdate?: boolean;
|
canUpdate?: boolean;
|
||||||
isLastOwner?: boolean;
|
currentRole: Role;
|
||||||
isOtherOwner?: boolean;
|
message?: string;
|
||||||
|
onSelectRole: (role: Role) => void;
|
||||||
|
rolesAllowed?: Role[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DocRoleDropdown = ({
|
export const DocRoleDropdown = ({
|
||||||
canUpdate = true,
|
canUpdate = true,
|
||||||
currentRole,
|
currentRole,
|
||||||
|
message,
|
||||||
onSelectRole,
|
onSelectRole,
|
||||||
isLastOwner,
|
rolesAllowed,
|
||||||
isOtherOwner,
|
}: DocRoleDropdownProps) => {
|
||||||
}: Props) => {
|
const { transRole, translatedRoles } = useTrans();
|
||||||
const { transRole, translatedRoles, getNotAllowedMessage } = useTrans();
|
|
||||||
|
|
||||||
const roles: DropdownMenuOption[] = Object.keys(translatedRoles).map(
|
|
||||||
(key) => {
|
|
||||||
return {
|
|
||||||
label: transRole(key as Role),
|
|
||||||
callback: () => onSelectRole?.(key as Role),
|
|
||||||
disabled: isLastOwner || isOtherOwner,
|
|
||||||
isSelected: currentRole === (key as Role),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!canUpdate) {
|
if (!canUpdate) {
|
||||||
return (
|
return (
|
||||||
@@ -38,13 +28,20 @@ export const DocRoleDropdown = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const roles: DropdownMenuOption[] = Object.keys(translatedRoles).map(
|
||||||
|
(key) => {
|
||||||
|
return {
|
||||||
|
label: transRole(key as Role),
|
||||||
|
callback: () => onSelectRole?.(key as Role),
|
||||||
|
disabled: rolesAllowed && !rolesAllowed.includes(key as Role),
|
||||||
|
isSelected: currentRole === (key as Role),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
topMessage={getNotAllowedMessage(
|
topMessage={message}
|
||||||
canUpdate,
|
|
||||||
!!isLastOwner,
|
|
||||||
!!isOtherOwner,
|
|
||||||
)}
|
|
||||||
label="doc-role-dropdown"
|
label="doc-role-dropdown"
|
||||||
showArrow={true}
|
showArrow={true}
|
||||||
options={roles}
|
options={roles}
|
||||||
|
|||||||
+6
-8
@@ -43,8 +43,6 @@ export const DocShareAddMemberList = ({
|
|||||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||||
const [invitationRole, setInvitationRole] = useState<Role>(Role.EDITOR);
|
const [invitationRole, setInvitationRole] = useState<Role>(Role.EDITOR);
|
||||||
const canShare = doc.abilities.accesses_manage;
|
const canShare = doc.abilities.accesses_manage;
|
||||||
const spacing = spacingsTokens();
|
|
||||||
const color = colorsTokens();
|
|
||||||
|
|
||||||
const { mutateAsync: createInvitation } = useCreateDocInvitation();
|
const { mutateAsync: createInvitation } = useCreateDocInvitation();
|
||||||
const { mutateAsync: createDocAccess } = useCreateDocAccess();
|
const { mutateAsync: createDocAccess } = useCreateDocAccess();
|
||||||
@@ -115,12 +113,12 @@ export const DocShareAddMemberList = ({
|
|||||||
<Box
|
<Box
|
||||||
data-testid="doc-share-add-member-list"
|
data-testid="doc-share-add-member-list"
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$padding={spacing.sm}
|
$padding={spacingsTokens.sm}
|
||||||
$align="center"
|
$align="center"
|
||||||
$background={color['greyscale-050']}
|
$background={colorsTokens['greyscale-050']}
|
||||||
$radius={spacing['3xs']}
|
$radius={spacingsTokens['3xs']}
|
||||||
$css={css`
|
$css={css`
|
||||||
border: 1px solid ${color['greyscale-200']};
|
border: 1px solid ${colorsTokens['greyscale-200']};
|
||||||
`}
|
`}
|
||||||
className="--docs--doc-share-add-member-list"
|
className="--docs--doc-share-add-member-list"
|
||||||
>
|
>
|
||||||
@@ -129,7 +127,7 @@ export const DocShareAddMemberList = ({
|
|||||||
$align="center"
|
$align="center"
|
||||||
$wrap="wrap"
|
$wrap="wrap"
|
||||||
$flex={1}
|
$flex={1}
|
||||||
$gap={spacing.xs}
|
$gap={spacingsTokens.xs}
|
||||||
>
|
>
|
||||||
{selectedUsers.map((user) => (
|
{selectedUsers.map((user) => (
|
||||||
<DocShareAddMemberListItem
|
<DocShareAddMemberListItem
|
||||||
@@ -139,7 +137,7 @@ export const DocShareAddMemberList = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Box $direction="row" $align="center" $gap={spacing.xs}>
|
<Box $direction="row" $align="center" $gap={spacingsTokens.xs}>
|
||||||
<DocRoleDropdown
|
<DocRoleDropdown
|
||||||
canUpdate={canShare}
|
canUpdate={canShare}
|
||||||
currentRole={invitationRole}
|
currentRole={invitationRole}
|
||||||
|
|||||||
+9
-11
@@ -12,27 +12,25 @@ type Props = {
|
|||||||
export const DocShareAddMemberListItem = ({ user, onRemoveUser }: Props) => {
|
export const DocShareAddMemberListItem = ({ user, onRemoveUser }: Props) => {
|
||||||
const { spacingsTokens, colorsTokens, fontSizesTokens } =
|
const { spacingsTokens, colorsTokens, fontSizesTokens } =
|
||||||
useCunninghamTheme();
|
useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
const color = colorsTokens();
|
|
||||||
const fontSize = fontSizesTokens();
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
data-testid={`doc-share-add-member-${user.email}`}
|
data-testid={`doc-share-add-member-${user.email}`}
|
||||||
$radius={spacing['3xs']}
|
$radius={spacingsTokens['3xs']}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$height="fit-content"
|
$height="fit-content"
|
||||||
$justify="center"
|
$justify="center"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacing['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$background={color['greyscale-250']}
|
$background={colorsTokens['greyscale-250']}
|
||||||
$padding={{
|
$padding={{
|
||||||
left: spacing['xs'],
|
left: spacingsTokens['xs'],
|
||||||
right: spacing['4xs'],
|
right: spacingsTokens['4xs'],
|
||||||
vertical: spacing['4xs'],
|
vertical: spacingsTokens['4xs'],
|
||||||
}}
|
}}
|
||||||
$css={css`
|
$css={css`
|
||||||
color: ${color['greyscale-1000']};
|
color: ${colorsTokens['greyscale-1000']};
|
||||||
font-size: ${fontSize['xs']};
|
font-size: ${fontSizesTokens['xs']};
|
||||||
`}
|
`}
|
||||||
className="--docs--doc-share-add-member-list-item"
|
className="--docs--doc-share-add-member-list-item"
|
||||||
>
|
>
|
||||||
|
|||||||
+1
-2
@@ -24,7 +24,6 @@ type Props = {
|
|||||||
export const DocShareInvitationItem = ({ doc, invitation }: Props) => {
|
export const DocShareInvitationItem = ({ doc, invitation }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
const fakeUser: User = {
|
const fakeUser: User = {
|
||||||
id: invitation.email,
|
id: invitation.email,
|
||||||
full_name: invitation.email,
|
full_name: invitation.email,
|
||||||
@@ -91,7 +90,7 @@ export const DocShareInvitationItem = ({ doc, invitation }: Props) => {
|
|||||||
alwaysShowRight={true}
|
alwaysShowRight={true}
|
||||||
user={fakeUser}
|
user={fakeUser}
|
||||||
right={
|
right={
|
||||||
<Box $direction="row" $align="center" $gap={spacing['2xs']}>
|
<Box $direction="row" $align="center" $gap={spacingsTokens['2xs']}>
|
||||||
<DocRoleDropdown
|
<DocRoleDropdown
|
||||||
currentRole={invitation.role}
|
currentRole={invitation.role}
|
||||||
onSelectRole={onUpdate}
|
onSelectRole={onUpdate}
|
||||||
|
|||||||
+11
-8
@@ -23,13 +23,16 @@ type Props = {
|
|||||||
};
|
};
|
||||||
export const DocShareMemberItem = ({ doc, access }: Props) => {
|
export const DocShareMemberItem = ({ doc, access }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isLastOwner, isOtherOwner } = useWhoAmI(access);
|
const { isLastOwner } = useWhoAmI(access);
|
||||||
const { toast } = useToastProvider();
|
const { toast } = useToastProvider();
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
const isNotAllowed =
|
const message = isLastOwner
|
||||||
isOtherOwner || !!isLastOwner || !doc.abilities.accesses_manage;
|
? t(
|
||||||
|
'You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.',
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const { mutate: updateDocAccess } = useUpdateDocAccess({
|
const { mutate: updateDocAccess } = useUpdateDocAccess({
|
||||||
onError: () => {
|
onError: () => {
|
||||||
@@ -64,7 +67,7 @@ export const DocShareMemberItem = ({ doc, access }: Props) => {
|
|||||||
label: t('Delete'),
|
label: t('Delete'),
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
callback: onRemove,
|
callback: onRemove,
|
||||||
disabled: isNotAllowed,
|
disabled: !access.abilities.destroy,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -78,13 +81,13 @@ export const DocShareMemberItem = ({ doc, access }: Props) => {
|
|||||||
alwaysShowRight={true}
|
alwaysShowRight={true}
|
||||||
user={access.user}
|
user={access.user}
|
||||||
right={
|
right={
|
||||||
<Box $direction="row" $align="center" $gap={spacing['2xs']}>
|
<Box $direction="row" $align="center" $gap={spacingsTokens['2xs']}>
|
||||||
<DocRoleDropdown
|
<DocRoleDropdown
|
||||||
currentRole={access.role}
|
currentRole={access.role}
|
||||||
onSelectRole={onUpdate}
|
onSelectRole={onUpdate}
|
||||||
canUpdate={doc.abilities.accesses_manage}
|
canUpdate={doc.abilities.accesses_manage}
|
||||||
isLastOwner={isLastOwner}
|
message={message}
|
||||||
isOtherOwner={!!isOtherOwner}
|
rolesAllowed={access.abilities.set_role_to}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isDesktop && doc.abilities.accesses_manage && (
|
{isDesktop && doc.abilities.accesses_manage && (
|
||||||
|
|||||||
+117
-88
@@ -70,10 +70,6 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
|
|||||||
docId: doc.id,
|
docId: doc.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const invitationQuery = useDocInvitationsInfinite({
|
|
||||||
docId: doc.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const searchUsersQuery = useUsers(
|
const searchUsersQuery = useUsers(
|
||||||
{ query: userQuery, docId: doc.id },
|
{ query: userQuery, docId: doc.id },
|
||||||
{
|
{
|
||||||
@@ -107,52 +103,6 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
|
|||||||
};
|
};
|
||||||
}, [membersQuery, t]);
|
}, [membersQuery, t]);
|
||||||
|
|
||||||
const invitationsData: QuickSearchData<Invitation> = useMemo(() => {
|
|
||||||
const invitations =
|
|
||||||
invitationQuery.data?.pages.flatMap((page) => page.results) || [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
groupName: t('Pending invitations'),
|
|
||||||
elements: invitations,
|
|
||||||
endActions: invitationQuery.hasNextPage
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
content: <LoadMoreText data-testid="load-more-invitations" />,
|
|
||||||
onSelect: () => void invitationQuery.fetchNextPage(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
}, [invitationQuery, t]);
|
|
||||||
|
|
||||||
const searchUserData: QuickSearchData<User> = useMemo(() => {
|
|
||||||
const users = searchUsersQuery.data || [];
|
|
||||||
const isEmail = isValidEmail(userQuery);
|
|
||||||
const newUser: User = {
|
|
||||||
id: userQuery,
|
|
||||||
full_name: '',
|
|
||||||
email: userQuery,
|
|
||||||
short_name: '',
|
|
||||||
language: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasEmailInUsers = users.some((user) => user.email === userQuery);
|
|
||||||
|
|
||||||
return {
|
|
||||||
groupName: t('Search user result'),
|
|
||||||
elements: users,
|
|
||||||
endActions:
|
|
||||||
isEmail && !hasEmailInUsers
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
content: <DocShareModalInviteUserRow user={newUser} />,
|
|
||||||
onSelect: () => void onSelect(newUser),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
}, [searchUsersQuery.data, t, userQuery]);
|
|
||||||
|
|
||||||
const onFilter = useDebouncedCallback((str: string) => {
|
const onFilter = useDebouncedCallback((str: string) => {
|
||||||
setUserQuery(str);
|
setUserQuery(str);
|
||||||
}, 300);
|
}, 300);
|
||||||
@@ -254,44 +204,17 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
|
|||||||
loading={searchUsersQuery.isLoading}
|
loading={searchUsersQuery.isLoading}
|
||||||
placeholder={t('Type a name or email')}
|
placeholder={t('Type a name or email')}
|
||||||
>
|
>
|
||||||
{canViewAccesses && (
|
{showMemberSection ? (
|
||||||
<>
|
<QuickSearchMemberSection
|
||||||
{!showMemberSection && inputValue !== '' && (
|
doc={doc}
|
||||||
<QuickSearchGroup
|
membersData={membersData}
|
||||||
group={searchUserData}
|
/>
|
||||||
onSelect={onSelect}
|
) : (
|
||||||
renderElement={(user) => (
|
<QuickSearchInviteInputSection
|
||||||
<DocShareModalInviteUserRow user={user} />
|
searchUsersRawData={searchUsersQuery.data}
|
||||||
)}
|
onSelect={onSelect}
|
||||||
/>
|
userQuery={userQuery}
|
||||||
)}
|
/>
|
||||||
{showMemberSection && (
|
|
||||||
<>
|
|
||||||
{invitationsData.elements.length > 0 && (
|
|
||||||
<Box aria-label={t('List invitation card')}>
|
|
||||||
<QuickSearchGroup
|
|
||||||
group={invitationsData}
|
|
||||||
renderElement={(invitation) => (
|
|
||||||
<DocShareInvitationItem
|
|
||||||
doc={doc}
|
|
||||||
invitation={invitation}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Box aria-label={t('List members card')}>
|
|
||||||
<QuickSearchGroup
|
|
||||||
group={membersData}
|
|
||||||
renderElement={(access) => (
|
|
||||||
<DocShareMemberItem doc={doc} access={access} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</QuickSearch>
|
</QuickSearch>
|
||||||
)}
|
)}
|
||||||
@@ -306,3 +229,109 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface QuickSearchInviteInputSectionProps {
|
||||||
|
onSelect: (usr: User) => void;
|
||||||
|
searchUsersRawData: User[] | undefined;
|
||||||
|
userQuery: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuickSearchInviteInputSection = ({
|
||||||
|
onSelect,
|
||||||
|
searchUsersRawData,
|
||||||
|
userQuery,
|
||||||
|
}: QuickSearchInviteInputSectionProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const searchUserData: QuickSearchData<User> = useMemo(() => {
|
||||||
|
const users = searchUsersRawData || [];
|
||||||
|
const isEmail = isValidEmail(userQuery);
|
||||||
|
const newUser: User = {
|
||||||
|
id: userQuery,
|
||||||
|
full_name: '',
|
||||||
|
email: userQuery,
|
||||||
|
short_name: '',
|
||||||
|
language: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasEmailInUsers = users.some((user) => user.email === userQuery);
|
||||||
|
|
||||||
|
return {
|
||||||
|
groupName: t('Search user result'),
|
||||||
|
elements: users,
|
||||||
|
endActions:
|
||||||
|
isEmail && !hasEmailInUsers
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
content: <DocShareModalInviteUserRow user={newUser} />,
|
||||||
|
onSelect: () => void onSelect(newUser),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
}, [onSelect, searchUsersRawData, t, userQuery]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QuickSearchGroup
|
||||||
|
group={searchUserData}
|
||||||
|
onSelect={onSelect}
|
||||||
|
renderElement={(user) => <DocShareModalInviteUserRow user={user} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface QuickSearchMemberSectionProps {
|
||||||
|
doc: Doc;
|
||||||
|
membersData: QuickSearchData<Access>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuickSearchMemberSection = ({
|
||||||
|
doc,
|
||||||
|
membersData,
|
||||||
|
}: QuickSearchMemberSectionProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { data, hasNextPage, fetchNextPage } = useDocInvitationsInfinite({
|
||||||
|
docId: doc.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const invitationsData: QuickSearchData<Invitation> = useMemo(() => {
|
||||||
|
const invitations = data?.pages.flatMap((page) => page.results) || [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
groupName: t('Pending invitations'),
|
||||||
|
elements: invitations,
|
||||||
|
endActions: hasNextPage
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
content: <LoadMoreText data-testid="load-more-invitations" />,
|
||||||
|
onSelect: () => void fetchNextPage(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
}, [data?.pages, fetchNextPage, hasNextPage, t]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{invitationsData.elements.length > 0 && (
|
||||||
|
<Box aria-label={t('List invitation card')}>
|
||||||
|
<QuickSearchGroup
|
||||||
|
group={invitationsData}
|
||||||
|
renderElement={(invitation) => (
|
||||||
|
<DocShareInvitationItem doc={doc} invitation={invitation} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box aria-label={t('List members card')}>
|
||||||
|
<QuickSearchGroup
|
||||||
|
group={membersData}
|
||||||
|
renderElement={(access) => (
|
||||||
|
<DocShareMemberItem doc={doc} access={access} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
|||||||
const { toast } = useToastProvider();
|
const { toast } = useToastProvider();
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
const colors = colorsTokens();
|
|
||||||
const canManage = doc.abilities.accesses_manage;
|
const canManage = doc.abilities.accesses_manage;
|
||||||
const [linkReach, setLinkReach] = useState<LinkReach>(doc.link_reach);
|
const [linkReach, setLinkReach] = useState<LinkReach>(doc.link_reach);
|
||||||
const [docLinkRole, setDocLinkRole] = useState<LinkRole>(doc.link_role);
|
const [docLinkRole, setDocLinkRole] = useState<LinkRole>(doc.link_role);
|
||||||
@@ -90,7 +88,7 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
|||||||
<Box
|
<Box
|
||||||
$padding={{ horizontal: 'base' }}
|
$padding={{ horizontal: 'base' }}
|
||||||
aria-label={t('Doc visibility card')}
|
aria-label={t('Doc visibility card')}
|
||||||
$gap={spacing['base']}
|
$gap={spacingsTokens['base']}
|
||||||
className="--docs--doc-visibility"
|
className="--docs--doc-visibility"
|
||||||
>
|
>
|
||||||
<Text $weight="700" $size="sm" $variation="700">
|
<Text $weight="700" $size="sm" $variation="700">
|
||||||
@@ -100,7 +98,7 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
|||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$justify="space-between"
|
$justify="space-between"
|
||||||
$gap={spacing['xs']}
|
$gap={spacingsTokens['xs']}
|
||||||
$width="100%"
|
$width="100%"
|
||||||
$wrap="nowrap"
|
$wrap="nowrap"
|
||||||
>
|
>
|
||||||
@@ -108,18 +106,18 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
|||||||
$direction="row"
|
$direction="row"
|
||||||
$align={isDesktop ? 'center' : undefined}
|
$align={isDesktop ? 'center' : undefined}
|
||||||
$padding={{ horizontal: '2xs' }}
|
$padding={{ horizontal: '2xs' }}
|
||||||
$gap={canManage ? spacing['3xs'] : spacing['base']}
|
$gap={canManage ? spacingsTokens['3xs'] : spacingsTokens['base']}
|
||||||
>
|
>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
label={t('Visibility')}
|
label={t('Visibility')}
|
||||||
arrowCss={css`
|
arrowCss={css`
|
||||||
color: ${colors['primary-800']} !important;
|
color: ${colorsTokens['primary-800']} !important;
|
||||||
`}
|
`}
|
||||||
disabled={!canManage}
|
disabled={!canManage}
|
||||||
showArrow={true}
|
showArrow={true}
|
||||||
options={linkReachOptions}
|
options={linkReachOptions}
|
||||||
>
|
>
|
||||||
<Box $direction="row" $align="center" $gap={spacing['3xs']}>
|
<Box $direction="row" $align="center" $gap={spacingsTokens['3xs']}>
|
||||||
<Icon
|
<Icon
|
||||||
$theme={canManage ? 'primary' : 'greyscale'}
|
$theme={canManage ? 'primary' : 'greyscale'}
|
||||||
$variation={canManage ? '800' : '600'}
|
$variation={canManage ? '800' : '600'}
|
||||||
@@ -142,7 +140,7 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{showLinkRoleOptions && (
|
{showLinkRoleOptions && (
|
||||||
<Box $direction="row" $align="center" $gap={spacing['3xs']}>
|
<Box $direction="row" $align="center" $gap={spacingsTokens['3xs']}>
|
||||||
{linkReach !== LinkReach.RESTRICTED && (
|
{linkReach !== LinkReach.RESTRICTED && (
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
disabled={!canManage}
|
disabled={!canManage}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ export const SearchUserRow = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const hasFullName = user.full_name != null && user.full_name !== '';
|
const hasFullName = user.full_name != null && user.full_name !== '';
|
||||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const colors = colorsTokens();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QuickSearchItemContent
|
<QuickSearchItemContent
|
||||||
@@ -34,12 +32,14 @@ export const SearchUserRow = ({
|
|||||||
<Box
|
<Box
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['xs']}
|
$gap={spacingsTokens['xs']}
|
||||||
className="--docs--search-user-row"
|
className="--docs--search-user-row"
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
user={user}
|
user={user}
|
||||||
background={isInvitation ? colors['greyscale-400'] : undefined}
|
background={
|
||||||
|
isInvitation ? colorsTokens['greyscale-400'] : undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Box $direction="column">
|
<Box $direction="column">
|
||||||
<Text $size="sm" $weight="500" $variation="1000">
|
<Text $size="sm" $weight="500" $variation="1000">
|
||||||
|
|||||||
+1
-1
@@ -58,7 +58,7 @@ export const Heading = ({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
$radius="4px"
|
$radius="4px"
|
||||||
$background={isActive ? `${colorsTokens()['greyscale-100']}` : 'none'}
|
$background={isActive ? `${colorsTokens['greyscale-100']}` : 'none'}
|
||||||
$css="text-align: left;"
|
$css="text-align: left;"
|
||||||
className="--docs--table-content-heading"
|
className="--docs--table-content-heading"
|
||||||
>
|
>
|
||||||
|
|||||||
+1
-2
@@ -13,7 +13,6 @@ export const TableContent = () => {
|
|||||||
const { headings } = useHeadingStore();
|
const { headings } = useHeadingStore();
|
||||||
const { editor } = useEditorStore();
|
const { editor } = useEditorStore();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
|
|
||||||
const [headingIdHighlight, setHeadingIdHighlight] = useState<string>();
|
const [headingIdHighlight, setHeadingIdHighlight] = useState<string>();
|
||||||
|
|
||||||
@@ -158,7 +157,7 @@ export const TableContent = () => {
|
|||||||
</BoxButton>
|
</BoxButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
$gap={spacing['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$css={css`
|
$css={css`
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
`}
|
`}
|
||||||
|
|||||||
+3
-4
@@ -24,7 +24,6 @@ export const VersionItem = ({
|
|||||||
isActive,
|
isActive,
|
||||||
}: VersionItemProps) => {
|
}: VersionItemProps) => {
|
||||||
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
|
|
||||||
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
|
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
|
||||||
|
|
||||||
@@ -33,13 +32,13 @@ export const VersionItem = ({
|
|||||||
<Box
|
<Box
|
||||||
$width="100%"
|
$width="100%"
|
||||||
as="li"
|
as="li"
|
||||||
$background={isActive ? colorsTokens()['greyscale-100'] : 'transparent'}
|
$background={isActive ? colorsTokens['greyscale-100'] : 'transparent'}
|
||||||
$radius={spacing['3xs']}
|
$radius={spacingsTokens['3xs']}
|
||||||
$css={`
|
$css={`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${colorsTokens()['greyscale-100']};
|
background: ${colorsTokens['greyscale-100']};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
$hasTransition
|
$hasTransition
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
|||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const { flexLeft, flexRight } = useResponsiveDocGrid();
|
const { flexLeft, flexRight } = useResponsiveDocGrid();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const shareModal = useModal();
|
const shareModal = useModal();
|
||||||
const isPublic = doc.link_reach === LinkReach.PUBLIC;
|
const isPublic = doc.link_reach === LinkReach.PUBLIC;
|
||||||
const isAuthenticated = doc.link_reach === LinkReach.AUTHENTICATED;
|
const isAuthenticated = doc.link_reach === LinkReach.AUTHENTICATED;
|
||||||
@@ -63,7 +62,7 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
|||||||
data-testid={`docs-grid-name-${doc.id}`}
|
data-testid={`docs-grid-name-${doc.id}`}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings.xs}
|
$gap={spacingsTokens.xs}
|
||||||
$flex={flexLeft}
|
$flex={flexLeft}
|
||||||
$padding={{ right: isDesktop ? 'md' : '3xs' }}
|
$padding={{ right: isDesktop ? 'md' : '3xs' }}
|
||||||
$maxWidth="100%"
|
$maxWidth="100%"
|
||||||
@@ -119,7 +118,7 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
|||||||
</StyledLink>
|
</StyledLink>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box $direction="row" $align="center" $gap={spacings.lg}>
|
<Box $direction="row" $align="center" $gap={spacingsTokens.lg}>
|
||||||
{isDesktop && (
|
{isDesktop && (
|
||||||
<DocsGridItemSharedButton
|
<DocsGridItemSharedButton
|
||||||
doc={doc}
|
doc={doc}
|
||||||
|
|||||||
@@ -34,13 +34,12 @@ export const SimpleDocItem = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const { untitledDocument } = useTrans();
|
const { untitledDocument } = useTrans();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$gap={spacings.sm}
|
$gap={spacingsTokens.sm}
|
||||||
$overflow="auto"
|
$overflow="auto"
|
||||||
className="--docs--simple-doc-item"
|
className="--docs--simple-doc-item"
|
||||||
>
|
>
|
||||||
@@ -73,7 +72,7 @@ export const SimpleDocItem = ({
|
|||||||
<Box
|
<Box
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$margin={{ top: '-2px' }}
|
$margin={{ top: '-2px' }}
|
||||||
>
|
>
|
||||||
<Text $variation="600" $size="xs">
|
<Text $variation="600" $size="xs">
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const BlueStripe = styled.div`
|
|||||||
export const Footer = () => {
|
export const Footer = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { themeTokens } = useCunninghamTheme();
|
const { themeTokens } = useCunninghamTheme();
|
||||||
const logo = themeTokens().logo;
|
const logo = themeTokens.logo;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box $position="relative" as="footer" className="--docs--footer">
|
<Box $position="relative" as="footer" className="--docs--footer">
|
||||||
|
|||||||
@@ -16,12 +16,9 @@ import { Title } from './Title';
|
|||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useCunninghamTheme();
|
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
|
|
||||||
const spacings = theme.spacingsTokens();
|
|
||||||
const colors = theme.colorsTokens();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
as="header"
|
as="header"
|
||||||
@@ -35,9 +32,9 @@ export const Header = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: ${HEADER_HEIGHT}px;
|
height: ${HEADER_HEIGHT}px;
|
||||||
padding: 0 ${spacings['base']};
|
padding: 0 ${spacingsTokens['base']};
|
||||||
background-color: ${colors['greyscale-000']};
|
background-color: ${colorsTokens['greyscale-000']};
|
||||||
border-bottom: 1px solid ${colors['greyscale-200']};
|
border-bottom: 1px solid ${colorsTokens['greyscale-200']};
|
||||||
`}
|
`}
|
||||||
className="--docs--header"
|
className="--docs--header"
|
||||||
>
|
>
|
||||||
@@ -45,7 +42,7 @@ export const Header = () => {
|
|||||||
<StyledLink href="/">
|
<StyledLink href="/">
|
||||||
<Box
|
<Box
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$position="relative"
|
$position="relative"
|
||||||
$height="fit-content"
|
$height="fit-content"
|
||||||
@@ -56,11 +53,11 @@ export const Header = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
{!isDesktop ? (
|
{!isDesktop ? (
|
||||||
<Box $direction="row" $gap={spacings['sm']}>
|
<Box $direction="row" $gap={spacingsTokens['sm']}>
|
||||||
<LaGaufre />
|
<LaGaufre />
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box $align="center" $gap={spacings['sm']} $direction="row">
|
<Box $align="center" $gap={spacingsTokens['sm']} $direction="row">
|
||||||
<ButtonLogin />
|
<ButtonLogin />
|
||||||
<LanguagePicker />
|
<LanguagePicker />
|
||||||
<LaGaufre />
|
<LaGaufre />
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const GaufreStyle = createGlobalStyle`
|
|||||||
export const LaGaufre = () => {
|
export const LaGaufre = () => {
|
||||||
const { componentTokens } = useCunninghamTheme();
|
const { componentTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
if (!componentTokens()['la-gauffre'].activated) {
|
if (!componentTokens['la-gauffre'].activated) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import { useCunninghamTheme } from '@/cunningham';
|
|||||||
|
|
||||||
export const Title = () => {
|
export const Title = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = theme.spacingsTokens();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['2xs']}
|
$gap={spacingsTokens['2xs']}
|
||||||
className="--docs--title"
|
className="--docs--title"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ import { getHeaderHeight } from './HomeHeader';
|
|||||||
export default function HomeBanner() {
|
export default function HomeBanner() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { componentTokens, spacingsTokens } = useCunninghamTheme();
|
const { componentTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const { isMobile, isSmallMobile } = useResponsiveStore();
|
const { isMobile, isSmallMobile } = useResponsiveStore();
|
||||||
const withProConnect = componentTokens()['home-proconnect'].activated;
|
const withProConnect = componentTokens['home-proconnect'].activated;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -45,7 +44,7 @@ export default function HomeBanner() {
|
|||||||
$width={!isMobile ? '50%' : '100%'}
|
$width={!isMobile ? '50%' : '100%'}
|
||||||
$justify="center"
|
$justify="center"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['sm']}
|
$gap={spacingsTokens['sm']}
|
||||||
>
|
>
|
||||||
<Image src={DocLogo} alt="DocLogo" width={64} />
|
<Image src={DocLogo} alt="DocLogo" width={64} />
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useResponsiveStore } from '@/stores';
|
|||||||
|
|
||||||
export function HomeBottom() {
|
export function HomeBottom() {
|
||||||
const { componentTokens } = useCunninghamTheme();
|
const { componentTokens } = useCunninghamTheme();
|
||||||
const withProConnect = componentTokens()['home-proconnect'].activated;
|
const withProConnect = componentTokens['home-proconnect'].activated;
|
||||||
|
|
||||||
if (!withProConnect) {
|
if (!withProConnect) {
|
||||||
return null;
|
return null;
|
||||||
@@ -22,7 +22,6 @@ export function HomeBottom() {
|
|||||||
function HomeProConnect() {
|
function HomeProConnect() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const { isMobile } = useResponsiveStore();
|
const { isMobile } = useResponsiveStore();
|
||||||
const parentGap = '230px';
|
const parentGap = '230px';
|
||||||
|
|
||||||
@@ -33,14 +32,14 @@ function HomeProConnect() {
|
|||||||
className="--docs--home-proconnect"
|
className="--docs--home-proconnect"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
$gap={spacings['md']}
|
$gap={spacingsTokens['md']}
|
||||||
$direction="column"
|
$direction="column"
|
||||||
$align="center"
|
$align="center"
|
||||||
$margin={{ top: isMobile ? 'none' : `-${parentGap}` }}
|
$margin={{ top: isMobile ? 'none' : `-${parentGap}` }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$position="relative"
|
$position="relative"
|
||||||
$height="fit-content"
|
$height="fit-content"
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export function HomeContent() {
|
|||||||
<Box
|
<Box
|
||||||
$css={css`
|
$css={css`
|
||||||
& a {
|
& a {
|
||||||
color: ${colorsTokens()['primary-600']};
|
color: ${colorsTokens['primary-600']};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ export const getHeaderHeight = (isSmallMobile: boolean) =>
|
|||||||
export const HomeHeader = () => {
|
export const HomeHeader = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { themeTokens, spacingsTokens } = useCunninghamTheme();
|
const { themeTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
const logo = themeTokens.logo;
|
||||||
const logo = themeTokens().logo;
|
|
||||||
const { isSmallMobile } = useResponsiveStore();
|
const { isSmallMobile } = useResponsiveStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -57,7 +56,7 @@ export const HomeHeader = () => {
|
|||||||
)}
|
)}
|
||||||
<Box
|
<Box
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacings['3xs']}
|
$gap={spacingsTokens['3xs']}
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$position="relative"
|
$position="relative"
|
||||||
$height="fit-content"
|
$height="fit-content"
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export const HomeSection = ({
|
|||||||
}: HomeSectionProps) => {
|
}: HomeSectionProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
|
|
||||||
const { isSmallMobile } = useResponsiveStore();
|
const { isSmallMobile } = useResponsiveStore();
|
||||||
|
|
||||||
@@ -97,17 +96,17 @@ export const HomeSection = ({
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
$direction={direction}
|
$direction={direction}
|
||||||
$gap={!isSmallDevice ? spacings['lg'] : spacings['sm']}
|
$gap={!isSmallDevice ? spacingsTokens['lg'] : spacingsTokens['sm']}
|
||||||
$maxHeight="100%"
|
$maxHeight="100%"
|
||||||
$height="auto"
|
$height="auto"
|
||||||
$width="100%"
|
$width="100%"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
$gap={spacings['sm']}
|
$gap={spacingsTokens['sm']}
|
||||||
$maxWidth="850px"
|
$maxWidth="850px"
|
||||||
$css={direction === 'column' ? '100%' : `flex-basis: ${textWidth};`}
|
$css={direction === 'column' ? '100%' : `flex-basis: ${textWidth};`}
|
||||||
>
|
>
|
||||||
<Box $direction="row" $gap={spacings['sm']} $wrap="wrap">
|
<Box $direction="row" $gap={spacingsTokens['sm']} $wrap="wrap">
|
||||||
<SectionTag tag={tag} />
|
<SectionTag tag={tag} />
|
||||||
{availableSoon && (
|
{availableSoon && (
|
||||||
<SectionTag tag={t('Available soon')} availableSoon />
|
<SectionTag tag={t('Available soon')} availableSoon />
|
||||||
@@ -200,14 +199,14 @@ const SectionTag = ({
|
|||||||
availableSoon?: boolean;
|
availableSoon?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const spacings = spacingsTokens();
|
|
||||||
const colors = colorsTokens();
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
$background={
|
$background={
|
||||||
!availableSoon ? colors['primary-100'] : colors['warning-100']
|
!availableSoon
|
||||||
|
? colorsTokens['primary-100']
|
||||||
|
: colorsTokens['warning-100']
|
||||||
}
|
}
|
||||||
$padding={{ horizontal: spacings['sm'], vertical: '6px' }}
|
$padding={{ horizontal: spacingsTokens['sm'], vertical: '6px' }}
|
||||||
$css={css`
|
$css={css`
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const useLanguageSynchronizer = () => {
|
|||||||
|
|
||||||
const availableBackendLanguages = useMemo(() => {
|
const availableBackendLanguages = useMemo(() => {
|
||||||
return conf?.LANGUAGES.map(([locale]) => locale);
|
return conf?.LANGUAGES.map(([locale]) => locale);
|
||||||
}, [conf]);
|
}, [conf?.LANGUAGES]);
|
||||||
|
|
||||||
const synchronizeLanguage = useCallback(
|
const synchronizeLanguage = useCallback(
|
||||||
async (direction?: 'toBackend' | 'toFrontend') => {
|
async (direction?: 'toBackend' | 'toFrontend') => {
|
||||||
|
|||||||
+5
-7
@@ -13,8 +13,6 @@ export const LeftPanelTargetFilters = () => {
|
|||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { togglePanel } = useLeftPanelStore();
|
const { togglePanel } = useLeftPanelStore();
|
||||||
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||||
const spacing = spacingsTokens();
|
|
||||||
const colors = colorsTokens();
|
|
||||||
|
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const target =
|
const target =
|
||||||
@@ -52,7 +50,7 @@ export const LeftPanelTargetFilters = () => {
|
|||||||
<Box
|
<Box
|
||||||
$justify="center"
|
$justify="center"
|
||||||
$padding={{ horizontal: 'sm' }}
|
$padding={{ horizontal: 'sm' }}
|
||||||
$gap={spacing['2xs']}
|
$gap={spacingsTokens['2xs']}
|
||||||
className="--docs--left-panel-target-filters"
|
className="--docs--left-panel-target-filters"
|
||||||
>
|
>
|
||||||
{defaultQueries.map((query) => {
|
{defaultQueries.map((query) => {
|
||||||
@@ -67,17 +65,17 @@ export const LeftPanelTargetFilters = () => {
|
|||||||
aria-selected={isActive}
|
aria-selected={isActive}
|
||||||
$align="center"
|
$align="center"
|
||||||
$justify="flex-start"
|
$justify="flex-start"
|
||||||
$gap={spacing['xs']}
|
$gap={spacingsTokens['xs']}
|
||||||
$radius={spacing['3xs']}
|
$radius={spacingsTokens['3xs']}
|
||||||
$padding={{ all: '2xs' }}
|
$padding={{ all: '2xs' }}
|
||||||
$css={css`
|
$css={css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: ${isActive
|
background-color: ${isActive
|
||||||
? colors['greyscale-100']
|
? colorsTokens['greyscale-100']
|
||||||
: undefined};
|
: undefined};
|
||||||
font-weight: ${isActive ? 700 : undefined};
|
font-weight: ${isActive ? 700 : undefined};
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${colors['greyscale-100']};
|
background-color: ${colorsTokens['greyscale-100']};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user