Compare commits

..

2 Commits

Author SHA1 Message Date
Cyril 0144044c55 (frontend) add pdf export regression test with embedded image fixtures
adds base64 png/jpg/svg image fixtures to stabilize pdf export snapshots

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-12-01 11:46:43 +01:00
Cyril a6da37e231 (frontend) add test to compare generated PDF against reference template
ensures no regression by validating PDF content matches the expected pattern

Signed-off-by: Cyril <c.gromoff@gmail.com>
2025-12-01 11:46:31 +01:00
92 changed files with 2213 additions and 4421 deletions
@@ -1,24 +0,0 @@
name: 'Free Disk Space'
description: 'Free up disk space by removing large preinstalled items and cleaning up Docker'
runs:
using: "composite"
steps:
- name: Free disk space (Linux only)
if: runner.os == 'Linux'
shell: bash
run: |
echo "Disk usage before cleanup:"
df -h
# Remove large preinstalled items that are not used on GitHub-hosted runners
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /opt/ghc || true
sudo rm -rf /usr/local/lib/android || true
# Clean up Docker
docker system prune -af || true
docker volume prune -f || true
echo "Disk usage after cleanup:"
df -h
-18
View File
@@ -52,12 +52,6 @@ jobs:
push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
-
name: Cleanup Docker after build
if: always()
run: |
docker system prune -af
docker volume prune -f
build-and-push-frontend:
runs-on: ubuntu-latest
@@ -97,12 +91,6 @@ jobs:
push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
-
name: Cleanup Docker after build
if: always()
run: |
docker system prune -af
docker volume prune -f
build-and-push-y-provider:
runs-on: ubuntu-latest
@@ -137,12 +125,6 @@ jobs:
push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
-
name: Cleanup Docker after build
if: always()
run: |
docker system prune -af
docker volume prune -f
notify-argocd:
needs:
-6
View File
@@ -85,9 +85,6 @@ jobs:
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install --frozen-lockfile && yarn install-playwright chromium
- name: Free disk space before Docker
uses: ./.github/actions/free-disk-space
- name: Start Docker services
run: make bootstrap-e2e FLUSH_ARGS='--no-input'
@@ -127,9 +124,6 @@ jobs:
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install --frozen-lockfile && yarn install-playwright firefox webkit chromium
- name: Free disk space before Docker
uses: ./.github/actions/free-disk-space
- name: Start Docker services
run: make bootstrap-e2e FLUSH_ARGS='--no-input'
+6 -29
View File
@@ -6,44 +6,22 @@ and this project adheres to
## [Unreleased]
## [4.1.0] - 2025-12-09
### Added
- ⚡️(frontend) export html #1669
### Changed
- ♿(frontend) improve accessibility:
- ♿(frontend) add skip to content button for keyboard accessibility #1624
- ♿(frontend) fix toggle panel button a11y labels #1634
- 🔒️(frontend) remove dangerouslySetInnerHTML from codebase #1712
- ⚡️(frontend) improve Comments feature #1687
### Fixed
- 🐛(nginx) fix / location to handle new static pages #1682
- 🐛(frontend) rerendering during resize window #1715
## [4.0.0] - 2025-12-01
### Added
- ✨ Add comments feature to the editor #1330
- ✨(backend) Comments on text editor #1330
- ✨(frontend) link to create new doc #1574
### Changed
- ⚡️(sw) stop to cache external resources likes videos #1655
- 💥(frontend) upgrade to ui-kit v2 #1605
- 💥(frontend) upgrade to ui-kit v2
- ⚡️(frontend) improve perf on upload and table of contents #1662
- ♿(frontend) improve accessibility:
- ♿(frontend) improve share modal button accessibility #1626
- ♿(frontend) improve screen reader support in DocShare modal #1628
### Fixed
- ♿(frontend) improve accessibility:
- ♿(frontend) improve share modal button accessibility #1626
- ♿(frontend) improve screen reader support in DocShare modal #1628
- 🐛(frontend) fix toolbar not activated when reader #1640
- 🐛(frontend) preserve left panel width on window resize #1588
- 🐛(frontend) prevent duplicate as first character in title #1595
@@ -54,6 +32,7 @@ and this project adheres to
- ✨(export) enable ODT export for documents #1524
- ✨(frontend) improve mobile UX by showing subdocs count #1540
- ✅(e2e) add test to compare generated PDF against reference template #1648
### Changed
@@ -919,9 +898,7 @@ and this project adheres to
- ✨(frontend) Coming Soon page (#67)
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/suitenumerique/docs/compare/v4.1.0...main
[v4.1.0]: https://github.com/suitenumerique/docs/releases/v4.1.0
[v4.0.0]: https://github.com/suitenumerique/docs/releases/v4.0.0
[unreleased]: https://github.com/suitenumerique/docs/compare/v3.10.0...main
[v3.10.0]: https://github.com/suitenumerique/docs/releases/v3.10.0
[v3.9.0]: https://github.com/suitenumerique/docs/releases/v3.9.0
[v3.8.2]: https://github.com/suitenumerique/docs/releases/v3.8.2
-23
View File
@@ -16,29 +16,6 @@ the following command inside your docker container:
## [Unreleased]
## [4.0.0] - 2025-11-26
- ⚠️ We updated `@gouvfr-lasuite/ui-kit` to `0.18.0`, so if you are customizing Docs with a css layer or with a custom template, you need to update your customization to follow the new design system structure.
More information about the changes in the design system can be found here:
- https://suitenumerique.github.io/cunningham/storybook/?path=/docs/migrating-from-v3-to-v4--docs
- https://github.com/suitenumerique/docs/pull/1605
- https://github.com/suitenumerique/docs/blob/main/docs/theming.md
- If you were using the `THEME_CUSTOMIZATION_FILE_PATH` and have overridden the header logo, you need to update your customization file to follow the new structure of the header, it is now:
```json
{
...,
"header": {
"icon": {
"src": "your_logo_src",
"width": "your_logo_width",
"height": "your_logo_height"
}
}
}
```
## [3.3.0] - 2025-05-22
⚠️ For some advanced features (ex: Export as PDF) Docs relies on XL packages from BlockNote. These are licenced under AGPL-3.0 and are not MIT compatible. You can perfectly use Docs without these packages by setting the environment variable `PUBLISH_AS_MIT` to true. That way you'll build an image of the application without the features that are not MIT compatible. Read the [environment variables documentation](/docs/env.md) for more information.
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Breton\n"
"Language: br_FR\n"
@@ -50,72 +50,67 @@ msgstr "Kuzhet"
msgid "Favorite"
msgstr "Sinedoù"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
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:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
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:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr "Ar vaezienn-mañ a zo rekis."
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Korf"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Doare korf"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Stumm"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "eilenn {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Lenner"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Embanner"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Merour"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Perc'henn"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Strishaet"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Anavezet"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Publik"
@@ -239,8 +234,8 @@ msgstr "implijer"
msgid "users"
msgstr "implijerien"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "titl"
@@ -256,188 +251,155 @@ msgstr "Restr"
msgid "Documents"
msgstr "Restroù"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Restr hep titl"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} en deus rannet ur restr ganeoc'h!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} en deus pedet ac'hanoc'h gant ar rol \"{role}\" war ar restr da-heul:"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} en deus rannet ur restr ganeoc'h: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Roud liamm ar restr/an implijer"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Roudoù liamm ar restr/an implijer"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Ur roud liamm a zo dija evit an restr/an implijer."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Restr muiañ-karet"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Restroù muiañ-karet"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Ar restr-mañ a zo ur restr muiañ karet gant an implijer-mañ."
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Liamm restr/implijer"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Liammoù restr/implijer"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "An implijer-mañ a zo dija er restr-mañ."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Ar skipailh-mañ a zo dija en restr-mañ."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr "An implijer pe ar skipailh a rank bezañ termenet, ket an daou avat."
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "Goulenn tizhout ar restr"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "Goulennoù tizhout ar restr"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "An implijer en deus goulennet tizhout ar restr-mañ."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} en defe c'hoant da dizhout ar restr-mañ!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} en defe c'hoant da dizhout ar restr da-heul:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} en defe c'hoant da dizhout ar restr: {title}"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "deskrivadur"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "kod"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "publik"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "M'eo foran ar patrom-mañ hag implijus gant n'eus forzh piv."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Patrom"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Patromoù"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Liamm patrom/implijer"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Liammoù patrom/implijer"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "An implijer-mañ a zo dija er patrom-mañ."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Ar skipailh-mañ a zo dija er patrom-mañ."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "postel"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Pedadenn d'ur restr"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Pedadennoù d'ur restr"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Ar postel-mañ a zo liammet ouzh un implijer enskrivet."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr "Favorit"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Ein neues Dokument wurde in Ihrem Namen erstellt!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "Sie sind Besitzer eines neuen Dokuments:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Inhalt"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Typ"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Format"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "Kopie von {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Lesen"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Bearbeiten"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Administrator"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Besitzer"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Beschränkt"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Authentifiziert"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Öffentlich"
@@ -239,8 +234,8 @@ msgstr "Benutzer"
msgid "users"
msgstr "Benutzer"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "Titel"
@@ -256,188 +251,155 @@ msgstr "Dokument"
msgid "Documents"
msgstr "Dokumente"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Unbenanntes Dokument"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} hat ein Dokument mit Ihnen geteilt!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} hat Sie mit der Rolle \"{role}\" zu folgendem Dokument eingeladen:"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} hat ein Dokument mit Ihnen geteilt: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Dokument/Benutzer Linkverfolgung"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Dokument/Benutzer Linkverfolgung"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Für dieses Dokument/ diesen Benutzer ist bereits eine Linkverfolgung vorhanden."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Dokumentenfavorit"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Dokumentfavoriten"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Dieses Dokument ist bereits durch den gleichen Benutzer favorisiert worden."
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Dokument/Benutzerbeziehung"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Dokument/Benutzerbeziehungen"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Dieser Benutzer befindet sich bereits in diesem Dokument."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Dieses Team befindet sich bereits in diesem Dokument."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr "Benutzer oder Team müssen gesetzt werden, nicht beides."
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "Beschreibung"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "Code"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "CSS"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "öffentlich"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "Ob diese Vorlage für jedermann öffentlich ist."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Vorlage"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Vorlagen"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Vorlage/Benutzer-Beziehung"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Vorlage/Benutzerbeziehungen"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Dieser Benutzer ist bereits in dieser Vorlage."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Dieses Team ist bereits in diesem Template."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "E-Mail-Adresse"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Einladung zum Dokument"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Dokumenteinladungen"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Diese E-Mail ist bereits einem registrierten Benutzer zugeordnet."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: English\n"
"Language: en_US\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr ""
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr ""
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr ""
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr ""
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr ""
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr ""
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr ""
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr ""
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr ""
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr ""
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr ""
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr ""
@@ -239,8 +234,8 @@ msgstr ""
msgid "users"
msgstr ""
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr ""
@@ -256,188 +251,155 @@ msgstr ""
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr ""
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr ""
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr ""
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr ""
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr ""
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr "Favorito"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "¡Un nuevo documento se ha creado por ti!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
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:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Cuerpo"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Tipo de Cuerpo"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Formato"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "copia de {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Lector"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Editor"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Administrador"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Propietario"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Restringido"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Autentificado"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Público"
@@ -239,8 +234,8 @@ msgstr "usuario"
msgid "users"
msgstr "usuarios"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "título"
@@ -256,188 +251,155 @@ msgstr "Documento"
msgid "Documents"
msgstr "Documentos"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Documento sin título"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "¡{name} ha compartido un documento contigo!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, 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:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} ha compartido un documento contigo: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Traza del enlace de documento/usuario"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Trazas del enlace de documento/usuario"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
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:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Documento favorito"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Documentos favoritos"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
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:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Relación documento/usuario"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Relaciones documento/usuario"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Este usuario ya forma parte del documento."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Este equipo ya forma parte del documento."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
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:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "Solicitud de acceso"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "Solicitud de accesos"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "Este usuario ya ha solicitado acceso a este documento."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "¡{name} desea acceder a un documento!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} desea acceso al siguiente documento:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} está pidiendo acceso al documento: {title}"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "descripción"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "código"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "público"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
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:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Plantilla"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Plantillas"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Relación plantilla/usuario"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Relaciones plantilla/usuario"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Este usuario ya forma parte de la plantilla."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Este equipo ya se encuentra en esta plantilla."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "dirección de correo electrónico"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Invitación al documento"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Invitaciones a documentos"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Este correo electrónico está asociado a un usuario registrado."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -50,72 +50,67 @@ msgstr "Masqué"
msgid "Favorite"
msgstr "Favoris"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Un nouveau document a été créé pour vous !"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "Vous avez été déclaré propriétaire d'un nouveau document :"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr "Ce champ est obligatoire."
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "La portée du lien '%(link_reach)s' n'est pas autorisée en fonction de la configuration du document parent."
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Corps"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Type de corps"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Format"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "copie de {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Lecteur"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr "Commentateur"
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Éditeur"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Administrateur"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Propriétaire"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Restreint"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Authentifié"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Public"
@@ -239,8 +234,8 @@ msgstr "utilisateur"
msgid "users"
msgstr "utilisateurs"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "titre"
@@ -256,188 +251,155 @@ msgstr "Document"
msgid "Documents"
msgstr "Documents"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Document sans titre"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} a partagé un document avec vous!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} vous a invité avec le rôle \"{role}\" sur le document suivant :"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} a partagé un document avec vous : {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Trace du lien document/utilisateur"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Traces du lien document/utilisateur"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Une trace de lien existe déjà pour ce document/utilisateur."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Document favori"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Documents favoris"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Ce document est déjà un favori de cet utilisateur."
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Relation document/utilisateur"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Relations document/utilisateur"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Cet utilisateur est déjà dans ce document."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Cette équipe est déjà dans ce document."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
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:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "Demande d'accès au document"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "Demande d'accès au document"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "Cet utilisateur a déjà demandé l'accès à ce document."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} souhaiterait accéder au document suivant !"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} souhaiterait accéder au document suivant :"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} demande l'accès au document : {title}"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr "Conversation"
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr "Conversations"
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr "Anonyme"
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr "Commentaire"
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr "Commentaires"
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr "Cet émoji a déjà été réagi à ce commentaire."
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr "Réaction"
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr "Réactions"
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "description"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "code"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "CSS"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "public"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
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:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Modèle"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Modèles"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Relation modèle/utilisateur"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Relations modèle/utilisateur"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Cet utilisateur est déjà dans ce modèle."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Cette équipe est déjà modèle."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "adresse e-mail"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Invitation à un document"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Invitations à un document"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Cette adresse email est déjà associée à un utilisateur inscrit."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr "Preferiti"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Un nuovo documento è stato creato a tuo nome!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "Sei ora proprietario di un nuovo documento:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Corpo"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Formato"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "copia di {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Lettore"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Editor"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Amministratore"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Proprietario"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Limitato"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Autenticato"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Pubblico"
@@ -239,8 +234,8 @@ msgstr "utente"
msgid "users"
msgstr "utenti"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "titolo"
@@ -256,188 +251,155 @@ msgstr "Documento"
msgid "Documents"
msgstr "Documenti"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Documento senza titolo"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} ha condiviso un documento con te!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, 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:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, 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:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Documento preferito"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Documenti preferiti"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Questo utente è già presente in questo documento."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Questo team è già presente in questo documento."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "descrizione"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "code"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "pubblico"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "Indica se questo modello è pubblico per chiunque."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Modello"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Modelli"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Questo utente è già in questo modello."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Questo team è già in questo modello."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "indirizzo e-mail"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Invito al documento"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Inviti al documento"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Questa email è già associata a un utente registrato."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -50,72 +50,67 @@ msgstr "Gemaskeerd"
msgid "Favorite"
msgstr "Favoriet"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Een nieuw document is namens u gemaakt!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "U heeft eigenaarschap van een nieuw document gekregen:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr "Dit veld is verplicht."
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "Link bereik '%(link_reach)s' is niet toegestaan op basis van bovenliggende documentconfiguratie."
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Text"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Text type"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Formaat"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "kopie van {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Lezer"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr "Commentator"
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Redacteur"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Beheerder"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Eigenaar"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Beperkt"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Geauthenticeerd"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Publiek"
@@ -239,8 +234,8 @@ msgstr "gebruiker"
msgid "users"
msgstr "gebruikers"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "titel"
@@ -256,188 +251,155 @@ msgstr "Document"
msgid "Documents"
msgstr "Documenten"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Naamloos Document"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} heeft een document met u gedeeld!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} heeft u uitgenodigd met de rol \"{role}\" op het volgende document:"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} heeft een document met u gedeeld: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Document/gebruiker link"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Document/gebruiker link"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Een link bestaat al voor dit document/deze gebruiker."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Document favoriet"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Document favorieten"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Dit document is al in gebruik als favoriet door dezelfde gebruiker."
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Document/gebruiker relatie"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Document/gebruiker relaties"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "De gebruiker bestaat al in dit document."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Dit team bestaat al in dit document."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr "Een gebruiker of team moet gekozen worden, maar niet beide."
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "Document verzoekt om toegang"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "Document verzoekt om toegangen"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "Deze gebruiker heeft al om toegang tot dit document gevraagd."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} verzoekt toegang tot een document!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} verzoekt toegang tot het volgende document:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} verzoekt toegang tot het document: {title}"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr "Kanaal"
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr "Kanalen"
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr "Anoniem"
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr "Reactie"
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr "Reacties"
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr "Deze emoji is al op deze opmerking gereageerd."
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr "Reactie"
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr "Reacties"
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "omschrijving"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "code"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "publiek"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "Of dit sjabloon door iedereen publiekelijk te gebruiken is."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Sjabloon"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Sjabloon"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Sjabloon/gebruiker relatie"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Sjabloon/gebruiker relaties"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "De gebruiker bestaat al in dit sjabloon."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Het team bestaat al in dit sjabloon."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "e-mailadres"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Document uitnodiging"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Document uitnodigingen"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Deze email is al geassocieerd met een geregistreerde gebruiker."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Language: pt_PT\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr "Favorito"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Um novo documento foi criado em seu nome!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "A propriedade de um novo documento foi concedida a você:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Corpo"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Tipo de corpo"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Formato"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "cópia de {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Leitor"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Editor"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Administrador"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Dono"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Restrito"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Autenticado"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Público"
@@ -239,8 +234,8 @@ msgstr ""
msgid "users"
msgstr ""
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr ""
@@ -256,188 +251,155 @@ msgstr ""
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr ""
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr ""
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr ""
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr ""
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr ""
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -50,72 +50,67 @@ msgstr "Скрытый"
msgid "Favorite"
msgstr "Избранное"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Новый документ был создан от вашего имени!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "Вы назначены владельцем для нового документа:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr "Это поле обязательное."
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "Доступ по ссылке '%(link_reach)s' запрещён в соответствии с настройками родительского документа."
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Текст сообщения"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Тип сообщения"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Формат"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "копия {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Читатель"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr "Комментатор"
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Редактор"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Администратор"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Владелец"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Доступ ограничен"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Аутентификация выполнена"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Доступно всем"
@@ -239,8 +234,8 @@ msgstr "пользователь"
msgid "users"
msgstr "пользователи"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "заголовок"
@@ -256,188 +251,155 @@ msgstr "Документ"
msgid "Documents"
msgstr "Документы"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Безымянный документ"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} делится с вами документом!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} приглашает вас присоединиться к следующему документу с ролью \"{role}\":"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} делится с вами документом: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Трассировка связи документ/пользователь"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Трассировка связей документ/пользователь"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Для этого документа/пользователя уже существует трассировка ссылки."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Избранный документ"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Избранные документы"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Этот документ уже помечен как избранный для этого пользователя."
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Отношение документ/пользователь"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Отношения документ/пользователь"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Этот пользователь уже имеет доступ к этому документу."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Эта команда уже имеет доступ к этому документу."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr "Может быть выбран либо пользователь, либо команда, но не оба варианта сразу."
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "Документ запрашивает доступ"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "Документ запрашивает доступы"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "Этот пользователь уже запросил доступ к этому документу."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} хочет получить доступ к документу!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} хочет получить доступ к следующему документу:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} запрашивает доступ к документу: {title}"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr "Обсуждение"
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr "Обсуждения"
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr "Аноним"
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr "Комментарий"
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr "Комментарии"
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr "Этот эмодзи уже использован в этом комментарии."
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr "Реакция"
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr "Реакции"
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "описание"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "код"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "доступно всем"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "Этот шаблон доступен всем пользователям."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Шаблон"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Шаблоны"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Отношение шаблон/пользователь"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Отношения шаблон/пользователь"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Этот пользователь уже указан в этом шаблоне."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Эта команда уже указана в этом шаблоне."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "адрес электронной почты"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Приглашение для документа"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Приглашения для документов"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Этот адрес уже связан с зарегистрированным пользователем."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Slovenian\n"
"Language: sl_SI\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr "Priljubljena"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
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:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
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:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Telo"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Vrsta telesa"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Oblika"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr ""
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Bralec"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Urednik"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Skrbnik"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Lastnik"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Omejeno"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Preverjeno"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Javno"
@@ -239,8 +234,8 @@ msgstr "uporabnik"
msgid "users"
msgstr "uporabniki"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "naslov"
@@ -256,188 +251,155 @@ msgstr "Dokument"
msgid "Documents"
msgstr "Dokumenti"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Dokument brez naslova"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} je delil dokument z vami!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, 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:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} je delil dokument z vami: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Dokument/sled povezave uporabnika"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Sledi povezav dokumenta/uporabnika"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Za ta dokument/uporabnika že obstaja sled povezave."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Priljubljeni dokument"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Priljubljeni dokumenti"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
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:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Odnos dokument/uporabnik"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Odnosi dokument/uporabnik"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Ta uporabnik je že v tem dokumentu."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Ta ekipa je že v tem dokumentu."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
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:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "opis"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "koda"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "javno"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "Ali je ta predloga javna za uporabo."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Predloga"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Predloge"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Odnos predloga/uporabnik"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Odnosi med predlogo in uporabnikom"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Ta uporabnik je že v tej predlogi."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Ta ekipa je že v tej predlogi."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "elektronski naslov"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Vabilo na dokument"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Vabila na dokument"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Ta e-poštni naslov je že povezan z registriranim uporabnikom."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Swedish\n"
"Language: sv_SE\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr "Favoriter"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Ett nytt dokument skapades åt dig!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
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:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Format"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr ""
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr ""
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr ""
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Administratör"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr ""
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr ""
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr ""
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Publik"
@@ -239,8 +234,8 @@ msgstr ""
msgid "users"
msgstr ""
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr ""
@@ -256,188 +251,155 @@ msgstr ""
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr ""
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr ""
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr ""
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr ""
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "e-postadress"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Bjud in dokument"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Inbjudningar dokument"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Denna e-postadress är redan associerad med en registrerad användare."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
@@ -50,72 +50,67 @@ msgstr ""
msgid "Favorite"
msgstr ""
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr ""
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr ""
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr ""
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr ""
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr ""
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr ""
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr ""
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr ""
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr ""
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr ""
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr ""
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr ""
@@ -239,8 +234,8 @@ msgstr ""
msgid "users"
msgstr ""
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr ""
@@ -256,188 +251,155 @@ msgstr ""
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr ""
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr ""
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr ""
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr ""
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr ""
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr ""
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr ""
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr ""
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr ""
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr ""
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr ""
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr ""
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
@@ -50,72 +50,67 @@ msgstr "Приховано"
msgid "Favorite"
msgstr "Обране"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "Новий документ був створений від вашого імені!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "Ви тепер є власником нового документа:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr "Це поле є обов’язковим."
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "Доступ до посилання '%(link_reach)s' заборонено на основі конфігурації батьківського документа."
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "Вміст"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "Тип вмісту"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "Формат"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "копія {title}"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "Читач"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr "Комментар"
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "Редактор"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "Адміністратор"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "Власник"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "Обмежено"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "Підтверджено"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "Публічне"
@@ -239,8 +234,8 @@ msgstr "користувач"
msgid "users"
msgstr "користувачі"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "заголовок"
@@ -256,188 +251,155 @@ msgstr "Документ"
msgid "Documents"
msgstr "Документи"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "Документ без назви"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} ділиться з вами документом!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} запрошує вас для роботи з документом із роллю \"{role}\":"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} ділиться з вами документом: {title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "Трасування посилання Документ/користувач"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "Трасування посилань Документ/користувач"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "Відстеження вже існуючих посилань для цього документа/користувача."
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "Обраний документ"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "Обрані документи"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Цей документ вже вказаний як обраний для одного користувача."
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "Відносини документ/користувач"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "Відносини документ/користувач"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "Цей користувач вже має доступ до цього документу."
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "Ця команда вже має доступ до цього документа."
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr "Вкажіть користувача або команду, а не обох."
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "Запит доступу до документа"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "Запит доступу для документа"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "Цей користувач вже попросив доступ до цього документа."
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} хоче отримати доступ до документа!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} бажає отримати доступ до наступного документа:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name} запитує доступ до документа: {title}"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr "Обговорення"
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr "Обговорення"
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr "Анонім"
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr "Коментар"
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr "Коментарі"
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr "Цим емодзі вже відреагували на цей коментар."
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr "Реакція"
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr "Реакції"
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "опис"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "код"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "публічне"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "Чи є цей шаблон публічним для будь-кого користувача."
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "Шаблон"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "Шаблони"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "Відношення шаблон/користувач"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "Відношення шаблон/користувач"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "Цей користувач вже має доступ до цього шаблону."
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "Ця команда вже має доступ до цього шаблону."
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "електронна адреса"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "Запрошення до редагування документа"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "Запрошення до редагування документів"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "Ця електронна пошта вже пов'язана з зареєстрованим користувачем."
+59 -97
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 14:08+0000\n"
"PO-Revision-Date: 2025-12-09 11:12\n"
"POT-Creation-Date: 2025-11-17 08:04+0000\n"
"PO-Revision-Date: 2025-11-19 10:13\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -50,72 +50,67 @@ msgstr "已屏蔽"
msgid "Favorite"
msgstr "收藏"
#: build/lib/core/api/serializers.py:497 core/api/serializers.py:497
#: build/lib/core/api/serializers.py:496 core/api/serializers.py:496
msgid "A new document was created on your behalf!"
msgstr "已为您创建了一份新文档!"
#: build/lib/core/api/serializers.py:501 core/api/serializers.py:501
#: build/lib/core/api/serializers.py:500 core/api/serializers.py:500
msgid "You have been granted ownership of a new document:"
msgstr "您已被授予新文档的所有权:"
#: build/lib/core/api/serializers.py:537 core/api/serializers.py:537
#: build/lib/core/api/serializers.py:536 core/api/serializers.py:536
msgid "This field is required."
msgstr "必填字段。"
#: build/lib/core/api/serializers.py:548 core/api/serializers.py:548
#: build/lib/core/api/serializers.py:547 core/api/serializers.py:547
#, python-format
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/serializers.py:694 core/api/serializers.py:694
#: build/lib/core/api/serializers.py:693 core/api/serializers.py:693
msgid "Body"
msgstr "正文"
#: build/lib/core/api/serializers.py:697 core/api/serializers.py:697
#: build/lib/core/api/serializers.py:696 core/api/serializers.py:696
msgid "Body type"
msgstr "正文类型"
#: build/lib/core/api/serializers.py:703 core/api/serializers.py:703
#: build/lib/core/api/serializers.py:702 core/api/serializers.py:702
msgid "Format"
msgstr "格式"
#: build/lib/core/api/viewsets.py:1004 core/api/viewsets.py:1004
#: build/lib/core/api/viewsets.py:1003 core/api/viewsets.py:1003
#, python-brace-format
msgid "copy of {title}"
msgstr "{title} 的副本"
#: build/lib/core/choices.py:35 build/lib/core/choices.py:43 core/choices.py:35
#: core/choices.py:43
#: build/lib/core/choices.py:35 build/lib/core/choices.py:42 core/choices.py:35
#: core/choices.py:42
msgid "Reader"
msgstr "阅读者"
#: build/lib/core/choices.py:36 build/lib/core/choices.py:44 core/choices.py:36
#: core/choices.py:44
msgid "Commenter"
msgstr ""
#: build/lib/core/choices.py:37 build/lib/core/choices.py:45 core/choices.py:37
#: core/choices.py:45
#: build/lib/core/choices.py:36 build/lib/core/choices.py:43 core/choices.py:36
#: core/choices.py:43
msgid "Editor"
msgstr "编辑者"
#: build/lib/core/choices.py:46 core/choices.py:46
#: build/lib/core/choices.py:44 core/choices.py:44
msgid "Administrator"
msgstr "超级管理员"
#: build/lib/core/choices.py:47 core/choices.py:47
#: build/lib/core/choices.py:45 core/choices.py:45
msgid "Owner"
msgstr "所有者"
#: build/lib/core/choices.py:58 core/choices.py:58
#: build/lib/core/choices.py:56 core/choices.py:56
msgid "Restricted"
msgstr "受限的"
#: build/lib/core/choices.py:62 core/choices.py:62
#: build/lib/core/choices.py:60 core/choices.py:60
msgid "Authenticated"
msgstr "已验证"
#: build/lib/core/choices.py:64 core/choices.py:64
#: build/lib/core/choices.py:62 core/choices.py:62
msgid "Public"
msgstr "公开"
@@ -239,8 +234,8 @@ msgstr "用户"
msgid "users"
msgstr "个用户"
#: build/lib/core/models.py:361 build/lib/core/models.py:1430
#: core/models.py:361 core/models.py:1430
#: build/lib/core/models.py:361 build/lib/core/models.py:1276
#: core/models.py:361 core/models.py:1276
msgid "title"
msgstr "标题"
@@ -256,188 +251,155 @@ msgstr "文档"
msgid "Documents"
msgstr "个文档"
#: build/lib/core/models.py:424 build/lib/core/models.py:824 core/models.py:424
#: core/models.py:824
#: build/lib/core/models.py:424 build/lib/core/models.py:822 core/models.py:424
#: core/models.py:822
msgid "Untitled Document"
msgstr "未命名文档"
#: build/lib/core/models.py:859 core/models.py:859
#: build/lib/core/models.py:857 core/models.py:857
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} 与您共享了一个文档!"
#: build/lib/core/models.py:863 core/models.py:863
#: build/lib/core/models.py:861 core/models.py:861
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} 邀请您以“{role}”角色访问以下文档:"
#: build/lib/core/models.py:869 core/models.py:869
#: build/lib/core/models.py:867 core/models.py:867
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} 与您共享了一个文档:{title}"
#: build/lib/core/models.py:969 core/models.py:969
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document/user link trace"
msgstr "文档/用户链接跟踪"
#: build/lib/core/models.py:970 core/models.py:970
#: build/lib/core/models.py:968 core/models.py:968
msgid "Document/user link traces"
msgstr "个文档/用户链接跟踪"
#: build/lib/core/models.py:976 core/models.py:976
#: build/lib/core/models.py:974 core/models.py:974
msgid "A link trace already exists for this document/user."
msgstr "此文档/用户的链接跟踪已存在。"
#: build/lib/core/models.py:999 core/models.py:999
#: build/lib/core/models.py:997 core/models.py:997
msgid "Document favorite"
msgstr "文档收藏"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:998 core/models.py:998
msgid "Document favorites"
msgstr "文档收藏夹"
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:1004 core/models.py:1004
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "该文档已被同一用户的收藏关系实例关联。"
#: build/lib/core/models.py:1028 core/models.py:1028
#: build/lib/core/models.py:1026 core/models.py:1026
msgid "Document/user relation"
msgstr "文档/用户关系"
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:1027 core/models.py:1027
msgid "Document/user relations"
msgstr "文档/用户关系集"
#: build/lib/core/models.py:1035 core/models.py:1035
#: build/lib/core/models.py:1033 core/models.py:1033
msgid "This user is already in this document."
msgstr "该用户已在此文档中。"
#: build/lib/core/models.py:1041 core/models.py:1041
#: build/lib/core/models.py:1039 core/models.py:1039
msgid "This team is already in this document."
msgstr "该团队已在此文档中。"
#: build/lib/core/models.py:1047 build/lib/core/models.py:1516
#: core/models.py:1047 core/models.py:1516
#: build/lib/core/models.py:1045 build/lib/core/models.py:1362
#: core/models.py:1045 core/models.py:1362
msgid "Either user or team must be set, not both."
msgstr "必须设置用户或团队之一,不能同时设置两者。"
#: build/lib/core/models.py:1198 core/models.py:1198
#: build/lib/core/models.py:1191 core/models.py:1191
msgid "Document ask for access"
msgstr "文档需要访问权限"
#: build/lib/core/models.py:1199 core/models.py:1199
#: build/lib/core/models.py:1192 core/models.py:1192
msgid "Document ask for accesses"
msgstr "文档需要访问权限"
#: build/lib/core/models.py:1205 core/models.py:1205
#: build/lib/core/models.py:1198 core/models.py:1198
msgid "This user has already asked for access to this document."
msgstr "用户已申请该文档的访问权限。"
#: build/lib/core/models.py:1262 core/models.py:1262
#: build/lib/core/models.py:1255 core/models.py:1255
#, python-brace-format
msgid "{name} would like access to a document!"
msgstr "{name} 申请访问文档!"
#: build/lib/core/models.py:1266 core/models.py:1266
#: build/lib/core/models.py:1259 core/models.py:1259
#, python-brace-format
msgid "{name} would like access to the following document:"
msgstr "{name} 申请访问以下文档:"
#: build/lib/core/models.py:1272 core/models.py:1272
#: build/lib/core/models.py:1265 core/models.py:1265
#, python-brace-format
msgid "{name} is asking for access to the document: {title}"
msgstr "{name}申请文档:{title}的访问权限"
#: build/lib/core/models.py:1314 core/models.py:1314
msgid "Thread"
msgstr ""
#: build/lib/core/models.py:1315 core/models.py:1315
msgid "Threads"
msgstr ""
#: build/lib/core/models.py:1318 build/lib/core/models.py:1370
#: core/models.py:1318 core/models.py:1370
msgid "Anonymous"
msgstr ""
#: build/lib/core/models.py:1365 core/models.py:1365
msgid "Comment"
msgstr ""
#: build/lib/core/models.py:1366 core/models.py:1366
msgid "Comments"
msgstr ""
#: build/lib/core/models.py:1415 core/models.py:1415
msgid "This emoji has already been reacted to this comment."
msgstr ""
#: build/lib/core/models.py:1419 core/models.py:1419
msgid "Reaction"
msgstr ""
#: build/lib/core/models.py:1420 core/models.py:1420
msgid "Reactions"
msgstr ""
#: build/lib/core/models.py:1431 core/models.py:1431
#: build/lib/core/models.py:1277 core/models.py:1277
msgid "description"
msgstr "说明"
#: build/lib/core/models.py:1432 core/models.py:1432
#: build/lib/core/models.py:1278 core/models.py:1278
msgid "code"
msgstr "代码"
#: build/lib/core/models.py:1433 core/models.py:1433
#: build/lib/core/models.py:1279 core/models.py:1279
msgid "css"
msgstr "css"
#: build/lib/core/models.py:1435 core/models.py:1435
#: build/lib/core/models.py:1281 core/models.py:1281
msgid "public"
msgstr "公开"
#: build/lib/core/models.py:1437 core/models.py:1437
#: build/lib/core/models.py:1283 core/models.py:1283
msgid "Whether this template is public for anyone to use."
msgstr "该模板是否公开供任何人使用。"
#: build/lib/core/models.py:1443 core/models.py:1443
#: build/lib/core/models.py:1289 core/models.py:1289
msgid "Template"
msgstr "模板"
#: build/lib/core/models.py:1444 core/models.py:1444
#: build/lib/core/models.py:1290 core/models.py:1290
msgid "Templates"
msgstr "模板"
#: build/lib/core/models.py:1497 core/models.py:1497
#: build/lib/core/models.py:1343 core/models.py:1343
msgid "Template/user relation"
msgstr "模板/用户关系"
#: build/lib/core/models.py:1498 core/models.py:1498
#: build/lib/core/models.py:1344 core/models.py:1344
msgid "Template/user relations"
msgstr "模板/用户关系集"
#: build/lib/core/models.py:1504 core/models.py:1504
#: build/lib/core/models.py:1350 core/models.py:1350
msgid "This user is already in this template."
msgstr "该用户已在此模板中。"
#: build/lib/core/models.py:1510 core/models.py:1510
#: build/lib/core/models.py:1356 core/models.py:1356
msgid "This team is already in this template."
msgstr "该团队已在此模板中。"
#: build/lib/core/models.py:1587 core/models.py:1587
#: build/lib/core/models.py:1433 core/models.py:1433
msgid "email address"
msgstr "电子邮件地址"
#: build/lib/core/models.py:1606 core/models.py:1606
#: build/lib/core/models.py:1452 core/models.py:1452
msgid "Document invitation"
msgstr "文档邀请"
#: build/lib/core/models.py:1607 core/models.py:1607
#: build/lib/core/models.py:1453 core/models.py:1453
msgid "Document invitations"
msgstr "文档邀请"
#: build/lib/core/models.py:1627 core/models.py:1627
#: build/lib/core/models.py:1473 core/models.py:1473
msgid "This email is already associated to a registered user."
msgstr "此电子邮件已经与现有注册用户关联。"
+2 -2
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "4.1.0"
version = "3.10.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -39,7 +39,7 @@ dependencies = [
"django-redis==6.0.0",
"django-storages[s3]==1.14.6",
"django-timezone-field>=5.1",
"django==5.2.9",
"django==5.2.8",
"django-treebeard==4.7.1",
"djangorestframework==3.16.1",
"drf_spectacular==0.29.0",
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 KiB

@@ -1,11 +1,6 @@
import { expect, test } from '@playwright/test';
import {
closeHeaderMenu,
createDoc,
getOtherBrowserName,
verifyDocName,
} from './utils-common';
import { createDoc, getOtherBrowserName, verifyDocName } from './utils-common';
import { writeInEditor } from './utils-editor';
import {
addNewMember,
@@ -121,7 +116,8 @@ test.describe('Doc Comments', () => {
await createDoc(page, 'comment-interaction', browserName, 1);
// Checks add react reaction
const editor = await writeInEditor({ page, text: 'Hello' });
const editor = page.locator('.ProseMirror');
await editor.locator('.bn-block-outer').last().fill('Hello World');
await editor.getByText('Hello').selectText();
await page.getByRole('button', { name: 'Comment' }).click();
@@ -185,28 +181,6 @@ test.describe('Doc Comments', () => {
'background-color',
'rgba(0, 0, 0, 0)',
);
/* Delete the last comment remove the thread */
await editor.getByText('Hello').selectText();
await page.getByRole('button', { name: 'Comment' }).click();
await thread.getByRole('paragraph').first().fill('This is a new comment');
await thread.locator('[data-test="save"]').click();
await expect(editor.getByText('Hello')).toHaveCSS(
'background-color',
'rgba(237, 180, 0, 0.4)',
);
await editor.getByText('Hello').click();
await thread.getByText('This is a new comment').first().hover();
await thread.locator('[data-test="moreactions"]').first().click();
await thread.getByRole('menuitem', { name: 'Delete comment' }).click();
await expect(editor.getByText('Hello')).toHaveCSS(
'background-color',
'rgba(0, 0, 0, 0)',
);
});
test('it checks the comments abilities', async ({ page, browserName }) => {
@@ -319,27 +293,3 @@ test.describe('Doc Comments', () => {
await cleanup();
});
});
test.describe('Doc Comments mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test('Comments are not visible on mobile', async ({ page, browserName }) => {
const [title] = await createDoc(
page,
'comment-mobile',
browserName,
1,
true,
);
await closeHeaderMenu(page);
await verifyDocName(page, title);
// Checks add react reaction
const editor = await writeInEditor({ page, text: 'Hello' });
await editor.getByText('Hello').selectText();
await expect(page.getByRole('button', { name: 'Comment' })).toBeHidden();
await expect(page.getByRole('button', { name: 'Paragraph' })).toBeVisible();
});
});
@@ -7,7 +7,6 @@ import {
randomName,
verifyDocName,
} from './utils-common';
import { connectOtherUserToDoc } from './utils-share';
test.beforeEach(async ({ page }) => {
await page.goto('/');
@@ -74,82 +73,6 @@ test.describe('Doc Create', () => {
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
});
test('it creates a doc with link "/doc/new/', async ({
page,
browserName,
}) => {
test.slow();
// Private doc creation
await page.goto('/docs/new/?title=My+private+doc+from+url');
await verifyDocName(page, 'My private doc from url');
await page.getByRole('button', { name: 'Share' }).click();
await expect(
page.getByTestId('doc-visibility').getByText('Private').first(),
).toBeVisible();
// Public editing doc creation
await page.goto(
'/docs/new/?title=My+public+doc+from+url&link-reach=public&link-role=editor',
);
await verifyDocName(page, 'My public doc from url');
await page.getByRole('button', { name: 'Share' }).click();
await expect(
page.getByTestId('doc-visibility').getByText('Public').first(),
).toBeVisible();
await expect(
page.getByTestId('doc-access-mode').getByText('Editing').first(),
).toBeVisible();
// Authenticated reading doc creation
await page.goto(
'/docs/new/?title=My+authenticated+doc+from+url&link-reach=authenticated&link-role=reader',
);
await verifyDocName(page, 'My authenticated doc from url');
await page.getByRole('button', { name: 'Share' }).click();
await expect(
page.getByTestId('doc-visibility').getByText('Connected').first(),
).toBeVisible();
await expect(
page.getByTestId('doc-access-mode').getByText('Reading').first(),
).toBeVisible();
const { cleanup, otherPage, otherBrowserName } =
await connectOtherUserToDoc({
docUrl:
'/docs/new/?title=From+unlogged+doc+from+url&link-reach=authenticated&link-role=reader',
browserName,
withoutSignIn: true,
});
await keyCloakSignIn(otherPage, otherBrowserName, false);
await verifyDocName(otherPage, 'From unlogged doc from url');
await otherPage.getByRole('button', { name: 'Share' }).click();
await expect(
otherPage.getByTestId('doc-visibility').getByText('Connected').first(),
).toBeVisible();
await expect(
otherPage.getByTestId('doc-access-mode').getByText('Reading').first(),
).toBeVisible();
await cleanup();
});
});
test.describe('Doc Create: Not logged', () => {
@@ -996,44 +996,4 @@ test.describe('Doc Editor', () => {
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe('test-pdf.pdf');
});
test('it preserves text when switching between mobile and desktop views', async ({
page,
browserName,
}) => {
const [docTitle] = await createDoc(
page,
'doc-viewport-test',
browserName,
1,
);
await verifyDocName(page, docTitle);
const editor = await writeInEditor({
page,
text: 'Hello World - Desktop Text',
});
await expect(editor.getByText('Hello World - Desktop Text')).toBeVisible();
await page.waitForTimeout(500);
// Switch to mobile viewport
await page.setViewportSize({ width: 500, height: 1200 });
await page.waitForTimeout(500);
await expect(editor.getByText('Hello World - Desktop Text')).toBeVisible();
await writeInEditor({
page,
text: 'Mobile Text',
});
await page.waitForTimeout(500);
// Switch back to desktop viewport
await page.setViewportSize({ width: 1280, height: 720 });
await page.waitForTimeout(500);
await expect(editor.getByText('Mobile Text')).toBeVisible();
});
});
@@ -1,8 +1,8 @@
import fs from 'fs';
import path from 'path';
import { expect, test } from '@playwright/test';
import cs from 'convert-stream';
import JSZip from 'jszip';
import { PDFParse } from 'pdf-parse';
import {
@@ -14,6 +14,35 @@ import {
import { openSuggestionMenu, writeInEditor } from './utils-editor';
import { createRootSubPage } from './utils-sub-pages';
const REGRESSION_FIXTURE_CONTENT = fs.readFileSync(
path.join(__dirname, 'assets/content.txt'),
'utf-8',
);
const REGRESSION_SNAPSHOT_NAME = 'doc-export-regression.pdf';
const REGRESSION_DOC_TITLE = 'doc-export-regression-reference';
/**
* Playwright snapshots store the raw PDF bytes. However, each export embeds
* dynamic metadata (timestamps, font-subset identifiers, etc.) that would make
* the snapshot differ at every run. To ensure deterministic comparisons we
* strip/neutralize those fields before matching against the reference PDF.
*/
const sanitizePdfBuffer = (buffer: Buffer) => {
const pdfText = buffer.toString('latin1');
const neutralized = pdfText
// Remove per-export timestamps
.replace(/\/CreationDate\s*\(.*?\)/g, '/CreationDate ()')
.replace(/\/ModDate\s*\(.*?\)/g, '/ModDate ()')
// Remove file identifiers
.replace(/\/ID\s*\[<[^>]+>\s*<[^>]+>\]/g, '/ID [<0><0>]')
.replace(/D:\d{14}Z/g, 'D:00000000000000Z')
// Remove subset font prefixes generated by PDF renderer
.replace(/\b[A-Z]{6}\+(Inter18pt-[A-Za-z]+)\b/g, 'STATIC+$1')
.replace(/\b[A-Z]{6}\+(GeistMono-[A-Za-z]+)\b/g, 'STATIC+$1');
return Buffer.from(neutralized, 'latin1');
};
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
@@ -32,7 +61,7 @@ test.describe('Doc Export', () => {
await expect(page.getByTestId('modal-export-title')).toBeVisible();
await expect(
page.getByText(/Download your document in a \.docx, \.odt.*format\./i),
page.getByText('Download your document in a .docx, .odt or .pdf format.'),
).toBeVisible();
await expect(
page.getByRole('combobox', { name: 'Template' }),
@@ -188,89 +217,6 @@ test.describe('Doc Export', () => {
expect(download.suggestedFilename()).toBe(`${randomDoc}.odt`);
});
test('it exports the doc to html zip', async ({ page, browserName }) => {
const [randomDoc] = await createDoc(
page,
'doc-editor-html-zip',
browserName,
1,
);
await verifyDocName(page, randomDoc);
// Add some content and at least one image so that the ZIP contains media files.
await page.locator('.ProseMirror.bn-editor').click();
await page.locator('.ProseMirror.bn-editor').fill('Hello HTML ZIP');
await page.keyboard.press('Enter');
await page.locator('.bn-block-outer').last().fill('/');
await page.getByText('Resizable image with caption').click();
const fileChooserPromise = page.waitForEvent('filechooser');
await page.getByText('Upload image').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg'));
const image = page
.locator('.--docs--editor-container img.bn-visual-media')
.first();
// Wait for the image to be attached and have a valid src (aria-hidden prevents toBeVisible on Chromium)
await expect(image).toBeAttached({ timeout: 10000 });
await expect(image).toHaveAttribute('src', /.*\.svg/);
// Give some time for the image to be fully processed
await page.waitForTimeout(1000);
await page
.getByRole('button', {
name: 'Export the document',
})
.click();
await page.getByRole('combobox', { name: 'Format' }).click();
await page.getByRole('option', { name: 'HTML' }).click();
await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.zip`);
});
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.zip`);
const zipBuffer = await cs.toBuffer(await download.createReadStream());
// Unzip and inspect contents
const zip = await JSZip.loadAsync(zipBuffer);
// Check that index.html exists
const indexHtml = zip.file('index.html');
expect(indexHtml).not.toBeNull();
// Read and verify HTML content
const htmlContent = await indexHtml!.async('string');
expect(htmlContent).toContain('Hello HTML ZIP');
expect(htmlContent).toContain('href="styles.css"');
// Check for media files (they are at the root of the ZIP, not in a media/ folder)
// Media files are named like "1-test.svg" or "media-1.png" by deriveMediaFilename
const allFiles = Object.keys(zip.files);
const mediaFiles = allFiles.filter(
(name) => name !== 'index.html' && !name.endsWith('/'),
);
expect(mediaFiles.length).toBeGreaterThan(0);
// Verify the SVG image is included
const svgFile = mediaFiles.find((name) => name.endsWith('.svg'));
expect(svgFile).toBeDefined();
const styleFile = mediaFiles.find((name) => name === 'styles.css');
expect(styleFile).toBeDefined();
});
/**
* This test tell us that the export to pdf is working with images
* but it does not tell us if the images are being displayed correctly
@@ -635,4 +581,118 @@ test.describe('Doc Export', () => {
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${docChild}.odt`);
});
/**
* Regression guard for the full PDF export pipeline.
*
* Usage reminder:
* 1. `npx playwright test __tests__/app-impress/doc-export.spec.ts --update-snapshots -g "full document" --project=chromium`
* -> refresh the reference PDF whenever we intentionally change the export output.
* 2. `npx playwright test __tests__/app-impress/doc-export.spec.ts -g "full document" --project=chromium`
* -> CI (and local runs without --update-snapshots) will compare the PDF to the reference
* and fail on any byte-level difference once the dynamic metadata has been sanitized.
*/
test('it keeps the full document PDF export identical to the reference snapshot', async ({
page,
browserName,
}, testInfo) => {
// PDF generation for a large, image-heavy document can be slow in CI.
// Give this regression test a higher timeout budget than the default.
testInfo.setTimeout(120000);
const snapshotPath = testInfo.snapshotPath(REGRESSION_SNAPSHOT_NAME);
test.skip(
!fs.existsSync(snapshotPath) &&
testInfo.config.updateSnapshots === 'none',
`Missing PDF snapshot at ${snapshotPath}. Run Playwright with --update-snapshots to record it.`,
);
// We must use a deterministic title so that block content (and thus the
// exported PDF) stays identical between runs.
await createDoc(page, 'doc-export-regression', browserName, 1);
const titleInput = page.getByRole('textbox', { name: 'Document title' });
await expect(titleInput).toBeVisible();
await titleInput.fill(REGRESSION_DOC_TITLE);
await titleInput.blur();
await verifyDocName(page, REGRESSION_DOC_TITLE);
const regressionDoc = REGRESSION_DOC_TITLE;
const docId = page
.url()
.split('/docs/')[1]
?.split('/')
.filter(Boolean)
.shift();
expect(docId).toBeTruthy();
// Inject the pre-crafted blocknote document via the REST API to avoid
// rebuilding it through the UI (which would be slow and flaky).
const cookies = await page.context().cookies();
const csrfToken = cookies.find(
(cookie) => cookie.name === 'csrftoken',
)?.value;
const headers: Record<string, string> = {
'content-type': 'application/json',
};
if (csrfToken) {
headers['X-CSRFToken'] = csrfToken;
}
const updateResponse = await page.request.patch(
`http://localhost:8071/api/v1.0/documents/${docId}/`,
{
headers,
data: {
content: REGRESSION_FIXTURE_CONTENT,
websocket: true,
},
},
);
if (!updateResponse.ok()) {
throw new Error(
`Failed to seed document content. Status: ${updateResponse.status()}, body: ${await updateResponse.text()}`,
);
}
await page.reload();
// After reloading, just ensure the editor container is present before exporting.
await expect(page.locator('.--docs--editor-container')).toBeVisible({
timeout: 15000,
});
await page
.getByRole('button', {
name: 'Export the document',
})
.click();
await expect(
page.getByTestId('doc-open-modal-download-button'),
).toBeVisible();
await expect(page.getByTestId('doc-export-download-button')).toBeEnabled({
timeout: 15000,
});
// Export to PDF and confirm the generated bytes match the reference file.
const downloadPromise = page.waitForEvent('download', {
timeout: 60000,
predicate: (download) =>
download.suggestedFilename().includes(`${regressionDoc}.pdf`),
});
void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${regressionDoc}.pdf`);
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
const normalizedPdfBuffer = sanitizePdfBuffer(pdfBuffer);
expect(normalizedPdfBuffer).toMatchSnapshot(REGRESSION_SNAPSHOT_NAME);
});
});
@@ -1,6 +1,6 @@
import { expect, test } from '@playwright/test';
import { createDoc, mockedListDocs, toggleHeaderMenu } from './utils-common';
import { createDoc, mockedListDocs } from './utils-common';
import { createRootSubPage } from './utils-sub-pages';
test.describe('Doc grid dnd', () => {
@@ -185,7 +185,10 @@ test.describe('Doc grid dnd mobile', () => {
true,
);
await toggleHeaderMenu(page);
await page
.getByRole('button', { name: 'Open the header menu' })
.getByText('menu')
.click();
await expect(page.locator('.--docs-sub-page-item').first()).toHaveAttribute(
'draggable',
@@ -408,6 +408,40 @@ test.describe('Doc Header', () => {
expect(clipboardContent.trim()).toBe('# Hello World');
});
test('It checks the copy as HTML button', async ({ page, browserName }) => {
test.skip(
browserName === 'webkit',
'navigator.clipboard is not working with webkit and playwright',
);
// create page and navigate to it
await page
.getByRole('button', {
name: 'New doc',
})
.click();
// Add dummy content to the doc
const editor = page.locator('.ProseMirror');
const docFirstBlock = editor.locator('.bn-block-content').first();
await docFirstBlock.click();
await page.keyboard.type('# Hello World', { delay: 100 });
const docFirstBlockContent = docFirstBlock.locator('h1');
await expect(docFirstBlockContent).toHaveText('Hello World');
// Copy content to clipboard
await page.getByLabel('Open the document options').click();
await page.getByRole('menuitem', { name: 'Copy as HTML' }).click();
await expect(page.getByText('Copied to clipboard')).toBeVisible();
// Test that clipboard is in HTML format
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText(),
);
const clipboardContent = await handle.jsonValue();
expect(clipboardContent.trim()).toBe(`<h1>Hello World</h1><p></p>`);
});
test('it checks the copy link button', async ({ page, browserName }) => {
test.skip(
browserName === 'webkit',
@@ -548,12 +582,8 @@ test.describe('Doc Header', () => {
await child.hover();
await child.getByText(`more_horiz`).click();
const currentUrl = page.url();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
await expect(page).not.toHaveURL(new RegExp(currentUrl));
await verifyDocName(page, duplicateTitle);
await expect(
@@ -19,8 +19,6 @@ test.describe('Doc Table Content', () => {
await page.locator('.ProseMirror').click();
await expect(page.getByRole('button', { name: 'Summary' })).toBeHidden();
await page.keyboard.type('# Level 1\n## Level 2\n### Level 3');
const summaryContainer = page.locator('#summaryContainer');
@@ -177,27 +177,3 @@ test.describe('Header: Override configuration', () => {
await expect(logoImage).toHaveAttribute('alt', '');
});
});
test.describe('Header: Skip to Content', () => {
test('it displays skip link on first TAB and focuses main content on click', async ({
page,
}) => {
await page.goto('/');
// Wait for skip button to be mounted (client-side only component)
const skipButton = page.getByRole('button', { name: 'Go to content' });
await skipButton.waitFor({ state: 'attached' });
// First TAB shows the skip button
await page.keyboard.press('Tab');
// The skip button should be visible and focused
await expect(skipButton).toBeFocused();
await expect(skipButton).toBeVisible();
// Clicking moves focus to the main content
await skipButton.click();
const mainContent = page.locator('main#mainContent');
await expect(mainContent).toBeFocused();
});
});
@@ -66,7 +66,6 @@ test.describe('Language', () => {
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
@@ -83,34 +83,6 @@ export const randomName = (name: string, browserName: string, length: number) =>
return `${browserName}-${Math.floor(Math.random() * 10000)}-${index}-${name}`;
});
export const openHeaderMenu = async (page: Page) => {
const toggleButton = page.getByTestId('header-menu-toggle');
await expect(toggleButton).toBeVisible();
const isExpanded =
(await toggleButton.getAttribute('aria-expanded')) === 'true';
if (!isExpanded) {
await toggleButton.click();
}
};
export const closeHeaderMenu = async (page: Page) => {
const toggleButton = page.getByTestId('header-menu-toggle');
await expect(toggleButton).toBeVisible();
const isExpanded =
(await toggleButton.getAttribute('aria-expanded')) === 'true';
if (isExpanded) {
await toggleButton.click();
}
};
export const toggleHeaderMenu = async (page: Page) => {
const toggleButton = page.getByTestId('header-menu-toggle');
await expect(toggleButton).toBeVisible();
await toggleButton.click();
};
export const createDoc = async (
page: Page,
docName: string,
@@ -122,7 +94,10 @@ export const createDoc = async (
for (let i = 0; i < randomDocs.length; i++) {
if (isMobile) {
await openHeaderMenu(page);
await page
.getByRole('button', { name: 'Open the header menu' })
.getByText('menu')
.click();
}
await page
@@ -2,8 +2,6 @@ import { Page, expect } from '@playwright/test';
import {
BrowserName,
closeHeaderMenu,
openHeaderMenu,
randomName,
updateDocTitle,
verifyDocName,
@@ -17,7 +15,10 @@ export const createRootSubPage = async (
isMobile = false,
) => {
if (isMobile) {
await openHeaderMenu(page);
await page
.getByRole('button', { name: 'Open the header menu' })
.getByText('menu')
.click();
}
// Get response
@@ -28,7 +29,10 @@ export const createRootSubPage = async (
const subPageJson = (await response.json()) as { id: string };
if (isMobile) {
await openHeaderMenu(page);
await page
.getByRole('button', { name: 'Open the header menu' })
.getByText('menu')
.click();
}
// Get doc tree
@@ -40,9 +44,13 @@ export const createRootSubPage = async (
.getByTestId(`doc-sub-page-item-${subPageJson.id}`)
.first();
await expect(subPageItem).toBeVisible();
await subPageItem.click();
if (isMobile) {
await closeHeaderMenu(page);
await page
.getByRole('button', { name: 'Open the header menu' })
.getByText('close')
.click();
}
// Update sub page name
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "app-e2e",
"version": "4.1.0",
"version": "3.10.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
+29
View File
@@ -0,0 +1,29 @@
//utilitary script to print the datauris of the targeted assets
const fs = require('fs');
const path = require('path');
const ASSETS_ROOT = path.resolve(__dirname, '__tests__/app-impress/assets');
function saveDataUrl(file, mime, outName) {
const abs = path.join(ASSETS_ROOT, file);
const base64 = fs.readFileSync(abs).toString('base64');
const dataUrl = `data:${mime};base64,${base64}`;
const outPath = path.join(ASSETS_ROOT, outName);
fs.writeFileSync(outPath, dataUrl, 'utf8');
console.log(`Wrote ${outName}`);
}
// PNG
saveDataUrl('panopng.png', 'image/png', 'pano-png-dataurl.txt');
// JPG
saveDataUrl('panojpg.jpeg', 'image/jpeg', 'pano-jpg-dataurl.txt');
// SVG
const svgPath = path.join(ASSETS_ROOT, 'test.svg');
const svgText = fs.readFileSync(svgPath, 'utf8');
const svgDataUrl =
'data:image/svg+xml;base64,' + Buffer.from(svgText).toString('base64');
fs.writeFileSync(path.join(ASSETS_ROOT, 'test-svg-dataurl.txt'), svgDataUrl, 'utf8');
console.log('Wrote test-svg-dataurl.txt');
+1 -1
View File
@@ -6,7 +6,7 @@ server {
root /usr/share/nginx/html;
location / {
try_files $uri index.html $uri/index.html =404;
try_files $uri index.html $uri/ =404;
add_header X-Frame-Options DENY always;
}
+16 -16
View File
@@ -1,6 +1,6 @@
{
"name": "app-impress",
"version": "4.1.0",
"version": "3.10.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
@@ -32,17 +32,17 @@
"@emoji-mart/data": "1.2.1",
"@emoji-mart/react": "1.1.1",
"@fontsource-variable/inter": "5.2.8",
"@fontsource-variable/material-symbols-outlined": "5.2.30",
"@fontsource-variable/material-symbols-outlined": "5.2.28",
"@fontsource/material-icons": "5.2.7",
"@gouvfr-lasuite/integration": "1.0.3",
"@gouvfr-lasuite/ui-kit": "0.18.0",
"@hocuspocus/provider": "3.4.0",
"@mantine/core": "8.3.9",
"@mantine/hooks": "8.3.9",
"@mantine/core": "8.3.6",
"@mantine/hooks": "8.3.6",
"@openfun/cunningham-react": "4.0.0",
"@react-pdf/renderer": "4.3.1",
"@sentry/nextjs": "10.27.0",
"@tanstack/react-query": "5.90.10",
"@tanstack/react-query": "5.90.6",
"@tiptap/extensions": "*",
"canvg": "4.0.3",
"clsx": "2.1.1",
@@ -52,17 +52,17 @@
"emoji-datasource-apple": "16.0.0",
"emoji-mart": "5.6.0",
"emoji-regex": "10.6.0",
"i18next": "25.6.3",
"i18next": "25.6.0",
"i18next-browser-languagedetector": "8.2.0",
"idb": "8.0.3",
"lodash": "4.17.21",
"luxon": "3.7.2",
"next": "15.5.7",
"posthog-js": "1.298.0",
"next": "15.5.4",
"posthog-js": "1.284.0",
"react": "*",
"react-aria-components": "1.13.0",
"react-dom": "*",
"react-i18next": "16.3.5",
"react-i18next": "16.3.3",
"react-intersection-observer": "10.0.0",
"react-resizable-panels": "3.0.6",
"react-select": "5.10.2",
@@ -74,32 +74,32 @@
},
"devDependencies": {
"@svgr/webpack": "8.1.0",
"@tanstack/react-query-devtools": "5.91.0",
"@tanstack/react-query-devtools": "5.90.2",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "16.3.0",
"@testing-library/user-event": "14.6.1",
"@types/lodash": "4.17.21",
"@types/lodash": "4.17.20",
"@types/luxon": "3.7.1",
"@types/node": "*",
"@types/react": "*",
"@types/react-dom": "*",
"@vitejs/plugin-react": "5.1.1",
"@vitejs/plugin-react": "5.1.0",
"copy-webpack-plugin": "13.0.1",
"cross-env": "10.1.0",
"dotenv": "17.2.3",
"eslint-plugin-docs": "*",
"fetch-mock": "9.11.0",
"jsdom": "27.2.0",
"jsdom": "27.1.0",
"node-fetch": "2.7.0",
"prettier": "3.6.2",
"stylelint": "16.26.0",
"stylelint": "16.25.0",
"stylelint-config-standard": "39.0.1",
"stylelint-prettier": "5.0.3",
"typescript": "*",
"vite-tsconfig-paths": "5.1.4",
"vitest": "4.0.13",
"webpack": "5.103.0",
"vitest": "4.0.6",
"webpack": "5.102.1",
"workbox-webpack-plugin": "7.1.0"
},
"packageManager": "yarn@1.22.22"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

After

Width:  |  Height:  |  Size: 992 B

@@ -1,78 +0,0 @@
import { Button } from '@openfun/cunningham-react';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { MAIN_LAYOUT_ID } from '@/layouts/conf';
export const SkipToContent = () => {
const { t } = useTranslation();
const router = useRouter();
const { spacingsTokens } = useCunninghamTheme();
const [isVisible, setIsVisible] = useState(false);
// Reset focus after route change so first TAB goes to skip link
useEffect(() => {
const handleRouteChange = () => {
(document.activeElement as HTMLElement)?.blur();
document.body.setAttribute('tabindex', '-1');
document.body.focus({ preventScroll: true });
setTimeout(() => {
document.body.removeAttribute('tabindex');
}, 100);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
const mainContent = document.getElementById(MAIN_LAYOUT_ID);
if (mainContent) {
mainContent.focus();
mainContent.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
};
return (
<Box
$css={css`
.c__button--brand--primary.--docs--skip-to-content:focus-visible {
box-shadow:
0 0 0 1px var(--c--globals--colors--white-000),
0 0 0 4px var(--c--contextuals--border--semantic--brand--primary);
border-radius: var(--c--globals--spacings--st);
}
`}
>
<Button
onClick={handleClick}
type="button"
color="brand"
className="--docs--skip-to-content"
onFocus={() => setIsVisible(true)}
onBlur={() => setIsVisible(false)}
style={{
opacity: isVisible ? 1 : 0,
pointerEvents: isVisible ? 'auto' : 'none',
position: 'fixed',
top: spacingsTokens['2xs'],
// padding header + logo(32px) + gap(3xs≈4px) + text "Docs"(≈70px) + 12px
left: `calc(${spacingsTokens['base']} + 32px + ${spacingsTokens['3xs']} + 70px + 12px)`,
zIndex: 9999,
whiteSpace: 'nowrap',
}}
>
{t('Go to content')}
</Button>
</Box>
);
};
@@ -11,6 +11,5 @@ export * from './Loading';
export * from './modal';
export * from './Overlayer';
export * from './separators';
export * from './SkipToContent';
export * from './Text';
export * from './TextErrors';
@@ -27,7 +27,7 @@ export const setAuthUrl = () => {
window.location.pathname !== '/' &&
window.location.pathname !== `${HOME_URL}/`
) {
localStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.href);
localStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.pathname);
}
};
@@ -87,9 +87,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
const { isDesktop } = useResponsiveStore();
const { isSynced: isConnectedToCollabServer } = useProviderStore();
const refEditorContainer = useRef<HTMLDivElement>(null);
const canSeeComment = doc.abilities.comment;
// Determine if comments should be visible in the UI
const showComments = canSeeComment && isDesktop;
const canSeeComment = doc.abilities.comment && isDesktop;
useSaveDoc(doc.id, provider.document, isConnectedToCollabServer);
const { i18n } = useTranslation();
@@ -209,7 +207,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
ref={refEditorContainer}
$css={css`
${cssEditor};
${cssComments(showComments, currentUserAvatarUrl)}
${cssComments(canSeeComment, currentUserAvatarUrl)}
`}
>
{errorAttachment && (
@@ -227,7 +225,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
formattingToolbar={false}
slashMenu={false}
theme="light"
comments={showComments}
comments={canSeeComment}
aria-label={t('Document editor')}
>
<BlockNoteSuggestionMenu />
@@ -108,7 +108,7 @@ export function AIGroupButton() {
data-test="ai-actions"
label="AI"
mainTooltip={t('AI Actions')}
icon={<Icon iconName="auto_awesome" $size="md" />}
icon={<Icon iconName="auto_awesome" $size="l" />}
/>
</Components.Generic.Menu.Trigger>
<Components.Generic.Menu.Dropdown
@@ -94,7 +94,6 @@ export function MarkdownButton() {
font-family: var(--c--globals--font--families--base);
`}
$weight="bold"
$size="sm"
>
M
</Text>
@@ -1,8 +1,3 @@
/**
* This file is adapted from BlockNote's AddCommentButton component
* https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/AddCommentButton.tsx
*/
import {
useBlockNoteEditor,
useComponentsContext,
@@ -15,7 +10,6 @@ import { css } from 'styled-components';
import { Box, Icon } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useDocStore } from '@/features/docs/doc-management';
import { useResponsiveStore } from '@/stores';
import {
DocsBlockSchema,
@@ -28,7 +22,6 @@ export const CommentToolbarButton = () => {
const { currentDoc } = useDocStore();
const { t } = useTranslation();
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
const { isDesktop } = useResponsiveStore();
const editor = useBlockNoteEditor<
DocsBlockSchema,
@@ -42,18 +35,7 @@ export const CommentToolbarButton = () => {
return !!selectedBlocks.find((block) => block.content !== undefined);
}, [selectedBlocks]);
const focusOnInputThread = () => {
// Use setTimeout to ensure the DOM has been updated with the new comment
setTimeout(() => {
const threadElement = document.querySelector<HTMLElement>(
'.bn-thread .bn-editor',
);
threadElement?.focus();
}, 400);
};
if (
!isDesktop ||
!show ||
!editor.isEditable ||
!Components ||
@@ -69,7 +51,6 @@ export const CommentToolbarButton = () => {
onClick={() => {
editor.comments?.startPendingComment();
editor.formattingToolbar.closeMenu();
focusOnInputThread();
}}
aria-haspopup="dialog"
data-test="comment-toolbar-button"
@@ -117,21 +117,6 @@ export class DocsThreadStore extends ThreadStore {
});
}
/**
* Scrolls to the bottom of a thread modal
* @param threadId
*/
private scrollToBottomOfThread() {
// Use setTimeout to ensure the DOM has been updated with the new comment
setTimeout(() => {
const threadElement = document.querySelector('.bn-thread');
threadElement?.scrollBy({
top: threadElement.scrollHeight,
behavior: 'smooth',
});
}, 200);
}
/**
* Notifies all subscribers about the current thread state
*/
@@ -360,10 +345,6 @@ export class DocsThreadStore extends ThreadStore {
await this.refreshThread(threadId);
}
this.ping(threadId);
// Auto-scroll to bottom of thread after adding comment
this.scrollToBottomOfThread();
return serverCommentToClientComment(comment);
};
@@ -424,20 +405,10 @@ export class DocsThreadStore extends ThreadStore {
// Optimistically remove the comment locally if we have the thread
const existing = this.threads.get(threadId);
if (existing) {
const updatedComments = existing.comments.filter(
(c) => c.id !== commentId,
);
// If this was the last comment, delete the thread
if (updatedComments.length === 0) {
await this.deleteThread({ threadId });
return;
}
const updated: ClientThreadData = {
...existing,
updatedAt: new Date(),
comments: updatedComments,
comments: existing.comments.filter((c) => c.id !== commentId),
};
this.upsertClientThreadData(updated);
this.notifySubscribers();
@@ -448,6 +419,10 @@ export class DocsThreadStore extends ThreadStore {
this.ping(threadId);
};
/**
* UI not implemented
* @param _options
*/
public deleteThread = async (_options: { threadId: string }) => {
const response = await fetchAPI(
`documents/${this.docId}/threads/${_options.threadId}/`,
@@ -11,11 +11,7 @@ export const cssComments = (
.bn-thread-mark:not([data-orphan='true']),
.bn-thread-mark-selected:not([data-orphan='true']) {
background: ${canSeeComment ? '#EDB40066' : 'transparent'};
color: var(--c--globals--colors--gray-700);
}
[data-show-selection] {
color: HighlightText;
color: var(--c--globals--colors--greyscale-700);
}
}
@@ -58,7 +54,7 @@ export const cssComments = (
& .bn-editor {
padding-left: var(--c--globals--spacings--lg);
.bn-inline-content {
color: var(--c--globals--colors--gray-700);
color: var(--c--globals--colors--greyscale-700);
}
}
@@ -69,7 +65,7 @@ export const cssComments = (
padding: var(--c--globals--spacings--0)
var(--c--globals--spacings--st);
background: none;
border: 1px solid var(--c--globals--colors--gray-300);
border: 1px solid var(--c--globals--colors--greyscale-300);
border-radius: var(--c--globals--spacings--st);
height: var(--c--globals--spacings--md);
}
@@ -107,12 +103,12 @@ export const cssComments = (
background-color: transparent;
&:hover {
background-color: var(--c--globals--colors--gray-100);
background-color: var(--c--globals--colors--greyscale-100);
}
}
button[role='menuitem'] svg {
color: var(--c--globals--colors--gray-600);
color: var(--c--globals--colors--greyscale-600);
}
}
@@ -141,7 +137,7 @@ export const cssComments = (
&[data-test='cancel'] {
background: white;
border: 1px solid var(--c--globals--colors--gray-300);
border: 1px solid var(--c--globals--colors--greyscale-300);
color: var(--c--globals--colors--info-600);
}
}
@@ -190,14 +186,13 @@ export const cssComments = (
padding: var(--c--globals--spacings--0);
&:disabled {
background: var(--c--globals--colors--gray-300);
background: var(--c--globals--colors--greyscale-300);
}
& .mantine-Button-label::before {
content: 'arrow_upward_alt';
font-family: 'Material Symbols Outlined Variable', sans-serif;
font-size: 18px;
color: var(--c--globals--colors--gray-100);
content: '🡡';
font-size: 13px;
color: var(--c--globals--colors--greyscale-100);
}
}
}
@@ -7,11 +7,6 @@ export const useHeadings = (editor: DocsBlockNoteEditor) => {
const { setHeadings, resetHeadings } = useHeadingStore();
useEffect(() => {
// Check if editor and its view are mounted before accessing document
if (!editor || !editor._tiptapEditor?.view?.dom) {
return;
}
setHeadings(editor);
let timeoutId: NodeJS.Timeout;
@@ -7,11 +7,6 @@ export const useShortcuts = (
el: HTMLDivElement | null,
) => {
useEffect(() => {
// Check if editor and its view are mounted
if (!editor || !editor._tiptapEditor?.view?.dom || !el) {
return;
}
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === '@' && editor?.isFocused()) {
const selection = window.getSelection();
@@ -37,6 +32,10 @@ export const useShortcuts = (
}
};
if (!el) {
return;
}
el.addEventListener('keydown', handleKeyDown);
return () => {
@@ -95,11 +95,6 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
);
useEffect(() => {
// Check if editor and its view are mounted before accessing document
if (!editor || !editor._tiptapEditor?.view?.dom) {
return;
}
const imagesBlocks = editor?.document.filter(
(block) =>
block.type === 'image' && block.props.url.includes(ANALYZE_URL),
@@ -115,11 +110,6 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
* block to show analyzing status
*/
useEffect(() => {
// Check if editor and its view are mounted before setting up handlers
if (!editor || !editor._tiptapEditor?.view?.dom) {
return;
}
editor.onUploadEnd((blockId) => {
if (!blockId) {
return;
@@ -28,11 +28,6 @@ export interface UseHeadingStore {
export const useHeadingStore = create<UseHeadingStore>((set, get) => ({
headings: [],
setHeadings: (editor) => {
// Check if editor and its view are mounted before accessing document
if (!editor || !editor._tiptapEditor?.view?.dom) {
return;
}
const headingBlocks = editor?.document
.filter(
(block) =>
@@ -8,7 +8,7 @@ export const cssEditor = css`
}
& .bn-editor {
color: var(--c--globals--colors--gray-700);
color: var(--c--globals--colors--greyscale-700);
}
/**
@@ -127,7 +127,7 @@ export const cssEditor = css`
font-size: 1.25rem;
}
a {
color: var(--c--globals--colors--gray-600);
color: var(--c--globals--colors--greyscale-600);
cursor: pointer;
}
.bn-block-group
@@ -16,12 +16,12 @@ describe('useModuleExport', () => {
const Export = await import('@/features/docs/doc-export/');
expect(Export.default).toBeUndefined();
}, 15000);
}, 10000);
it('should load modules when NEXT_PUBLIC_PUBLISH_AS_MIT is false', async () => {
process.env.NEXT_PUBLIC_PUBLISH_AS_MIT = 'false';
const Export = await import('@/features/docs/doc-export/');
expect(Export.default).toHaveProperty('ModalExport');
}, 15000);
});
});
@@ -1,67 +0,0 @@
import { deriveMediaFilename } from '../utils';
describe('deriveMediaFilename', () => {
test('uses last URL segment when src is a valid URL', () => {
const result = deriveMediaFilename({
src: 'https://example.com/path/video.mp4',
index: 0,
blob: new Blob([], { type: 'video/mp4' }),
});
expect(result).toBe('1-video.mp4');
});
test('handles URLs with query/hash and keeps the last segment', () => {
const result = deriveMediaFilename({
src: 'https://site.com/assets/file.name.svg?x=1#test',
index: 0,
blob: new Blob([], { type: 'image/svg+xml' }),
});
expect(result).toBe('1-file.name.svg');
});
test('handles relative URLs using last segment', () => {
const result = deriveMediaFilename({
src: 'not a valid url',
index: 0,
blob: new Blob([], { type: 'image/png' }),
});
// "not a valid url" becomes a relative URL, so we get the last segment
expect(result).toBe('1-not%20a%20valid%20url.png');
});
test('data URLs always use media-{index+1}', () => {
const result = deriveMediaFilename({
src: 'data:image/png;base64,xxx',
index: 0,
blob: new Blob([], { type: 'image/png' }),
});
expect(result).toBe('media-1.png');
});
test('adds extension from MIME when baseName has no extension', () => {
const result = deriveMediaFilename({
src: 'https://a.com/abc',
index: 0,
blob: new Blob([], { type: 'image/webp' }),
});
expect(result).toBe('1-abc.webp');
});
test('does not override extension if baseName already contains one', () => {
const result = deriveMediaFilename({
src: 'https://a.com/image.png',
index: 0,
blob: new Blob([], { type: 'image/jpeg' }),
});
expect(result).toBe('1-image.png');
});
test('handles complex MIME types (e.g., audio/mpeg)', () => {
const result = deriveMediaFilename({
src: 'https://a.com/song',
index: 1,
blob: new Blob([], { type: 'audio/mpeg' }),
});
expect(result).toBe('2-song.mpeg');
});
});
@@ -1,569 +0,0 @@
/* Reset and Base Styles */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: inter, 'roboto flex variable', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: normal;
color: #25252f;
background-color: #f9f9f9;
}
main {
max-width: 868px;
width: 100%;
margin: 0 auto;
padding: 24px 54px 32px;
background: #fff;
min-height: 100vh;
}
/* Editor Container Styles */
.--docs--editor-container {
width: 100%;
padding: 24px 0 32px;
background: #fff;
font-size: 16px;
color: #25252f;
}
/* Block Spacing */
.bn-block-outer {
margin: 0;
}
.bn-block-content {
margin-bottom: 0;
}
/* Typography */
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: 0;
line-height: 1.5;
color: #454558;
}
h1 {
font-size: 30px;
font-weight: 700;
line-height: 45px;
}
h2 {
font-size: 24px;
font-weight: 700;
line-height: 36px;
}
h3 {
font-size: 20px;
font-weight: 700;
line-height: 30px;
}
h4 {
font-size: 18px;
font-weight: 600;
line-height: 27px;
}
h5 {
font-size: 16px;
font-weight: 600;
line-height: 24px;
}
h6 {
font-size: 14px;
font-weight: 600;
line-height: 21px;
}
p {
margin: 0;
line-height: 24px;
}
.bn-inline-content {
line-height: 24px;
}
/* Links */
a {
color: #5d5d70;
text-decoration: underline;
cursor: pointer;
}
a:hover {
color: #454558;
}
/* Text Formatting */
strong,
b {
font-weight: 700;
}
em,
i {
font-style: italic;
}
u {
text-decoration: underline;
}
s {
text-decoration: line-through;
}
/* Custom text colors */
[data-style-type='textColor'][data-value='red'] {
color: rgb(224 62 62);
}
[data-style-type='backgroundColor'][data-value='blue'] {
background-color: rgb(221 235 241);
padding: 2px 4px;
border-radius: 3px;
}
/* Lists */
.bn-block-content[data-content-type='bulletListItem'],
.bn-block-content[data-content-type='numberedListItem'] {
padding-left: 0;
margin: 0;
display: list-item;
list-style-position: outside;
}
.bn-block-content[data-content-type='bulletListItem'] {
list-style-type: disc;
margin-left: 24px;
}
.bn-block-content[data-content-type='numberedListItem'] {
list-style-type: decimal;
margin-left: 24px;
}
.bn-block-content[data-content-type='bulletListItem'] p,
.bn-block-content[data-content-type='numberedListItem'] p {
display: inline;
}
/* Checkboxes */
.bn-block-content[data-content-type='checkListItem'] {
display: flex;
align-items: center;
gap: 8px;
}
.bn-block-content[data-content-type='checkListItem'] input[type='checkbox'] {
margin: 0;
width: 16px;
height: 16px;
cursor: pointer;
flex-shrink: 0;
}
.bn-block-content[data-content-type='checkListItem'] p {
flex: 1;
margin: 0;
}
/* Quotes */
blockquote,
.bn-block-content[data-content-type='quote'] blockquote {
border-left: 4px solid #a9a9bf;
padding-left: 16px;
margin: 0;
color: #7d797a;
font-style: normal;
}
/* Code Blocks */
.bn-block-content[data-content-type='codeBlock'] {
background-color: #161616;
border-radius: 8px;
padding: 3px 0;
margin: 8px 0;
overflow: auto;
}
.bn-block-content[data-content-type='codeBlock'] pre {
background-color: transparent;
padding: 24px;
margin: 0;
border-radius: 0;
overflow-x: auto;
}
.bn-block-content[data-content-type='codeBlock'] code {
font-family: monospace;
font-size: 16px;
line-height: 24px;
color: #fff;
background: transparent;
padding: 0;
}
.bn-block-content[data-content-type='codeBlock'] select {
padding: 4px 8px;
margin: 8px 0 0 24px;
border: 1px solid #3a3a3a;
border-radius: 4px;
background-color: #2a2a2a;
color: #fff;
font-size: 12.8px;
cursor: pointer;
}
/* Inline Code */
code {
font-family: monospace;
font-size: 16px;
background-color: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
}
pre code {
background: none;
padding: 0;
}
/* Tables */
table {
border-collapse: collapse;
width: 100%;
margin: 8px 0;
}
th,
td {
border: 1px solid #ddd;
padding: 5px 10px;
text-align: left;
vertical-align: top;
}
th {
background-color: #f5f5f5;
font-weight: 700;
}
td p {
margin: 0;
}
/* Callout Blocks */
.bn-block-content[data-content-type='callout'] {
display: flex;
align-items: flex-start;
gap: 8px;
background-color: #fbf3db;
border-radius: 4px;
padding: 12px;
margin: 8px 0;
}
.bn-block-content[data-content-type='callout'][data-background-color='yellow'] {
background-color: #fbf3db;
}
.bn-block-content[data-content-type='callout'][data-background-color='blue'] {
background-color: #ddedf1;
}
.bn-block-content[data-content-type='callout'][data-background-color='red'] {
background-color: #ffe5e5;
}
.bn-block-content[data-content-type='callout'][data-background-color='green'] {
background-color: #e5f5e5;
}
.bn-block-content[data-content-type='callout'] > div {
display: flex;
flex-direction: row;
}
.bn-block-content[data-content-type='callout'] button {
background: transparent;
border: none;
padding: 0;
cursor: default;
font-size: 20px;
line-height: 1;
flex-shrink: 0;
}
.bn-block-content[data-content-type='callout'] .inline-content {
flex: 1;
}
/* Toggle Sections */
.bn-toggle-wrapper {
display: flex;
align-items: flex-start;
gap: 4px;
}
.bn-toggle-button {
width: 24px;
height: 24px;
padding: 3px;
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}
.bn-toggle-button:hover {
background-color: #f0f0f0;
}
.bn-toggle-button svg {
width: 18px;
height: 18px;
fill: #454558;
transition: transform 0.2s;
}
.bn-toggle-wrapper[data-show-children='true'] .bn-toggle-button svg {
transform: rotate(90deg);
}
.bn-toggle-wrapper h1,
.bn-toggle-wrapper h2,
.bn-toggle-wrapper h3,
.bn-toggle-wrapper h4,
.bn-toggle-wrapper h5,
.bn-toggle-wrapper h6,
.bn-toggle-wrapper p {
flex: 1;
margin: 0;
}
/* Toggle List Items */
.bn-block-content[data-content-type='toggleListItem'] {
margin-left: 24px;
}
/* Images and Media */
img.bn-visual-media {
max-width: 100%;
height: auto;
display: block;
border-radius: 4px;
margin: 8px 0;
}
video.bn-visual-media {
width: 100% !important;
height: auto !important;
max-width: 760px;
display: block;
border-radius: 4px;
margin: 8px 0;
object-fit: contain;
background-color: #000;
}
audio.bn-audio {
width: 100%;
max-width: 760px;
margin: 8px 0;
}
/* File Blocks */
.bn-file-block-content-wrapper {
margin: 8px 0;
max-width: 100%;
}
figure.bn-file-block-content-wrapper {
margin: 8px 0;
max-width: 100%;
}
.bn-block-content[data-content-type='video'] .bn-file-block-content-wrapper {
display: flex;
flex-direction: column;
width: fit-content;
max-width: 100%;
}
.bn-visual-media-wrapper {
display: flex;
position: relative;
max-width: 100%;
}
.bn-file-caption,
figcaption.bn-file-caption {
color: #454558;
font-size: 12.8px;
margin-top: 4px;
text-align: left;
font-style: normal;
}
/* Add File Button */
.bn-add-file-button {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
border: 2px dashed #d0d0d0;
border-radius: 8px;
cursor: pointer;
transition:
border-color 0.2s,
background-color 0.2s;
}
.bn-add-file-button:hover {
border-color: #a0a0a0;
background-color: #f9f9f9;
}
.bn-add-file-button-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.bn-add-file-button-icon svg {
width: 100%;
height: 100%;
fill: #757575;
}
.bn-add-file-button-text {
margin: 0;
color: #757575;
font-size: 14px;
font-weight: 500;
}
/* Buttons */
button {
cursor: pointer;
font-family: inherit;
}
/* Select/Dropdown */
select {
font-family: inherit;
cursor: pointer;
}
/* Spacing between blocks */
.bn-block-outer + .bn-block-outer {
margin-top: 4px;
}
/* Heading spacing */
.bn-block-content[data-content-type='heading'] + .bn-block-outer {
margin-top: 8px;
}
.bn-block-outer + .bn-block-content[data-content-type='heading'] {
margin-top: 16px;
}
/* Responsive */
@media (width <= 920px) {
main {
padding: 24px 32px 32px;
}
}
@media (width <= 768px) {
main {
padding: 16px 16px 32px;
}
h1 {
font-size: 26px;
line-height: 39px;
}
h2 {
font-size: 22px;
line-height: 33px;
}
h3 {
font-size: 18px;
line-height: 27px;
}
video.bn-visual-media {
max-width: 100%;
}
}
/* Print styles */
@media print {
body {
background: white;
}
main {
max-width: 100%;
padding: 0;
}
button,
select,
.bn-add-file-button {
display: none;
}
a {
color: #000;
text-decoration: underline;
}
.bn-block-content[data-content-type='codeBlock'] {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
.bn-block-content[data-content-type='codeBlock'] code {
color: #000;
}
}
/* Accessibility */
*:focus-visible {
outline: 2px solid #06c;
outline-offset: 2px;
}
/* Hidden elements for export */
[contenteditable='false'] {
user-select: none;
}
@@ -13,30 +13,22 @@ import {
import { DocumentProps, pdf } from '@react-pdf/renderer';
import jsonemoji from 'emoji-datasource-apple' assert { type: 'json' };
import i18next from 'i18next';
import JSZip from 'jszip';
import { cloneElement, isValidElement, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, ButtonCloseModal, Text } from '@/components';
import { useMediaUrl } from '@/core';
import { useEditorStore } from '@/docs/doc-editor';
import { Doc, useTrans } from '@/docs/doc-management';
import { fallbackLng } from '@/i18n/config';
import { exportCorsResolveFileUrl } from '../api/exportResolveFileUrl';
import { TemplatesOrdering, useTemplates } from '../api/useTemplates';
import { docxDocsSchemaMappings } from '../mappingDocx';
import { odtDocsSchemaMappings } from '../mappingODT';
import { pdfDocsSchemaMappings } from '../mappingPDF';
import {
addMediaFilesToZip,
downloadFile,
generateHtmlDocument,
} from '../utils';
import { downloadFile } from '../utils';
enum DocDownloadFormat {
HTML = 'html',
PDF = 'pdf',
DOCX = 'docx',
ODT = 'odt',
@@ -60,7 +52,6 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
DocDownloadFormat.PDF,
);
const { untitledDocument } = useTrans();
const mediaUrl = useMediaUrl();
const templateOptions = useMemo(() => {
const templateOptions = (templates?.pages || [])
@@ -151,47 +142,13 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
});
blobExport = await exporter.toODTDocument(exportDocument);
} else if (format === DocDownloadFormat.HTML) {
// Use BlockNote "full HTML" export so that we stay closer to the editor rendering.
const fullHtml = await editor.blocksToFullHTML();
// Parse HTML and fetch media so that we can package a fully offline HTML document in a ZIP.
const domParser = new DOMParser();
const parsedDocument = domParser.parseFromString(fullHtml, 'text/html');
const zip = new JSZip();
await addMediaFilesToZip(parsedDocument, zip, mediaUrl);
const lang = i18next.language || fallbackLng;
const editorHtmlWithLocalMedia = parsedDocument.body.innerHTML;
const htmlContent = generateHtmlDocument(
documentTitle,
editorHtmlWithLocalMedia,
lang,
);
zip.file('index.html', htmlContent);
// CSS Styles
const cssResponse = await fetch(
new URL('../assets/export-html-styles.txt', import.meta.url).toString(),
);
const cssContent = await cssResponse.text();
zip.file('styles.css', cssContent);
blobExport = await zip.generateAsync({ type: 'blob' });
} else {
toast(t('The export failed'), VariantType.ERROR);
setIsExporting(false);
return;
}
const downloadExtension =
format === DocDownloadFormat.HTML ? 'zip' : format;
downloadFile(blobExport, `${filename}.${downloadExtension}`);
downloadFile(blobExport, `${filename}.${format}`);
toast(
t('Your {{format}} was downloaded succesfully', {
@@ -268,10 +225,18 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
className="--docs--modal-export-content"
>
<Text $variation="secondary" $size="sm" as="p">
{t(
'Download your document in a .docx, .odt, .pdf or .html(zip) format.',
)}
{t('Download your document in a .docx, .odt or .pdf format.')}
</Text>
<Select
clearable={false}
fullWidth
label={t('Template')}
options={templateOptions}
value={templateSelected}
onChange={(options) =>
setTemplateSelected(options.target.value as string)
}
/>
<Select
clearable={false}
fullWidth
@@ -280,24 +245,12 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
{ label: t('Docx'), value: DocDownloadFormat.DOCX },
{ label: t('ODT'), value: DocDownloadFormat.ODT },
{ label: t('PDF'), value: DocDownloadFormat.PDF },
{ label: t('HTML'), value: DocDownloadFormat.HTML },
]}
value={format}
onChange={(options) =>
setFormat(options.target.value as DocDownloadFormat)
}
/>
<Select
clearable={false}
fullWidth
label={t('Template')}
options={templateOptions}
value={templateSelected}
disabled={format === DocDownloadFormat.HTML}
onChange={(options) =>
setTemplateSelected(options.target.value as string)
}
/>
{isExporting && (
<Box
@@ -5,11 +5,8 @@ import {
} from '@blocknote/core';
import { Canvg } from 'canvg';
import { IParagraphOptions, ShadingType } from 'docx';
import JSZip from 'jszip';
import React from 'react';
import { exportResolveFileUrl } from './api';
export function downloadFile(blob: Blob, filename: string) {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -182,172 +179,3 @@ export function odtRegisterParagraphStyleForBlock(
return styleName;
}
// Escape user-provided text before injecting it into the exported HTML document.
export const escapeHtml = (value: string): string =>
value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
interface MediaFilenameParams {
src: string;
index: number;
blob: Blob;
}
/**
* Derives a stable, readable filename for media exported in the HTML ZIP.
*
* Rules:
* - Default base name is "media-{index+1}".
* - For non data: URLs, we reuse the last path segment when possible (e.g. 1-photo.png).
* - If the base name has no extension, we try to infer one from the blob MIME type.
*/
export const deriveMediaFilename = ({
src,
index,
blob,
}: MediaFilenameParams): string => {
// Default base name
let baseName = `media-${index + 1}`;
// Try to reuse the last path segment for non data URLs.
if (!src.startsWith('data:')) {
try {
const url = new URL(src, window.location.origin);
const lastSegment = url.pathname.split('/').pop();
if (lastSegment) {
baseName = `${index + 1}-${lastSegment}`;
}
} catch {
// Ignore invalid URLs, keep default baseName.
}
}
let filename = baseName;
// Ensure the filename has an extension consistent with the blob MIME type.
const mimeType = blob.type;
if (mimeType && !baseName.includes('.')) {
const slashIndex = mimeType.indexOf('/');
const rawSubtype =
slashIndex !== -1 && slashIndex < mimeType.length - 1
? mimeType.slice(slashIndex + 1)
: '';
let extension = '';
const subtype = rawSubtype.toLowerCase();
if (subtype.includes('svg')) {
extension = 'svg';
} else if (subtype.includes('jpeg') || subtype.includes('pjpeg')) {
extension = 'jpg';
} else if (subtype.includes('png')) {
extension = 'png';
} else if (subtype.includes('gif')) {
extension = 'gif';
} else if (subtype.includes('webp')) {
extension = 'webp';
} else if (subtype.includes('pdf')) {
extension = 'pdf';
} else if (subtype) {
extension = subtype.split('+')[0];
}
if (extension) {
filename = `${baseName}.${extension}`;
}
}
return filename;
};
/**
* Generates a complete HTML document structure for export.
*
* @param documentTitle - The title of the document (will be escaped)
* @param editorHtmlWithLocalMedia - The HTML content from the editor
* @param lang - The language code for the document (e.g., 'fr', 'en')
* @returns A complete HTML5 document string
*/
export const generateHtmlDocument = (
documentTitle: string,
editorHtmlWithLocalMedia: string,
lang: string,
): string => {
return `<!DOCTYPE html>
<html lang="${lang}">
<head>
<meta charset="utf-8" />
<title>${escapeHtml(documentTitle)}</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main role="main">
${editorHtmlWithLocalMedia}
</main>
</body>
</html>`;
};
export const addMediaFilesToZip = async (
parsedDocument: Document,
zip: JSZip,
mediaUrl: string,
) => {
const mediaFiles: { filename: string; blob: Blob }[] = [];
const mediaElements = Array.from(
parsedDocument.querySelectorAll<
HTMLImageElement | HTMLVideoElement | HTMLAudioElement | HTMLSourceElement
>('img, video, audio, source'),
);
await Promise.all(
mediaElements.map(async (element, index) => {
const src = element.getAttribute('src');
if (!src) {
return;
}
// data: URLs are already embedded and work offline; no need to create separate files.
if (src.startsWith('data:')) {
return;
}
// Only download same-origin resources (internal media like /media/...).
// External URLs keep their original src and are not included in the ZIP
let url: URL | null = null;
try {
url = new URL(src, mediaUrl);
} catch {
url = null;
}
if (!url || url.origin !== mediaUrl) {
return;
}
const fetched = await exportResolveFileUrl(url.href);
if (!(fetched instanceof Blob)) {
return;
}
const filename = deriveMediaFilename({
src: url.href,
index,
blob: fetched,
});
element.setAttribute('src', filename);
mediaFiles.push({ filename, blob: fetched });
}),
);
mediaFiles.forEach(({ filename, blob }) => {
zip.file(filename, blob);
});
};
@@ -0,0 +1,86 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { Fragment } from 'react';
import { beforeEach, describe, expect, vi } from 'vitest';
import { AbstractAnalytic, Analytics } from '@/libs';
import { AppWrapper } from '@/tests/utils';
import { DocToolBox } from '../components/DocToolBox';
let flag = true;
class TestAnalytic extends AbstractAnalytic {
public constructor() {
super();
}
public Provider() {
return <Fragment />;
}
public trackEvent() {}
public isFeatureFlagActivated(flagName: string): boolean {
if (flagName === 'CopyAsHTML') {
return flag;
}
return true;
}
}
vi.mock('next/router', async () => ({
...(await vi.importActual('next/router')),
useRouter: () => ({
push: vi.fn(),
}),
}));
const doc = {
nb_accesses: 1,
abilities: {
versions_list: true,
destroy: true,
},
};
beforeEach(() => {
Analytics.clearAnalytics();
process.env.NEXT_PUBLIC_PUBLISH_AS_MIT = 'false';
});
describe('DocToolBox "Copy as HTML" option', () => {
test('renders "Copy as HTML" option when feature flag is enabled', async () => {
new TestAnalytic();
render(<DocToolBox doc={doc as any} />, {
wrapper: AppWrapper,
});
const optionsButton = await screen.findByLabelText(
'Open the document options',
);
await userEvent.click(optionsButton);
expect(await screen.findByText('Copy as HTML')).toBeInTheDocument();
});
test('does not render "Copy as HTML" option when feature flag is disabled', async () => {
flag = false;
new TestAnalytic();
render(<DocToolBox doc={doc as any} />, {
wrapper: AppWrapper,
});
const optionsButton = screen.getByLabelText('Open the document options');
await userEvent.click(optionsButton);
expect(screen.queryByText('Copy as HTML')).not.toBeInTheDocument();
});
test('render "Copy as HTML" option when we did not add analytics', async () => {
render(<DocToolBox doc={doc as any} />, {
wrapper: AppWrapper,
});
const optionsButton = screen.getByLabelText('Open the document options');
await userEvent.click(optionsButton);
expect(screen.getByText('Copy as HTML')).toBeInTheDocument();
});
});
@@ -42,11 +42,10 @@ describe('DocToolBox - Licence', () => {
});
const optionsButton = await screen.findByLabelText('Export the document');
await userEvent.click(optionsButton);
// Wait for the export modal to be visible, then assert on its content text.
await screen.findByTestId('modal-export-title');
expect(
screen.getByText(/Download your document in a .docx, .odt.*format\./i),
await screen.findByText(
'Download your document in a .docx, .odt or .pdf format.',
),
).toBeInTheDocument();
}, 10000);
@@ -33,6 +33,7 @@ import {
KEY_LIST_DOC_VERSIONS,
ModalSelectVersion,
} from '@/docs/doc-versioning';
import { useAnalytics } from '@/libs';
import { useResponsiveStore } from '@/stores';
import { useCopyCurrentEditorToClipboard } from '../hooks/useCopyCurrentEditorToClipboard';
@@ -66,6 +67,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
void router.push(`/docs/${data.id}`);
},
});
const { isFeatureFlagActivated } = useAnalytics();
const removeFavoriteDoc = useDeleteFavoriteDoc({
listInvalidQueries: [KEY_LIST_DOC, KEY_DOC],
});
@@ -153,6 +155,14 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
callback: () => {
void copyCurrentEditorToClipboard('markdown');
},
},
{
label: t('Copy as {{format}}', { format: 'HTML' }),
icon: 'content_copy',
callback: () => {
void copyCurrentEditorToClipboard('html');
},
show: isFeatureFlagActivated('CopyAsHTML'),
showSeparator: true,
},
{
@@ -1,8 +1,4 @@
import {
UseMutationOptions,
useMutation,
useQueryClient,
} from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { APIError, errorCauses, fetchAPI } from '@/api';
@@ -10,14 +6,9 @@ import { Doc } from '../types';
import { KEY_LIST_DOC } from './useDocs';
type CreateDocParams = {
title?: string;
} | void;
export const createDoc = async (params: CreateDocParams): Promise<Doc> => {
export const createDoc = async (): Promise<Doc> => {
const response = await fetchAPI(`documents/`, {
method: 'POST',
body: JSON.stringify({ title: params?.title }),
});
if (!response.ok) {
@@ -27,17 +18,23 @@ export const createDoc = async (params: CreateDocParams): Promise<Doc> => {
return response.json() as Promise<Doc>;
};
type UseCreateDocOptions = UseMutationOptions<Doc, APIError, CreateDocParams>;
interface CreateDocProps {
onSuccess: (data: Doc) => void;
onError?: (error: APIError) => void;
}
export function useCreateDoc(options?: UseCreateDocOptions) {
export function useCreateDoc({ onSuccess, onError }: CreateDocProps) {
const queryClient = useQueryClient();
return useMutation<Doc, APIError, CreateDocParams>({
return useMutation<Doc, APIError>({
mutationFn: createDoc,
onSuccess: (data, variables, onMutateResult, context) => {
onSuccess: (data) => {
void queryClient.resetQueries({
queryKey: [KEY_LIST_DOC],
});
options?.onSuccess?.(data, variables, onMutateResult, context);
onSuccess(data);
},
onError: (error) => {
onError?.(error);
},
});
}
@@ -4,13 +4,12 @@ import { css } from 'styled-components';
import { Box, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Doc, useDocUtils, useTrans } from '@/docs/doc-management';
import { useResponsiveStore } from '@/stores';
import ChildDocument from '../assets/child-document.svg';
import PinnedDocumentIcon from '../assets/pinned-document.svg';
import SimpleFileIcon from '../assets/simple-document.svg';
import { useDocUtils, useTrans } from '../hooks';
import { Doc } from '../types';
const ItemTextCss = css`
overflow: hidden;
@@ -1,21 +1,17 @@
import {
UseMutationOptions,
useMutation,
useQueryClient,
} from '@tanstack/react-query';
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { APIError, errorCauses, fetchAPI } from '@/api';
import { Doc, LinkReach, LinkRole } from '@/docs/doc-management';
import { Doc } from '@/docs/doc-management';
export type UpdateDocLinkParams = Pick<Doc, 'id' | 'link_reach'> &
Partial<Pick<Doc, 'link_role'>>;
type UpdateDocLinkResponse = { link_role: LinkRole; link_reach: LinkReach };
export const updateDocLink = async ({
id,
...params
}: UpdateDocLinkParams): Promise<UpdateDocLinkResponse> => {
}: UpdateDocLinkParams): Promise<Doc> => {
const response = await fetchAPI(`documents/${id}/link-configuration/`, {
method: 'PUT',
body: JSON.stringify({
@@ -30,31 +26,40 @@ export const updateDocLink = async ({
);
}
return response.json() as Promise<UpdateDocLinkResponse>;
return response.json() as Promise<Doc>;
};
type UseUpdateDocLinkOptions = UseMutationOptions<
UpdateDocLinkResponse,
APIError,
UpdateDocLinkParams
> & {
interface UpdateDocLinkProps {
onSuccess?: (data: Doc) => void;
listInvalidQueries?: string[];
};
}
export function useUpdateDocLink(options?: UseUpdateDocLinkOptions) {
export function useUpdateDocLink({
onSuccess,
listInvalidQueries,
}: UpdateDocLinkProps = {}) {
const queryClient = useQueryClient();
const { toast } = useToastProvider();
const { t } = useTranslation();
return useMutation<UpdateDocLinkResponse, APIError, UpdateDocLinkParams>({
return useMutation<Doc, APIError, UpdateDocLinkParams>({
mutationFn: updateDocLink,
...options,
onSuccess: (data, variables, onMutateResult, context) => {
options?.listInvalidQueries?.forEach((queryKey) => {
onSuccess: (data) => {
listInvalidQueries?.forEach((queryKey) => {
void queryClient.invalidateQueries({
queryKey: [queryKey],
});
});
options?.onSuccess?.(data, variables, onMutateResult, context);
toast(
t('The document visibility has been updated.'),
VariantType.SUCCESS,
{
duration: 2000,
},
);
onSuccess?.(data);
},
});
}
@@ -1,8 +1,4 @@
import {
Button,
VariantType,
useToastProvider,
} from '@openfun/cunningham-react';
import { Button } from '@openfun/cunningham-react';
import { useTranslation } from 'react-i18next';
import { Box, Card, Text } from '@/components';
@@ -21,15 +17,9 @@ interface DocDesynchronizedProps {
export const DocDesynchronized = ({ doc }: DocDesynchronizedProps) => {
const { t } = useTranslation();
const { spacingsTokens } = useCunninghamTheme();
const { toast } = useToastProvider();
const { mutate: updateDocLink } = useUpdateDocLink({
listInvalidQueries: [KEY_LIST_DOC, KEY_DOC],
onSuccess: () => {
toast(t('The document visibility restored.'), VariantType.SUCCESS, {
duration: 2000,
});
},
});
return (
@@ -1,4 +1,3 @@
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
@@ -42,7 +41,6 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
const { isDesynchronized } = useDocUtils(doc);
const { linkModeTranslations, linkReachChoices, linkReachTranslations } =
useTranslatedShareSettings();
const { toast } = useToastProvider();
const description =
docLinkRole === LinkRole.READER
@@ -51,15 +49,6 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
const { mutate: updateDocLink } = useUpdateDocLink({
listInvalidQueries: [KEY_LIST_DOC, KEY_DOC],
onSuccess: () => {
toast(
t('The document visibility has been updated.'),
VariantType.SUCCESS,
{
duration: 2000,
},
);
},
});
const linkReachOptions: DropdownMenuOption[] = useMemo(() => {
@@ -4,12 +4,7 @@ import { css } from 'styled-components';
import { Box, BoxButton, Icon, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import {
DocsBlockNoteEditor,
HeadingBlock,
useEditorStore,
useHeadingStore,
} from '@/docs/doc-editor';
import { useEditorStore, useHeadingStore } from '@/docs/doc-editor';
import { MAIN_LAYOUT_ID } from '@/layouts/conf';
import { Heading } from './Heading';
@@ -17,8 +12,6 @@ import { Heading } from './Heading';
export const TableContent = () => {
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
const [containerHeight, setContainerHeight] = useState('100vh');
const { headings } = useHeadingStore();
const { editor } = useEditorStore();
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
@@ -37,15 +30,6 @@ export const TableContent = () => {
setIsOpen(true);
};
if (
!editor ||
!headings ||
headings.length === 0 ||
(headings.length === 1 && !headings[0].contentText)
) {
return null;
}
return (
<Box
$height={containerHeight}
@@ -112,13 +96,7 @@ export const TableContent = () => {
/>
</BoxButton>
)}
{isOpen && (
<TableContentOpened
setIsOpen={setIsOpen}
headings={headings}
editor={editor}
/>
)}
{isOpen && <TableContentOpened setIsOpen={setIsOpen} />}
</Box>
</Box>
);
@@ -126,13 +104,11 @@ export const TableContent = () => {
const TableContentOpened = ({
setIsOpen,
headings,
editor,
}: {
setIsOpen: (isOpen: boolean) => void;
headings: HeadingBlock[];
editor: DocsBlockNoteEditor;
}) => {
const { headings } = useHeadingStore();
const { editor } = useEditorStore();
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
const [headingIdHighlight, setHeadingIdHighlight] = useState<string>();
const { t } = useTranslation();
@@ -196,6 +172,15 @@ const TableContentOpened = ({
setIsOpen(false);
};
if (
!editor ||
!headings ||
headings.length === 0 ||
(headings.length === 1 && !headings[0].contentText)
) {
return null;
}
return (
<Box
$width="100%"
@@ -121,6 +121,7 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
background-color: var(--c--globals--colors--gray-000);
.light-doc-item-actions {
display: ${menuOpen || !isDesktop ? 'flex' : 'none'};
position: absolute;
right: var(--c--globals--spacings--0);
}
.c__tree-view--node.isFocused {
@@ -234,7 +234,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
}
.doc-tree-root-item-actions {
display: ${rootActionsOpen ? 'flex' : 'none'};
display: flex;
opacity: ${rootActionsOpen ? '1' : '0'};
&:has(.isOpen) {
@@ -244,7 +244,6 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
&:hover,
&:focus-visible {
.doc-tree-root-item-actions {
display: flex;
opacity: 1;
}
}
@@ -4,10 +4,10 @@ import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
import { useModal } from '@openfun/cunningham-react';
import { useQueryClient } from '@tanstack/react-query';
import { useMemo, useRef } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useTranslation } from 'react-i18next';
import { AlertModal, Card, Text } from '@/components';
import { Doc, KEY_LIST_DOC, useTrans } from '@/docs/doc-management';
import { Doc, KEY_LIST_DOC } from '@/docs/doc-management';
import {
getDocAccesses,
getDocInvitations,
@@ -60,7 +60,6 @@ export const DraggableDocGridContentList = ({
const { mutate: handleDeleteInvitation } = useDeleteDocInvitation();
const { mutate: handleDeleteAccess } = useDeleteDocAccess();
const onDragData = useRef<DocDragEndData | null>(null);
const { untitledDocument } = useTrans();
const handleMoveDoc = async () => {
if (!onDragData.current) {
@@ -145,8 +144,8 @@ export const DraggableDocGridContentList = ({
return t('You must be at least the administrator of the target document');
}
return selectedDoc?.title || untitledDocument;
}, [canDrag, canDrop, selectedDoc?.title, t, untitledDocument]);
return selectedDoc?.title || t('Unnamed document');
}, [canDrag, canDrop, selectedDoc, t]);
const cannotMoveDoc =
!canDrag || (canDrop !== undefined && !canDrop) || isError;
@@ -194,16 +193,17 @@ export const DraggableDocGridContentList = ({
{...modalConfirmation}
title={t('Move document')}
description={
<Text $display="inline">
<Trans
i18nKey="By moving this document to <strong>{{targetDocumentTitle}}</strong>, it will lose its current access rights and inherit the permissions of that document. <strong>This access change cannot be undone.</strong>"
values={{
targetDocumentTitle:
onDragData.current?.target.title ?? untitledDocument,
}}
components={{ strong: <strong /> }}
/>
</Text>
<span
dangerouslySetInnerHTML={{
__html: t(
'By moving this document to <strong>{{targetDocumentTitle}}</strong>, it will lose its current access rights and inherit the permissions of that document. <strong>This access change cannot be undone.</strong>',
{
targetDocumentTitle:
onDragData.current?.target.title ?? t('Unnamed document'),
},
),
}}
/>
}
confirmLabel={t('Move')}
onConfirm={() => {
@@ -12,16 +12,12 @@ export const ButtonTogglePanel = () => {
<Button
size="medium"
onClick={() => togglePanel()}
aria-label={t(
isPanelOpen ? 'Close the header menu' : 'Open the header menu',
)}
aria-expanded={isPanelOpen}
aria-label={t('Open the header menu')}
variant="tertiary"
icon={
<Icon $withThemeInherited iconName={isPanelOpen ? 'close' : 'menu'} />
}
className="--docs--button-toggle-panel"
data-testid="header-menu-toggle"
/>
);
};
@@ -2,7 +2,7 @@ import Image from 'next/image';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, SkipToContent, StyledLink } from '@/components/';
import { Box, StyledLink } from '@/components/';
import { useConfig } from '@/core/config';
import { useCunninghamTheme } from '@/cunningham';
import { ButtonLogin } from '@/features/auth';
@@ -25,76 +25,72 @@ export const Header = () => {
config?.theme_customization?.header?.icon || componentTokens.icon;
return (
<>
<SkipToContent />
<Box
as="header"
role="banner"
<Box
as="header"
role="banner"
$css={css`
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: ${HEADER_HEIGHT}px;
padding: 0 ${spacingsTokens['base']};
background-color: var(--c--contextuals--background--surface--primary);
border-bottom: 1px solid var(--c--contextuals--border--surface--primary);
`}
className="--docs--header"
>
{!isDesktop && <ButtonTogglePanel />}
<StyledLink
href="/"
data-testid="header-logo-link"
aria-label={t('Back to homepage')}
$css={css`
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: ${HEADER_HEIGHT}px;
padding: 0 ${spacingsTokens['base']};
background-color: var(--c--contextuals--background--surface--primary);
border-bottom: 1px solid
var(--c--contextuals--border--surface--primary);
outline: none;
&:focus-visible {
box-shadow: 0 0 0 2px var(--c--globals--colors--brand-400) !important;
border-radius: var(--c--globals--spacings--st);
}
`}
className="--docs--header"
>
{!isDesktop && <ButtonTogglePanel />}
<StyledLink
href="/"
data-testid="header-logo-link"
aria-label={t('Back to homepage')}
$css={css`
outline: none;
&:focus-visible {
box-shadow: 0 0 0 2px var(--c--globals--colors--brand-400) !important;
border-radius: var(--c--globals--spacings--st);
}
`}
<Box
$align="center"
$gap={spacingsTokens['3xs']}
$direction="row"
$position="relative"
$height="fit-content"
$margin={{ top: 'auto' }}
>
<Box
$align="center"
$gap={spacingsTokens['3xs']}
$direction="row"
$position="relative"
$height="fit-content"
$margin={{ top: 'auto' }}
>
<Image
data-testid="header-icon-docs"
src={icon.src || ''}
alt=""
width={0}
height={0}
style={{
width: icon.width,
height: icon.height,
}}
priority
/>
<Title headingLevel="h1" aria-hidden="true" />
</Box>
</StyledLink>
{!isDesktop ? (
<Box $direction="row" $gap={spacingsTokens['sm']}>
<LaGaufre />
</Box>
) : (
<Box $align="center" $gap={spacingsTokens['sm']} $direction="row">
<ButtonLogin />
<LanguagePicker />
<LaGaufre />
</Box>
)}
</Box>
</>
<Image
data-testid="header-icon-docs"
src={icon.src || ''}
alt=""
width={0}
height={0}
style={{
width: icon.width,
height: icon.height,
}}
priority
/>
<Title headingLevel="h1" aria-hidden="true" />
</Box>
</StyledLink>
{!isDesktop ? (
<Box $direction="row" $gap={spacingsTokens['sm']}>
<LaGaufre />
</Box>
) : (
<Box $align="center" $gap={spacingsTokens['sm']} $direction="row">
<ButtonLogin />
<LanguagePicker />
<LaGaufre />
</Box>
)}
</Box>
);
};
@@ -5,7 +5,6 @@ import { css } from 'styled-components';
import { Box, Icon, Text } from '@/components';
import { Footer } from '@/features/footer';
import { LeftPanel } from '@/features/left-panel';
import { MAIN_LAYOUT_ID } from '@/layouts/conf';
import { useResponsiveStore } from '@/stores';
import SC1ResponsiveEn from '../assets/SC1-responsive-en.png';
@@ -35,19 +34,8 @@ export function HomeContent() {
<Box
as="main"
role="main"
id={MAIN_LAYOUT_ID}
tabIndex={-1}
className="--docs--home-content"
aria-label={t('Main content')}
$css={css`
&:focus {
outline: 3px solid var(--c--theme--colors--primary-600);
outline-offset: -3px;
}
&:focus:not(:focus-visible) {
outline: none;
}
`}
>
<HomeHeader />
{isSmallMobile && (
@@ -12,7 +12,7 @@ import { useLeftPanelStore } from '../stores';
export const LeftPanelHeaderButton = () => {
const router = useRouter();
const { t } = useTranslation();
const { closePanel } = useLeftPanelStore();
const { togglePanel } = useLeftPanelStore();
const { setIsSkeletonVisible } = useSkeletonStore();
const [isNavigating, setIsNavigating] = useState(false);
@@ -25,7 +25,7 @@ export const LeftPanelHeaderButton = () => {
.then(() => {
// The skeleton will be disabled by the [id] page once the data is loaded
setIsNavigating(false);
closePanel();
togglePanel();
})
.catch(() => {
// In case of navigation error, disable the skeleton
@@ -6,8 +6,6 @@ import {
PanelResizeHandle,
} from 'react-resizable-panels';
import { useResponsiveStore } from '@/stores';
// Convert a target pixel width to a percentage of the current viewport width.
const pxToPercent = (px: number) => {
return (px / window.innerWidth) * 100;
@@ -26,27 +24,18 @@ export const ResizableLeftPanel = ({
minPanelSizePx = 300,
maxPanelSizePx = 450,
}: ResizableLeftPanelProps) => {
const { isDesktop } = useResponsiveStore();
const ref = useRef<ImperativePanelHandle>(null);
const savedWidthPxRef = useRef<number>(minPanelSizePx);
const [panelSizePercent, setPanelSizePercent] = useState(() =>
pxToPercent(minPanelSizePx),
);
const minPanelSizePercent = pxToPercent(minPanelSizePx);
const maxPanelSizePercent = Math.min(pxToPercent(maxPanelSizePx), 40);
const [panelSizePercent, setPanelSizePercent] = useState(() => {
const initialSize = pxToPercent(minPanelSizePx);
return Math.max(
minPanelSizePercent,
Math.min(initialSize, maxPanelSizePercent),
);
});
// Keep pixel width constant on window resize
useEffect(() => {
if (!isDesktop) {
return;
}
const handleResize = () => {
const newPercent = pxToPercent(savedWidthPxRef.current);
setPanelSizePercent(newPercent);
@@ -59,7 +48,7 @@ export const ResizableLeftPanel = ({
return () => {
window.removeEventListener('resize', handleResize);
};
}, [isDesktop]);
}, []);
const handleResize = (sizePercent: number) => {
const widthPx = (sizePercent / 100) * window.innerWidth;
@@ -68,29 +57,29 @@ export const ResizableLeftPanel = ({
};
return (
<PanelGroup direction="horizontal">
<Panel
ref={ref}
order={0}
defaultSize={isDesktop ? panelSizePercent : 0}
minSize={isDesktop ? minPanelSizePercent : 0}
maxSize={isDesktop ? maxPanelSizePercent : 0}
onResize={handleResize}
>
{leftPanel}
</Panel>
<PanelResizeHandle
style={{
borderRightWidth: '1px',
borderRightStyle: 'solid',
borderRightColor: 'var(--c--contextuals--border--surface--primary)',
width: '1px',
cursor: 'col-resize',
}}
disabled={!isDesktop}
/>
<Panel order={1}>{children}</Panel>
</PanelGroup>
<>
<PanelGroup direction="horizontal">
<Panel
ref={ref}
order={0}
defaultSize={panelSizePercent}
minSize={minPanelSizePercent}
maxSize={maxPanelSizePercent}
onResize={handleResize}
>
{leftPanel}
</Panel>
<PanelResizeHandle
style={{
borderRightWidth: '1px',
borderRightStyle: 'solid',
borderRightColor: 'var(--c--contextuals--border--surface--primary)',
width: '1px',
cursor: 'col-resize',
}}
/>
<Panel order={1}>{children}</Panel>
</PanelGroup>
</>
);
};
@@ -3,7 +3,6 @@ import { create } from 'zustand';
interface LeftPanelState {
isPanelOpen: boolean;
togglePanel: (value?: boolean) => void;
closePanel: () => void;
}
export const useLeftPanelStore = create<LeftPanelState>((set, get) => ({
@@ -16,7 +15,4 @@ export const useLeftPanelStore = create<LeftPanelState>((set, get) => ({
set({ isPanelOpen: sanitizedValue });
},
closePanel: () => {
set({ isPanelOpen: false });
},
}));
@@ -33,21 +33,12 @@ interface IDocsDB extends DBSchema {
type TableName = 'doc-list' | 'doc-item' | 'doc-mutation';
/**
* IndexDB prefers incremental versioning when upgrading the database,
* otherwise it may throw an error, and we need to destroy the database
* and recreate it.
* To calculate the version number, we use the following formula,
* assuring incremental versioning:
* version = major * 1000000 + minor * 1000 + patch
* ex:
* - version 1.2.3 => 1002003
* - version 0.10.15 => 10015
* - version 2.0.0 => 2000000
* @returns number
* IndexDB version must be a integer
* @returns
*/
export const getCurrentVersion = () => {
const [major, minor, patch] = pkg.version.split('.').map(Number);
return major * 1000000 + minor * 1000 + patch;
const getCurrentVersion = () => {
const [major, minor, patch] = pkg.version.split('.');
return parseFloat(`${major}${minor}${patch}`);
};
/**
@@ -1,36 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
vi.mock('@/../package.json', () => ({
default: { version: '0.0.0' },
}));
describe('DocsDB', () => {
afterEach(() => {
vi.clearAllMocks();
vi.resetModules();
});
let previousExpected = 0;
[
{ version: '0.0.1', expected: 1 },
{ version: '0.10.15', expected: 10015 },
{ version: '1.0.0', expected: 1000000 },
{ version: '2.105.3', expected: 2105003 },
{ version: '3.0.0', expected: 3000000 },
{ version: '10.20.30', expected: 10020030 },
].forEach(({ version, expected }) => {
it(`correctly computes version for ${version}`, () => {
vi.doMock('@/../package.json', () => ({
default: { version },
}));
return vi.importActual('../DocsDB').then((module: any) => {
const result = module.getCurrentVersion();
expect(result).toBe(expected);
expect(result).toBeGreaterThan(previousExpected);
previousExpected = result;
});
});
});
});
@@ -151,6 +151,7 @@
"Open document: {{title}}": "Digeriñ ar restr: {{title}}",
"Open root document": "Digeriñ ar restr gwrizienn",
"Open the document options": "Digeriñ dibarzhioù ar restr",
"Open the header menu": "Digeriñ lañser an talbennoù",
"Open the menu of actions for the document: {{title}}": "Digeriñ lañser oberezhioù evit ar restr: {{title}}",
"Organize": "Aozañ",
"Others are editing this document. Unfortunately your network blocks WebSockets, the technology enabling real-time co-editing.": "Tud all a zo oc'h aozañ ar restr-mañ. Siwazh e stank ho kenrouedad ar WebSockets, an deknologiezh a ro an tu da skrivañ a-stroll war an dro.",
@@ -214,6 +215,7 @@
"Too many requests. Please wait 60 seconds.": "Re a c'houlennoù. Gortozit 60 eilenn mar plij.",
"Type a name or email": "Skrivit hoc'h anv pe ur postel",
"Type the name of a document": "Skrivit anv ur restr",
"Unnamed document": "Teul hep titl",
"Unpin": "Dispilhennañ",
"Untitled document": "Restr hep titl",
"Updated": "Hizivaet",
@@ -326,11 +328,14 @@
"Document sections": "Linke Seitenleiste",
"Document title": "Titel des Dokuments",
"Document tree": "Dokumentenbaum",
"Document version viewer": "Dokumentenversion-Betrachter",
"Document visibility": "Sichtbarkeit",
"Documents grid": "Dokumentenübersicht",
"Docx": "Docx",
"Download": "Herunterladen",
"Download anyway": "Trotzdem herunterladen",
"Download your document in a .docx, .odt or .pdf format.": "Dokument als DOCX-, ODT- oder PDF-Datei exportieren.",
"Drag and drop status": "Drag and Drop Status",
"Duplicate": "Duplizieren",
"Edit document emoji": "Dokumenten-Symbol bearbeiten",
"Editing": "Bearbeiten",
@@ -407,8 +412,10 @@
"Open Source": "Open Source",
"Open document {{title}}": "Öffne Dokument: {{title}}",
"Open document: {{title}}": "Öffne Dokument: {{title}}",
"Open invitation actions menu": "Öffne Freigabemenü",
"Open root document": "Öffne Wurzel-Dokument",
"Open the document options": "Öffnen Sie die Dokumentoptionen",
"Open the header menu": "Öffne das Kopfzeilen-Menü",
"Open the menu of actions for the document: {{title}}": "Öffne das Aktionsmenü für das Dokument: {{title}}",
"Open the sharing settings for the document": "Öffne die Freigabeeinstellungen für das Dokument",
"Organize": "Organisieren",
@@ -482,6 +489,7 @@
"Trashbin": "Papierkorb",
"Type a name or email": "Geben Sie einen Namen oder eine E-Mail-Adresse ein",
"Type the name of a document": "Geben Sie den Namen eines Dokuments ein",
"Unnamed document": "Unbenanntes Dokument",
"Unpin": "Lösen",
"Untitled document": "Unbenanntes Dokument",
"Updated": "Aktualisiert",
@@ -627,6 +635,7 @@
"Only invited people can access": "Solo las personas invitadas pueden acceder",
"Open Source": "Código abierto",
"Open the document options": "Abrir las opciones del documento",
"Open the header menu": "Abrir el menú de encabezado",
"Organize": "Organiza",
"Owner": "Propietario",
"PDF": "PDF",
@@ -733,8 +742,6 @@
"Cancel": "Annuler",
"Cancel the deletion": "Annuler la suppression",
"Cancel the download": "Annuler le téléchargement",
"Change role for {{email}}": "Changer le rôle pour {{email}}",
"Change role for {{name}}": "Changer le rôle pour {{name}}",
"Close the access request modal": "Fermer la fenêtre modale de demande d'accès",
"Close the delete modal": "Fermer la fenêtre modale de suppression",
"Close the download modal": "Fermer la fenêtre modale de téléchargement",
@@ -744,7 +751,6 @@
"Collaborate": "Collaborer",
"Collaborate and write in real time, without layout constraints.": "Collaborez et rédigez en temps réel, sans contrainte de mise en page.",
"Collaborative writing, Simplified.": "L'écriture collaborative simplifiée.",
"Comment": "Commentaire",
"Confirm": "Confirmez",
"Connected": "Connecté",
"Contains {{count}} sub-documents_many": "Contient {{count}} sous-documents",
@@ -783,14 +789,14 @@
"Document sections": "Sections du document",
"Document title": "Titre du document",
"Document tree": "Arborescence du document",
"Document viewer": "Visualiseur de document",
"Document version viewer": "Visionneuse de version du document",
"Document visibility": "Visibilité du document",
"Documents grid": "Grille des documents",
"Docx": "Docx",
"Download": "Télécharger",
"Download anyway": "Télécharger malgré tout",
"Download your document in a .docx, .odt, .pdf or .html(zip) format.": "Téléchargez votre document au format .docx, .odt, .pdf ou .html(zip).",
"Drag and drop status": "État du glisser-déposer",
"Download your document in a .docx, .odt or .pdf format.": "Téléchargez votre document au format .docx, .odt ou .pdf.",
"Drag and drop status": "État du glisser-déposer",
"Duplicate": "Dupliquer",
"Edit document emoji": "Modifier l'émoticône du document",
"Editing": "Édition",
@@ -813,9 +819,7 @@
"Failed to duplicate the document...": "Échec de la duplication du document...",
"Flexible export.": "Un export flexible.",
"Format": "Format",
"Go to content": "Voir le contenu",
"Govs ❤️ Open Source.": "Gouvernements ❤️ Open Source.",
"HTML": "HTML",
"History": "Historique",
"Home": "Accueil",
"I understand": "Jai compris",
@@ -826,11 +830,6 @@
"Image: {{title}}": "Image : {{title}}",
"Insufficient access rights to view the document.": "Droits d'accès insuffisants pour voir le document.",
"Invite": "Inviter",
"Invite new members": "Inviter de nouveaux membres",
"Invite {{count}} members_many": "Inviter {{count}} membres",
"Invite {{count}} members_one": "Inviter {{count}} membres",
"Invite {{count}} members_other": "Inviter {{count}} membres",
"Invite {{name}}": "Inviter {{name}}",
"It is the card information about the document.": "Il s'agit de la carte d'information du document.",
"It is the document title": "Il s'agit du titre du document",
"It seems that the page you are looking for does not exist or cannot be displayed correctly.": "Il semble que la page que vous cherchez n'existe pas ou ne puisse pas être affichée correctement.",
@@ -874,8 +873,10 @@
"Open Source": "Open Source",
"Open document {{title}}": "Ouvrir le document {{title}}",
"Open document: {{title}}": "Ouvrir le document : {{title}}",
"Open invitation actions menu": "Menu des actions d'ouverture d'une invitation",
"Open root document": "Ouvrir le document racine",
"Open the document options": "Ouvrir les options du document",
"Open the header menu": "Ouvrir le menu d'en-tête",
"Open the menu of actions for the document: {{title}}": "Ouvrir le menu des actions du document : {{title}}",
"Open the sharing settings for the document": "Ouvrir les paramètres de partage pour le document",
"Organize": "Organiser",
@@ -898,7 +899,6 @@
"Reading": "Lecture seule",
"Remove access": "Supprimer l'accès",
"Remove emoji": "Supprimer les emojis",
"Remove {{name}} from the invite list": "Retirer {{name}} de la liste d'invitation",
"Rename": "Renommer",
"Rephrase": "Reformuler",
"Request access": "Demander l'accès",
@@ -938,7 +938,6 @@
"The document has been deleted.": "Le document a bien été supprimé.",
"The document has been restored.": "Le document a été restauré.",
"The document visibility has been updated.": "La visibilité du document a été mise à jour.",
"The document visibility restored.": "La visibilité du document a été restaurée.",
"The export failed": "Lexportation a échoué",
"The link sharing rules differ from the parent document": "Les règles de partage du lien diffèrent du document parent",
"This document and <strong>any sub-documents</strong> will be placed in the trashbin. You can restore it within {{days}} days.": "Ce document et <strong>tous les sous-documents</strong> seront placés dans la corbeille. Vous pouvez le restaurer dans {{days}} jours.",
@@ -951,6 +950,7 @@
"Trashbin": "Corbeille",
"Type a name or email": "Tapez un nom ou un email",
"Type the name of a document": "Tapez le nom d'un document",
"Unnamed document": "Document sans titre",
"Unpin": "Désépingler",
"Untitled document": "Document sans titre",
"Updated": "Mise à jour",
@@ -980,10 +980,7 @@
"home-content-open-source-part1": "Docs est construit sur <2>Django Rest Framework</2> et <6>Next.js</6>. Nous utilisons également <9>Yjs</9> et <13>BlockNote.js</13>, deux projets que nous sommes fiers de sponsoriser.",
"home-content-open-source-part2": "Vous pouvez facilement auto-héberger Docs (consultez notre <2>documentation</2> d'installation).<br/>Docs utilise une <7>licence</7> (MIT) adaptée à l'innovation et aux entreprises.<br/>Les contributions sont les bienvenues (consultez notre feuille de route <13>ici</13>).",
"home-content-open-source-part3": "Docs est le résultat d'un effort conjoint mené par les gouvernements français 🇫🇷🥖 <1>(DINUM)</1> et allemand 🇩🇪🥨 <5>(ZenDiS)</5>.",
"pdf": "pdf",
"{{action}}, current role: {{role}}": "{{action}}, rôle actuel : {{role}}",
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} a été ajouté à la liste d'invitation. Ajoutez plus de membres ou appuyez sur Tab pour sélectionner le rôle et inviter.",
"{{name}} removed from invite list": "{{name}} a été retiré de la liste d'invitation"
"pdf": "pdf"
}
},
"it": {
@@ -1073,6 +1070,7 @@
"Only invited people can access": "Solo le persone invitate possono accedere",
"Open Source": "Open Source",
"Open the document options": "Apri le opzioni del documento",
"Open the header menu": "Apri il menu dell'intestazione",
"Organize": "Organizza",
"Owner": "Proprietario",
"PDF": "PDF",
@@ -1168,8 +1166,6 @@
"Cancel": "Annuleren",
"Cancel the deletion": "Verwijderen annuleren",
"Cancel the download": "Annuleer de download",
"Change role for {{email}}": "Rol wijzigen voor {{email}}",
"Change role for {{name}}": "Rol wijzigen voor {{name}}",
"Close the access request modal": "Sluit het toegangsverzoek venster",
"Close the delete modal": "Sluit het verwijder venster",
"Close the download modal": "Sluit het downloadvenster",
@@ -1179,7 +1175,6 @@
"Collaborate": "Samenwerken",
"Collaborate and write in real time, without layout constraints.": "Samenwerken en schrijven in realtime, zonder lay-outbeperkingen.",
"Collaborative writing, Simplified.": "Gezamenlijk schrijven, Vereenvoudigd.",
"Comment": "Reactie",
"Confirm": "Bevestigen",
"Connected": "Ingelogd",
"Contains {{count}} sub-documents_many": "Bevat {{count}} subdocumenten",
@@ -1218,14 +1213,14 @@
"Document sections": "Document secties",
"Document title": "Documenttitel",
"Document tree": "Boomstructuur document",
"Document viewer": "Document-viewer",
"Document version viewer": "Document versie viewer",
"Document visibility": "Document toegankelijkheid",
"Documents grid": "Documenten overzicht",
"Docx": "Docx",
"Download": "Download",
"Download anyway": "Download alsnog",
"Download your document in a .docx, .odt, .pdf or .html(zip) format.": "Download uw document in een .docx, .odt, .pdf of .html(zip) formaat.",
"Drag and drop status": "Drag & drop status",
"Download your document in a .docx, .odt or .pdf format.": "Download uw document in een .docx, .odt of .pdf formaat.",
"Drag and drop status": "Drag & drop status",
"Duplicate": "Dupliceer",
"Edit document emoji": "Bewerk document emoji",
"Editing": "Bewerken",
@@ -1248,9 +1243,7 @@
"Failed to duplicate the document...": "Het dupliceren van het document is mislukt...",
"Flexible export.": "Flexibele export.",
"Format": "Formaat",
"Go to content": "Ga naar inhoud",
"Govs ❤️ Open Source.": "Govs ❤️ Open Source.",
"HTML": "HTML",
"History": "Geschiedenis",
"Home": "Startscherm",
"I understand": "Ik begrijp het",
@@ -1261,11 +1254,6 @@
"Image: {{title}}": "Afbeelding: {{title}}",
"Insufficient access rights to view the document.": "Onvoldoende toegangsrechten om het document te bekijken.",
"Invite": "Uitnodigen",
"Invite new members": "Nieuwe leden uitnodigen",
"Invite {{count}} members_many": "Nodig {{count}} leden uit",
"Invite {{count}} members_one": "Nodig {{count}} leden uit",
"Invite {{count}} members_other": "Nodig {{count}} leden uit",
"Invite {{name}}": "Nodig {{name}} uit",
"It is the card information about the document.": "Het is een informatie kaart over het document.",
"It is the document title": "Het is de titel van het document",
"It seems that the page you are looking for does not exist or cannot be displayed correctly.": "Het lijkt erop dat de pagina die u zoekt niet bestaat of niet correct kan worden weergegeven.",
@@ -1309,8 +1297,10 @@
"Open Source": "Open Source",
"Open document {{title}}": "Open document {{title}}",
"Open document: {{title}}": "Open document: {{title}}",
"Open invitation actions menu": "Open uitnodigingsactiemenu",
"Open root document": "Open hoofddocument",
"Open the document options": "Open document opties",
"Open the header menu": "Open het hoofdmenu",
"Open the menu of actions for the document: {{title}}": "Open het menu van acties voor het document: {{title}}",
"Open the sharing settings for the document": "Open de instellingen voor delen van het document",
"Organize": "Organiseer",
@@ -1333,7 +1323,6 @@
"Reading": "Lezen",
"Remove access": "Toegang verwijderen",
"Remove emoji": "Emoji verwijderen",
"Remove {{name}} from the invite list": "Verwijder {{name}} uit de uitnodigingslijst",
"Rename": "Hernoem",
"Rephrase": "Herschrijf",
"Request access": "Toegang aanvragen",
@@ -1373,7 +1362,6 @@
"The document has been deleted.": "Het document is verwijderd",
"The document has been restored.": "Het document is hersteld.",
"The document visibility has been updated.": "De toegang van het document is bijgewerkt.",
"The document visibility restored.": "De zichtbaarheid van het document is hersteld.",
"The export failed": "Het exporteren is mislukt",
"The link sharing rules differ from the parent document": "De regels voor het delen via een link verschillen van het bovenliggende document",
"This document and <strong>any sub-documents</strong> will be placed in the trashbin. You can restore it within {{days}} days.": "Dit document en <strong>alle sub-documenten</strong> zullen in de prullenbak worden geplaatst. Je kunt het binnen {{days}} dagen herstellen.",
@@ -1386,6 +1374,7 @@
"Trashbin": "Prullenbak",
"Type a name or email": "Type een naam of email",
"Type the name of a document": "Vul de naam van een document in",
"Unnamed document": "Naamloos document",
"Unpin": "Losmaken",
"Untitled document": "Naamloos document",
"Updated": "Bijgewerkt",
@@ -1415,10 +1404,7 @@
"home-content-open-source-part1": "Docs is gebouwd op <2>Django Rest Framework</2> en <6>Next.js</6>. We gebruiken ook <9>Yjs</9> en <13>BlockNote.js</13>, twee projecten die we met trots sponsoren.",
"home-content-open-source-part2": "U kunt Docs eenvoudig zelf hosten (zie onze <2>installatiedocumentatie</2>).<br/>Docs gebruikt een <7>licentie</7> (MIT) die is afgestemd op innovatie en ondernemingen.<br/>Bijdragen zijn welkom (zie onze routekaart <13>hier</13>).",
"home-content-open-source-part3": "Docs is het resultaat van een gezamenlijke inspanning geleid door de Franse 🇫🇷🥖 <1>(DINUM)</1> en Duitse 🇩🇪🥨 <5>(ZenDiS)</5> overheden.",
"pdf": "pdf",
"{{action}}, current role: {{role}}": "{{action}}, huidige rol: {{role}}",
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} toegevoegd aan de uitnodigingslijst. Voeg meer deelnemers toe of druk op Tab om de rol en uitnodiging te selecteren.",
"{{name}} removed from invite list": "{{name}} verwijderd uit de uitnodigingslijst"
"pdf": "pdf"
}
},
"pt": { "translation": {} },
@@ -1464,8 +1450,6 @@
"Cancel": "Отмена",
"Cancel the deletion": "Отменить удаление",
"Cancel the download": "Отменить загрузку",
"Change role for {{email}}": "Изменить роль для {{email}}",
"Change role for {{name}}": "Изменить роль для {{name}}",
"Close the access request modal": "Закрыть окно запроса доступа",
"Close the delete modal": "Закрыть окно удаления",
"Close the download modal": "Закрыть окно загрузки",
@@ -1475,7 +1459,6 @@
"Collaborate": "Совместный доступ",
"Collaborate and write in real time, without layout constraints.": "Совместная работа и сохранение в реальном времени, без ограничений.",
"Collaborative writing, Simplified.": "Простой совместный доступ к документам.",
"Comment": "Комментарий",
"Confirm": "Подтвердить",
"Connected": "Подключённая",
"Contains {{count}} sub-documents_many": "Содержит {{count}} вложенных документов",
@@ -1514,14 +1497,14 @@
"Document sections": "Разделы документа",
"Document title": "Заголовок документа",
"Document tree": "Иерархия документа",
"Document viewer": "Просмотрщик документа",
"Document version viewer": "Просмотрщик версий документа",
"Document visibility": "Видимость документа",
"Documents grid": "Сетка документов",
"Docx": "Docx",
"Download": "Загрузить",
"Download anyway": "Всё равно загрузить",
"Download your document in a .docx, .odt, .pdf or .html(zip) format.": "Загрузить документ в формате .docx, .odt, .pdf или .html(zip).",
"Drag and drop status": "Состояние перетаскивания",
"Download your document in a .docx, .odt or .pdf format.": "Загрузить документ в формате .docx, .odt или .pdf.",
"Drag and drop status": "Состояние перетаскивания",
"Duplicate": "Дублировать",
"Edit document emoji": "Изменить документ emoji",
"Editing": "Редактирование",
@@ -1544,9 +1527,7 @@
"Failed to duplicate the document...": "Не удалось дублировать документ...",
"Flexible export.": "Полезные форматы экспорта.",
"Format": "Формат",
"Go to content": "Перейти к содержимому",
"Govs ❤️ Open Source.": "Govs ❤️ Open Source.",
"HTML": "HTML",
"History": "История изменений",
"Home": "Главная",
"I understand": "Я понимаю",
@@ -1557,11 +1538,6 @@
"Image: {{title}}": "Изображение: {{title}}",
"Insufficient access rights to view the document.": "Недостаточно прав доступа для просмотра документа.",
"Invite": "Приглашение",
"Invite new members": "Пригласить новых участников",
"Invite {{count}} members_many": "Пригласить {{count}} участников",
"Invite {{count}} members_one": "Пригласить {{count}} участников",
"Invite {{count}} members_other": "Пригласить {{count}} участников",
"Invite {{name}}": "Пригласить {{name}}",
"It is the card information about the document.": "Это информационная карта документа.",
"It is the document title": "Это заголовок документа",
"It seems that the page you are looking for does not exist or cannot be displayed correctly.": "Похоже, страница, которую вы ищете, не существует или не может быть правильно отображена.",
@@ -1605,8 +1581,10 @@
"Open Source": "Open Source",
"Open document {{title}}": "Открыть документ {{title}}",
"Open document: {{title}}": "Открыть документ: {{title}}",
"Open invitation actions menu": "Меню действий открытия приглашения",
"Open root document": "Открыть корневой документ",
"Open the document options": "Открыть параметры документа",
"Open the header menu": "Открыть меню заголовка",
"Open the menu of actions for the document: {{title}}": "Открыть меню действий для документа: {{title}}",
"Open the sharing settings for the document": "Открыть настройки общего доступа к документу",
"Organize": "Организация данных",
@@ -1629,7 +1607,6 @@
"Reading": "Чтение",
"Remove access": "Отменить доступ",
"Remove emoji": "Убрать эмодзи",
"Remove {{name}} from the invite list": "Удалить {{name}} из списка приглашений",
"Rename": "Переименовать",
"Rephrase": "Переформулировать",
"Request access": "Запрос доступа",
@@ -1669,7 +1646,6 @@
"The document has been deleted.": "Документ удалён.",
"The document has been restored.": "Документ восстановлен.",
"The document visibility has been updated.": "Видимость документа обновлена.",
"The document visibility restored.": "Видимость документа восстановлена.",
"The export failed": "Ошибка при экспорте",
"The link sharing rules differ from the parent document": "Правила обмена ссылками отличаются от правил родительского документа",
"This document and <strong>any sub-documents</strong> will be placed in the trashbin. You can restore it within {{days}} days.": "Этот документ и <strong>все вложенные в него документы</strong> будут помещены в корзину. Вы можете восстановить их в течение {{days}} дн.",
@@ -1682,6 +1658,7 @@
"Trashbin": "Корзина",
"Type a name or email": "Введите имя или e-mail",
"Type the name of a document": "Введите название документа",
"Unnamed document": "Безымянный документ",
"Unpin": "Открепить",
"Untitled document": "Безымянный документ",
"Updated": "Обновлено",
@@ -1708,13 +1685,10 @@
"document": "документ",
"embed": "вложение",
"file": "файл",
"home-content-open-source-part1": "Docs создаётся на основе <2>Django Rest Framework</2> и <6>Next.js</6>. Мы также используем <9>Yjs</9> и <13>BlockNote.js</13> — два проекта, которые мы с гордостью спонсируем.",
"home-content-open-source-part2": "Вы можете легко разместить Docs у себя (см. нашу <2>документацию по установке</2>).<br/>Docs использует <7>лицензию</7> (MIT), подходящую для инноваций и бизнеса.<br/>Мы приветствуем ваши вклады (см. наш план разработки <13>здесь</13>).",
"home-content-open-source-part3": "Docs — это результат совместных усилий правительств Франции 🇫🇷🥖 <1>(DINUM)</1> и Германии 🇩🇪🥨 <5>(ZenDiS)</5>.",
"pdf": "pdf",
"{{action}}, current role: {{role}}": "{{action}}, текущая роль: {{role}}",
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} добавлен(а) в список приглашённых. Добавьте других участников или нажмите Tab, чтобы выбрать роль и пригласить.",
"{{name}} removed from invite list": "{{name}} удален(а) из списка приглашений"
"home-content-open-source-part1": "текст ещё не готов",
"home-content-open-source-part2": "текст ещё не готов",
"home-content-open-source-part3": "текст ещё не готов",
"pdf": "pdf"
}
},
"sl": {
@@ -1863,8 +1837,6 @@
"Cancel": "Скасувати",
"Cancel the deletion": "Скасувати видалення",
"Cancel the download": "Скасувати завантаження",
"Change role for {{email}}": "Змінити роль для {{email}}",
"Change role for {{name}}": "Змінити роль для {{name}}",
"Close the access request modal": "Закрити вікно запиту доступу",
"Close the delete modal": "Закрити вікно видалення",
"Close the download modal": "Закрити вікно завантаження",
@@ -1874,7 +1846,6 @@
"Collaborate": "Спільна робота",
"Collaborate and write in real time, without layout constraints.": "Сумісна робота і збереження результатів у реальному часі, без обмеження макета.",
"Collaborative writing, Simplified.": "Простий спільний доступ до документів.",
"Comment": "Коментар",
"Confirm": "Підтвердити",
"Connected": "Під'єднане",
"Contains {{count}} sub-documents_many": "Містить {{count}} вкладених документів",
@@ -1913,14 +1884,14 @@
"Document sections": "Розділи документу",
"Document title": "Назва документа",
"Document tree": "Дерево документа",
"Document viewer": "Переглядач документа",
"Document version viewer": "Переглядач версії документа",
"Document visibility": "Видимість документа",
"Documents grid": "Сітка документів",
"Docx": "Docx",
"Download": "Завантажити",
"Download anyway": "Все одно завантажити",
"Download your document in a .docx, .odt, .pdf or .html(zip) format.": "Завантажити документ у форматі .docx, .odt, .pdf або .html(zip).",
"Drag and drop status": "Стан перетягування",
"Download your document in a .docx, .odt or .pdf format.": "Завантажити документ у форматі .docx, .odt або .pdf.",
"Drag and drop status": "Стан перетягування",
"Duplicate": "Дублювати",
"Edit document emoji": "Редагувати документ емодзі",
"Editing": "Редагування",
@@ -1943,9 +1914,7 @@
"Failed to duplicate the document...": "Не вдалося дублювати документ...",
"Flexible export.": "Гнучкий експорт.",
"Format": "Формат",
"Go to content": "Перейти до вмісту",
"Govs ❤️ Open Source.": "Govs ❤️ Open Source.",
"HTML": "HTML",
"History": "Історія",
"Home": "Головна",
"I understand": "Я розумію",
@@ -1956,11 +1925,6 @@
"Image: {{title}}": "Зображення: {{title}}",
"Insufficient access rights to view the document.": "Недостатньо прав для перегляду документа.",
"Invite": "Запрошення",
"Invite new members": "Запросити нових учасників",
"Invite {{count}} members_many": "Запросити {{count}} учасників",
"Invite {{count}} members_one": "Запросити {{count}} учасників",
"Invite {{count}} members_other": "Запросити {{count}} учасників",
"Invite {{name}}": "Запросити {{name}}",
"It is the card information about the document.": "Це інформаційна картка документу.",
"It is the document title": "Це заголовок документа",
"It seems that the page you are looking for does not exist or cannot be displayed correctly.": "Схоже, що сторінка, яку ви шукаєте, не існує або не може бути показаною правильно.",
@@ -2004,8 +1968,10 @@
"Open Source": "Open Source",
"Open document {{title}}": "Відкрити документ {{title}}",
"Open document: {{title}}": "Відкрити документ: {{title}}",
"Open invitation actions menu": "Меню дій відкриття запрошення",
"Open root document": "Відкрити кореневий документ",
"Open the document options": "Відкрити параметри документа",
"Open the header menu": "Відкрити меню заголовка",
"Open the menu of actions for the document: {{title}}": "Відкрити меню дій для документа: {{title}}",
"Open the sharing settings for the document": "Відкрити параметри спільного доступу для документа",
"Organize": "Організуйте",
@@ -2028,7 +1994,6 @@
"Reading": "Читання",
"Remove access": "Вилучити доступ",
"Remove emoji": "Прибрати емодзі",
"Remove {{name}} from the invite list": "Видалити {{name}} зі списку запрошень",
"Rename": "Перейменувати",
"Rephrase": "Перефразувати",
"Request access": "Запит доступу",
@@ -2068,7 +2033,6 @@
"The document has been deleted.": "Документ був видалений.",
"The document has been restored.": "Документ був відновлений.",
"The document visibility has been updated.": "Видимість документа оновлено.",
"The document visibility restored.": "Видимість документа відновлено.",
"The export failed": "Не вдалося виконати експорт",
"The link sharing rules differ from the parent document": "Правила обміну посиланнями відрізняються від батьківського документа",
"This document and <strong>any sub-documents</strong> will be placed in the trashbin. You can restore it within {{days}} days.": "Цей документ та <strong>всі вкладені документи</strong> будуть переміщені до кошика. Ви можете відновити їх протягом {{days}} дн.",
@@ -2081,6 +2045,7 @@
"Trashbin": "Кошик",
"Type a name or email": "Введіть ім'я або електронну адресу",
"Type the name of a document": "Введіть назву документа",
"Unnamed document": "Документ без назви",
"Unpin": "Відкріпити",
"Untitled document": "Документ без назви",
"Updated": "Оновлено",
@@ -2107,13 +2072,10 @@
"document": "документ",
"embed": "вкладення",
"file": "файл",
"home-content-open-source-part1": "Docs створюється на основі <2>Django Rest Framework</2> і <6>Next.js</6>. Ми також використовуємо <9>Yjs</9> і <13>BlockNote.js</13>, два проекти, які ми з гордістю спонсоруємо.",
"home-content-open-source-part2": "Ви можете легко самостійно розмістити Docs (див. нашу <2>документацію з встановлення</2>).<br/>Docs використовує <7>ліцензію</7> (MIT), що підходить для інновацій та бізнесу.<br/>Внески вітаються (див. наш план розробки <13>тут</13>).",
"home-content-open-source-part3": "Docs є результатом спільних зусиль, очолюваних урядами Франції 🇫🇷🥖 <1>(DINUM)</1> та Німеччини 🇩🇪🥨 <5>(ZenDiS)</5>.",
"pdf": "pdf",
"{{action}}, current role: {{role}}": "{{action}}, поточна роль: {{role}}",
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} додано до списку запрошень. Додайте більше учасників або натисніть Tab, щоб вибрати роль та надіслати запрошення.",
"{{name}} removed from invite list": "{{name}} видалено зі списку запрошень"
"home-content-open-source-part1": "текст ще не готовий",
"home-content-open-source-part2": "текст ще не готовий",
"home-content-open-source-part3": "текст ще не готовий",
"pdf": "pdf"
}
},
"zh": {
@@ -2228,6 +2190,7 @@
"Open Source": "开放源代码",
"Open document {{title}}": "打开文档{{title}}",
"Open the document options": "打开文档选项",
"Open the header menu": "打开标题菜单",
"Organize": "组织",
"Owner": "所有者",
"PDF": "PDF",
@@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Header } from '@/features/header';
import { HEADER_HEIGHT } from '@/features/header/conf';
import { LeftPanel, ResizableLeftPanel } from '@/features/left-panel';
@@ -53,57 +52,15 @@ export function MainLayoutContent({
enableResizablePanel = false,
}: PropsWithChildren<MainLayoutContentProps>) {
const { isDesktop } = useResponsiveStore();
if (enableResizablePanel) {
return (
<ResizableLeftPanel leftPanel={<LeftPanel />}>
<MainContent backgroundColor={backgroundColor}>{children}</MainContent>
</ResizableLeftPanel>
);
}
if (!isDesktop) {
return (
<>
<LeftPanel />
<MainContent backgroundColor={backgroundColor}>{children}</MainContent>
</>
);
}
return (
<>
<Box
$css={css`
width: 300px;
border-right: 1px solid
var(--c--contextuals--border--surface--primary);
`}
>
<LeftPanel />
</Box>
<MainContent backgroundColor={backgroundColor}>{children}</MainContent>
</>
);
}
const MainContent = ({
children,
backgroundColor,
}: PropsWithChildren<MainLayoutContentProps>) => {
const { isDesktop } = useResponsiveStore();
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const currentBackgroundColor = !isDesktop ? 'white' : backgroundColor;
return (
const mainContent = (
<Box
as="main"
role="main"
aria-label={t('Main content')}
id={MAIN_LAYOUT_ID}
tabIndex={-1}
$align="center"
$flex={1}
$width="100%"
@@ -120,10 +77,6 @@ const MainContent = ({
$css={css`
overflow-y: auto;
overflow-x: clip;
&:focus {
outline: 3px solid ${colorsTokens['brand-400']};
outline-offset: -3px;
}
`}
>
<Skeleton>
@@ -132,4 +85,36 @@ const MainContent = ({
{children}
</Box>
);
};
if (!isDesktop) {
return (
<>
<LeftPanel />
{mainContent}
</>
);
}
if (enableResizablePanel) {
return (
<ResizableLeftPanel leftPanel={<LeftPanel />}>
{mainContent}
</ResizableLeftPanel>
);
}
return (
<>
<Box
$css={css`
width: 300px;
border-right: 1px solid
var(--c--contextuals--border--surface--primary);
`}
>
<LeftPanel />
</Box>
{mainContent}
</>
);
}
@@ -1,6 +1,5 @@
import { PropsWithChildren } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box } from '@/components';
import { Footer } from '@/features/footer';
@@ -8,8 +7,6 @@ import { HEADER_HEIGHT, Header } from '@/features/header';
import { LeftPanel } from '@/features/left-panel';
import { useResponsiveStore } from '@/stores';
import { MAIN_LAYOUT_ID } from './conf';
interface PageLayoutProps {
withFooter?: boolean;
}
@@ -30,19 +27,8 @@ export function PageLayout({
<Box
as="main"
role="main"
id={MAIN_LAYOUT_ID}
tabIndex={-1}
$width="100%"
$css={css`
flex-grow: 1;
&:focus {
outline: 3px solid var(--c--theme--colors--primary-600);
outline-offset: -3px;
}
&:focus:not(:focus-visible) {
outline: none;
}
`}
$css="flex-grow:1;"
aria-label={t('Main content')}
>
{!isDesktop && <LeftPanel />}
@@ -1,114 +0,0 @@
import { captureException } from '@sentry/nextjs';
import Head from 'next/head';
import { useSearchParams } from 'next/navigation';
import { useRouter } from 'next/router';
import { ReactElement, useCallback, useEffect } from 'react';
import { Loading } from '@/components';
import { LOGIN_URL, setAuthUrl, useAuth } from '@/features/auth';
import {
LinkReach,
LinkRole,
useCreateDoc,
} from '@/features/docs/doc-management';
import { useUpdateDocLink } from '@/features/docs/doc-share/api/useUpdateDocLink';
import { useSkeletonStore } from '@/features/skeletons';
import { MainLayout } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => {
const { setIsSkeletonVisible } = useSkeletonStore();
const router = useRouter();
const searchParams = useSearchParams();
const linkReach = searchParams.get('link-reach');
const linkRole = searchParams.get('link-role');
const title = searchParams.get('title');
const { authenticated } = useAuth();
const { mutateAsync: createDocAsync, data: doc } = useCreateDoc();
const { mutateAsync: updateDocLinkAsync } = useUpdateDocLink();
const redirectToDoc = useCallback(
(docId: string) => {
void router.push(`/docs/${docId}`);
},
[router],
);
useEffect(() => {
setIsSkeletonVisible(true);
}, [setIsSkeletonVisible]);
useEffect(() => {
if (doc) {
return;
}
if (!authenticated) {
setAuthUrl();
window.location.replace(LOGIN_URL);
return;
}
createDocAsync({
title: title || undefined,
})
.then((createdDoc) => {
if ((linkReach && linkRole) || linkReach) {
updateDocLinkAsync({
id: createdDoc.id,
link_reach: linkReach as LinkReach,
link_role: (linkRole as LinkRole | undefined) || undefined,
})
.catch((error) => {
captureException(error, {
extra: {
docId: createdDoc.id,
linkReach,
linkRole,
},
});
})
.finally(() => {
redirectToDoc(createdDoc.id);
});
return;
}
redirectToDoc(createdDoc.id);
})
.catch((error) => {
captureException(error, {
extra: {
title,
},
});
});
}, [
authenticated,
createDocAsync,
doc,
linkReach,
linkRole,
redirectToDoc,
title,
updateDocLinkAsync,
]);
return <Loading />;
};
Page.getLayout = function getLayout(page: ReactElement) {
return (
<>
<Head>
<meta name="robots" content="noindex" />
</Head>
<MainLayout enableResizablePanel={false}>{page}</MainLayout>
</>
);
};
export default Page;
+12 -6
View File
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "4.1.0",
"version": "3.10.0",
"private": true,
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
@@ -31,12 +31,18 @@
"server:test": "yarn COLLABORATION_SERVER run test"
},
"resolutions": {
"@tiptap/extensions": "3.11.0",
"@types/node": "24.10.1",
"@types/react": "19.2.6",
"@types/react-dom": "19.2.3",
"@tiptap/extensions": "3.10.2",
"@types/node": "24.10.0",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"docx": "9.5.0",
"eslint": "9.39.1",
"eslint": "9.39.0",
"prosemirror-state": "1.4.4",
"prosemirror-view": "1.41.3",
"prosemirror-model": "1.25.4",
"prosemirror-transform": "1.10.4",
"react": "19.2.0",
"react-dom": "19.2.0",
"typescript": "5.9.3",
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-docs",
"version": "4.1.0",
"version": "3.10.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
@@ -17,20 +17,20 @@
"eslint": ">=9.0.0"
},
"dependencies": {
"@next/eslint-plugin-next": "16.0.3",
"@next/eslint-plugin-next": "15.5.4",
"@tanstack/eslint-plugin-query": "5.91.2",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"@vitest/eslint-plugin": "1.4.3",
"eslint-config-next": "16.0.3",
"@typescript-eslint/eslint-plugin": "*",
"@typescript-eslint/parser": "*",
"@vitest/eslint-plugin": "1.4.0",
"eslint-config-next": "15.5.4",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jest": "29.2.1",
"eslint-plugin-jest": "29.0.1",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-playwright": "2.3.0",
"eslint-plugin-prettier": "5.5.4",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-testing-library": "7.13.5",
"eslint-plugin-testing-library": "7.13.3",
"prettier": "3.6.2"
},
"packageManager": "yarn@1.22.22"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "packages-i18n",
"version": "4.1.0",
"version": "3.10.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "server-y-provider",
"version": "4.1.0",
"version": "3.10.0",
"description": "Y.js provider for docs",
"repository": "https://github.com/suitenumerique/docs",
"license": "MIT",
@@ -18,10 +18,10 @@
"dependencies": {
"@blocknote/server-util": "0.42.3",
"@hocuspocus/server": "3.4.0",
"@sentry/node": "10.26.0",
"@sentry/profiling-node": "10.26.0",
"@sentry/node": "10.22.0",
"@sentry/profiling-node": "10.22.0",
"@tiptap/extensions": "*",
"axios": "1.13.2",
"axios": "1.13.1",
"cors": "2.8.5",
"express": "5.1.0",
"express-ws": "5.0.2",
@@ -40,12 +40,12 @@
"@types/ws": "8.18.1",
"cross-env": "10.1.0",
"eslint-plugin-docs": "*",
"nodemon": "3.1.11",
"nodemon": "3.1.10",
"supertest": "7.1.4",
"ts-node": "10.9.2",
"tsc-alias": "1.8.16",
"typescript": "*",
"vitest": "4.0.13",
"vitest": "4.0.6",
"vitest-mock-extended": "3.1.0",
"ws": "8.18.3"
},
+509 -802
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,10 +1,10 @@
environments:
dev:
values:
- version: 4.1.0
- version: 3.10.0
feature:
values:
- version: 4.1.0
- version: 3.10.0
feature: ci
domain: example.com
imageTag: demo
+1 -1
View File
@@ -1,5 +1,5 @@
apiVersion: v2
type: application
name: docs
version: 4.1.0
version: 3.10.0
appVersion: latest
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "mail_mjml",
"version": "4.1.0",
"version": "3.10.0",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {
"@html-to/text-cli": "0.5.4",
"mjml": "4.17.1"
"mjml": "4.16.1"
},
"private": true,
"scripts": {
+193 -193
View File
@@ -581,46 +581,46 @@ minimatch@^9.0.3, minimatch@^9.0.4:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
mjml-accordion@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-accordion/-/mjml-accordion-4.17.1.tgz#6ee3c016ea78a5a0ed29f3ec28c17d181028994f"
integrity sha512-xl9oUbMp8aju6b1OZYqv3orE287ofGNEv09h2mFmzRTJxug7nJBFXs2I9v7dUVuWIBRk940PjnIVSuW+9bPvCA==
mjml-accordion@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-accordion/-/mjml-accordion-4.16.1.tgz#c7a26bc80d15164bfadbd9334dfe6e3e4a5abe07"
integrity sha512-WqBaDmov7uI15dDVZ5UK6ngNwVhhXawW+xlCVbjs21wmskoG4lXc1j+28trODqGELk3BcQOqjO8Ee6Ytijp4PA==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-body@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-body/-/mjml-body-4.17.1.tgz#a4e4d2ee34abfbb45b74999ee49356b35830a0dc"
integrity sha512-EfvVVfutARRjJ1jsOxxf2DY/ufqWswv9JKjbwu/Fu8h4havAcdmw2BDmX3vwXEzatqpL1l//YWOKlqUe9ZEs+A==
mjml-body@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-body/-/mjml-body-4.16.1.tgz#ad7f7c04a8ae018617e0db63c51e472d0bddf4ba"
integrity sha512-A19pJ2HXqc7A5pKc8Il/d1cH5yyO2Jltwit3eUKDrZ/fBfYxVWZVPNuMooqt6QyC26i+xhhVbVsRNTwL1Aclqg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-button@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-button/-/mjml-button-4.17.1.tgz#1cbdf444802690329ea59ea524d088607666fa5f"
integrity sha512-A9xQwgccPzrwr11izorBsA92THpkrviWkCwlYMxL9V3wgt5YJYDrt4r023HCveqN7b6iTkvqkXeDoIPX/kEDDQ==
mjml-button@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-button/-/mjml-button-4.16.1.tgz#a2e69306dd01f219c145f18786d353a73b767e92"
integrity sha512-z2YsSEDHU4ubPMLAJhgopq3lnftjRXURmG8A+K/QIH4Js6xHIuSNzCgVbBl13/rB1hwc2RxUP839JoLt3M1FRg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-carousel@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-carousel/-/mjml-carousel-4.17.1.tgz#c395842741f55420dd7a3c08a76cd3d3a73e49ba"
integrity sha512-pWo/aIgRL3XduckOBVEvbpph3vR4f9maRrbJ8Jfu4NVI6Ws3PQ6wt7HPXHmJlzJlK0gTiAF9f4+I076RVHPG7A==
mjml-carousel@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-carousel/-/mjml-carousel-4.16.1.tgz#d5c461e21012935623fc493cc027b491f5e9f8de"
integrity sha512-Xna+lSHJGMiPxDG3kvcK3OfEDQbkgyXEz0XebN7zpLDs1Mo4IXe8qI7fFnDASckwC14gmdPwh/YcLlQ4nkzwrQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-cli@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-cli/-/mjml-cli-4.17.1.tgz#0bc278762bc2391b6c61d16784156ee429cd104f"
integrity sha512-1cMWP+yDDBUIjDYnjiKhIPW3NYJrt/W5rqOiB3zOTZQBT722Uh055S3BoLUikKxc+1sQPww4d9dH371zX2HaXA==
mjml-cli@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-cli/-/mjml-cli-4.16.1.tgz#61f1185701e51c5773bbce727307a1709e5f1c6d"
integrity sha512-1dTGWOKucdNImjLzDZfz1+aWjjZW4nRW5pNUMOdcIhgGpygYGj1X4/R8uhrC61CGQXusUrHyojQNVks/aBm9hQ==
dependencies:
"@babel/runtime" "^7.28.4"
chokidar "^3.0.0"
@@ -629,25 +629,25 @@ mjml-cli@4.17.1:
js-beautify "^1.6.14"
lodash "^4.17.21"
minimatch "^9.0.3"
mjml-core "4.17.1"
mjml-migrate "4.17.1"
mjml-parser-xml "4.17.1"
mjml-validator "4.17.1"
mjml-core "4.16.1"
mjml-migrate "4.16.1"
mjml-parser-xml "4.16.1"
mjml-validator "4.16.1"
yargs "^17.7.2"
mjml-column@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-column/-/mjml-column-4.17.1.tgz#3aa64972bb827fd96b596c71ce583e9ef720aa41"
integrity sha512-S+oNZaWFP1/TCEgVwVcwqYIyHwwVZWSKLKj4fcWIMUCaHWKuojYrexOGfULDAwTjYEDhZaRDrrq96ulF12wJeQ==
mjml-column@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-column/-/mjml-column-4.16.1.tgz#10265232ca524b09e8ddd79d3d075642af231d87"
integrity sha512-olScfxGEC0hp3VGzJUn7/znu7g9QlU1PsVRNL7yGKIUiZM/foysYimErBq2CfkF+VkEA9ZlMMeRLGNFEW7H3qQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-core@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-core/-/mjml-core-4.17.1.tgz#a303e8e4f94f5124284843bebd70c1d91a9085a6"
integrity sha512-u2aHbBxFA2uJdS6T1A1ZTGYryPNebHMByRrMPCbe5W8Os+sGiC5gKLhZgjavZteKiMS+09swkvfneNLGYwzBKg==
mjml-core@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-core/-/mjml-core-4.16.1.tgz#f59883d771fb65de0daa4d639361a0526f715ea0"
integrity sha512-sT7VbcUyd3m68tyZvK/cYbZIn7J3E4A+AFtAxI2bxj4Mz8QPjpz6BUGXkRJcYYxvNYVA+2rBFCFRXe5ErsVMVg==
dependencies:
"@babel/runtime" "^7.28.4"
cheerio "1.0.0-rc.12"
@@ -656,263 +656,263 @@ mjml-core@4.17.1:
js-beautify "^1.6.14"
juice "^10.0.0"
lodash "^4.17.21"
mjml-migrate "4.17.1"
mjml-parser-xml "4.17.1"
mjml-validator "4.17.1"
mjml-migrate "4.16.1"
mjml-parser-xml "4.16.1"
mjml-validator "4.16.1"
mjml-divider@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-divider/-/mjml-divider-4.17.1.tgz#1219c4f25d9e6f963de9438a5adf85064c7e9f01"
integrity sha512-KUWvcx1cIDwkN/gDuY37e9Vv+0U5U+xOVOfXRGloSnapYcP0IvmLtLTJeBwvGhwoN30wBiHDGwO8p/7B6CyxqQ==
mjml-divider@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-divider/-/mjml-divider-4.16.1.tgz#d1b57b3a0025ebb4613704b54fffad75379c09be"
integrity sha512-KNqk0V3VRXU0f3yoziFUl1TboeRJakm+7B7NmGRUj13AJrEkUela2Y4/u0wPk8GMC8Qd25JTEdbVHlImfyNIQQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-group@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-group/-/mjml-group-4.17.1.tgz#38b4da7e67151c2c8c84378ef176432a135e11e2"
integrity sha512-0vOcLm7l4ptLM5rqC6DhCafxIw5+WUrSYLcdUSJxO3AZMGJMxU7fkWeWGowE+PQdgqh6ee1/4RYc2qJDWtHW5A==
mjml-group@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-group/-/mjml-group-4.16.1.tgz#ded668a832dff437bb237878e7c4ead53204aa12"
integrity sha512-pjNEpS9iTh0LGeYZXhfhI27pwFFTAiqx+5Q420P4ebLbeT5Vsmr8TrcaB/gEPNn/eLrhzH/IssvnFOh5Zlmrlg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-attributes@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-attributes/-/mjml-head-attributes-4.17.1.tgz#198ca06a6a9b9148af1b6508aab38a41c2cf9b30"
integrity sha512-p+g33eI4xyHb9Yv28zIXnNdsXQYvoGex/GvoGumwyxu6O8OOvPk1mIV87SjDISQHosJJMcTiZVd/RfBlwnZpGA==
mjml-head-attributes@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-attributes/-/mjml-head-attributes-4.16.1.tgz#016961989da30e7bf8e076e82bc5f52ca67950ec"
integrity sha512-JHFpSlQLJomQwKrdptXTdAfpo3u3bSezM/4JfkCi53MBmxNozWzQ/b8lX3fnsTSf9oywkEEGZD44M2emnTWHug==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-breakpoint@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-breakpoint/-/mjml-head-breakpoint-4.17.1.tgz#4fafa9cc176c052a2796d904ff84172290826adb"
integrity sha512-vjsNAgdLnwqmkVlIENbH6odK6ZARiNQvsm+1o8CLo9ymw82WhIEbOnAeCfoddumZ6h2ywbZuBZzS23jJi13MUQ==
mjml-head-breakpoint@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-breakpoint/-/mjml-head-breakpoint-4.16.1.tgz#654060439f00e975052387a83333a54caf5e29b6"
integrity sha512-b4C/bZCMV1k/br2Dmqfp/mhYPkcZpBQdMpAOAaI8na7HmdS4rE/seJUfeCUr7fy/7BvbmsN2iAAttP54C4bn/A==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-font@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-font/-/mjml-head-font-4.17.1.tgz#0984862ebae07fbf2427e61c3d997ac155d56bd2"
integrity sha512-Xeih/vqocR1BoBLbh8Sn67kNkfLsyHeZ7Z/3nyNz7TriZ//TGAR/PGTFFghQlXyX1BCtSx/eFoxMkKKswLYReA==
mjml-head-font@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-font/-/mjml-head-font-4.16.1.tgz#77551b40d3e714b63188d24dede83a4cf10bff60"
integrity sha512-Bw3s5HSeWX3wVq4EJnBS8OOgw/RP4zO0pbidv7T+VqKunUEuUwCEaLZyuTyhBqJ61QiPOehBBGBDGwYyVaJGVg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-html-attributes@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-html-attributes/-/mjml-head-html-attributes-4.17.1.tgz#cb4b74210257d9bb7ba9b23ca5cc409516456b8b"
integrity sha512-O7YzEAFtSELB7wVYV808g6JcxXrzHk5glDdzzCEhDR4bjPHewSUpkrYOqvt0BdfdFsvqH4zm4vsJImUMW692HQ==
mjml-head-html-attributes@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-html-attributes/-/mjml-head-html-attributes-4.16.1.tgz#e58c4c62d7727ea705fe10d412b9108520b547c9"
integrity sha512-GtT0vb6rb/dyrdPzlMQTtMjCwUyXINAHcUR+IGi1NTx8xoHWUjmWPQ/v95IhgelsuQgynuLWVPundfsPn8/PTQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-preview@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-preview/-/mjml-head-preview-4.17.1.tgz#63e52bae35b43bdc43da838a0c3e85f3131439ef"
integrity sha512-XL+8N9yrADJSw4gX9lvDcp31ghGy8WavenVO8UhxPyhLu/sMJ9lFXLbTB4z5JU1z4t/HPEp/GtgMGxAbr+QrQQ==
mjml-head-preview@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-preview/-/mjml-head-preview-4.16.1.tgz#0e72ffe23f33c9e54279291f5cedbe9f9527ea33"
integrity sha512-5iDM5ZO0JWgucIFJG202kGKVQQWpn1bOrySIIp2fQn1hCXQaefAPYduxu7xDRtnHeSAw623IxxKzZutOB8PMSg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-style@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-style/-/mjml-head-style-4.17.1.tgz#29a1cb440ae36e0029af6cf1adc9eeb181ada09f"
integrity sha512-YTjtqZAG0hD0aYwk02/8hS1W+T4nDUhVCBFmcxL/aTSrRbJQew0dSVtCvqNpAsbIJCUg/mUxx6pKKzRPdN+FtA==
mjml-head-style@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-style/-/mjml-head-style-4.16.1.tgz#b6d9a22ed423ace6e1af81e2a457ed9852953fee"
integrity sha512-P6NnbG3+y1Ow457jTifI9FIrpkVSxEHTkcnDXRtq3fA5UR7BZf3dkrWQvsXelm6DYCSGUY0eVuynPPOj71zetQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head-title@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head-title/-/mjml-head-title-4.17.1.tgz#def969e9ac58e975bc686fc95273ddc2d23925f1"
integrity sha512-cUO4b7tDuX1BLu6XYnPgG40o3pBUCkT+Yzu5DGsvRxvCWougJFN68ocF6zcc7OOanmLgBYlJevQKUyT6W5Rp0g==
mjml-head-title@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head-title/-/mjml-head-title-4.16.1.tgz#6c73f17f4f5eedb40988e348bf284dc7c0b1727b"
integrity sha512-s7X9XkIu46xKXvjlZBGkpfsTcgVqpiQjAm0OrHRV9E5TLaICoojmNqEz5CTvvlTz7olGoskI1gzJlnhKxPmkXQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-head@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-head/-/mjml-head-4.17.1.tgz#516d6039e103424d05ec5b55202b79a2b9a440f4"
integrity sha512-+DBJ6UvkpYkKJGJKqo8luucDGbg9+rQZKytl/4VOGTE8bmbrKFixY3lkfmBrSkQ7/t6L4dDVSXywl6H91JsL+g==
mjml-head@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-head/-/mjml-head-4.16.1.tgz#57e8190df9736e1f5bd9257159b4c62acbb232aa"
integrity sha512-R/YA6wxnUZHknJ2H7TT6G6aXgNY7B3bZrAbJQ4I1rV/l0zXL9kfjz2EpkPfT0KHzS1cS2J1pK/5cn9/KHvHA2Q==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-hero@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-hero/-/mjml-hero-4.17.1.tgz#d4f7ad9e29cb11107843f68a906f9389acb6a230"
integrity sha512-WDmNVJ4+xHLrkYOrGrq23hUYDVG3iFSyk/vIC/KlcG5Kebu5vVWbe6n3ZEucatPuYn/EUVV1ofIJM6dnXXfkGQ==
mjml-hero@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-hero/-/mjml-hero-4.16.1.tgz#a872d3e7a30b0364a8bd074276ecd9e253d4ec8b"
integrity sha512-1q6hsG7l2hgdJeNjSNXVPkvvSvX5eJR5cBvIkSbIWqT297B1WIxwcT65Nvfr1FpkEALeswT4GZPSfvTuXyN8hg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-image@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-image/-/mjml-image-4.17.1.tgz#9a427d719caf664b3a60b8f6cfb10e91dabdcb5d"
integrity sha512-ZIFXmP2Fb77vvX8SBQYbrAPPvkqx5GqJ7AqVWteQk4iz6nJf8GspZiotWyL4LvgZzf0B81aQCB11y7+RvAfVvw==
mjml-image@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-image/-/mjml-image-4.16.1.tgz#04cc0525913304d6dd14621a5cd244cadcab78d7"
integrity sha512-snTULRoskjMNPxajSFIp4qA/EjZ56N0VXsAfDQ9ZTXZs0Mo3vy2N81JDGNVRmKkAJyPEwN77zrAHbic0Ludm1w==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-migrate@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-migrate/-/mjml-migrate-4.17.1.tgz#d50dd85f5f964d2e860741e657da03078209eeb4"
integrity sha512-Rb66BdvuV8fGYdQJzvLK0naWGI8G9smzm1OJDjdhcCrQU3BfTW/BiTS9FP5G0W73kFJe//vlHCDZ3uBIr6REAA==
mjml-migrate@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-migrate/-/mjml-migrate-4.16.1.tgz#500bd6757a06085582995385e3e35fa7f3ac2adf"
integrity sha512-4SuaFWyu1Hg948ODHz1gF5oXrhgRI1LgtWMRE+Aoz4F6SSA7kL78iJqEVvouOHCpcxQStDdiZo8/KeuQ1llEAw==
dependencies:
"@babel/runtime" "^7.28.4"
js-beautify "^1.6.14"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-parser-xml "4.17.1"
mjml-core "4.16.1"
mjml-parser-xml "4.16.1"
yargs "^17.7.2"
mjml-navbar@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-navbar/-/mjml-navbar-4.17.1.tgz#215e1dc8546dc9658af59770113ac0f9b3eae47e"
integrity sha512-SWtovALlb+tM2lu2stlsKItrM/Tc/YxWiCm+UtLuOvkBmouBX/vASufaFab3VPAq/pGJKF9nFGX2eWoJCGA4rA==
mjml-navbar@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-navbar/-/mjml-navbar-4.16.1.tgz#b25a211265bebb22204b2196f3a8b31f8bf8ba61"
integrity sha512-lLlTOU3pVvlnmIJ/oHbyuyV8YZ99mnpRvX+1ieIInFElOchEBLoq1Mj+RRfaf2EV/q3MCHPyYUZbDITKtqdMVg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-parser-xml@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-parser-xml/-/mjml-parser-xml-4.17.1.tgz#7b497c20bf1bb343fe49e2c79b24aa5ae926a4a8"
integrity sha512-8cc1+cI1+ymeKmiaioZMaIzg8K9SmCErr0WOdS0n90pnt5eLqGQEh3RQJv7VoucO5aoJXgAnCSGeCstVXvZykg==
mjml-parser-xml@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-parser-xml/-/mjml-parser-xml-4.16.1.tgz#e11c42752dd0664bd235b71b0dd68925f4fd0203"
integrity sha512-QsHnPgVGgzcLX82wn1uP53X9pIUP3H6bJMad9R1v2F1A9rhaKK+wctxvXWBp4+XXJOv3SqpE5GDBEQPWNs5IgQ==
dependencies:
"@babel/runtime" "^7.28.4"
detect-node "2.1.0"
htmlparser2 "^9.1.0"
lodash "^4.17.21"
mjml-preset-core@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-preset-core/-/mjml-preset-core-4.17.1.tgz#7826184b7ca57383e47597c1593e492a9a5b4102"
integrity sha512-cFfelKeRJNG+WZv+kGWjjHrQam5PiHH8JaC3vvjl1eEwLcR2nbaYArlnLTIzgG+M3+cBlIl0Ru3Say5ZqWAcxw==
mjml-preset-core@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-preset-core/-/mjml-preset-core-4.16.1.tgz#cec5d896186ba3164909f8c3efd39178295128d9"
integrity sha512-D7ogih4k31xCvj2u5cATF8r6Z1yTbjMnR+rs19fZ35gXYhl0B8g4cARwXVCu0WcU4vs/3adInAZ8c54NL5ruWA==
dependencies:
"@babel/runtime" "^7.28.4"
mjml-accordion "4.17.1"
mjml-body "4.17.1"
mjml-button "4.17.1"
mjml-carousel "4.17.1"
mjml-column "4.17.1"
mjml-divider "4.17.1"
mjml-group "4.17.1"
mjml-head "4.17.1"
mjml-head-attributes "4.17.1"
mjml-head-breakpoint "4.17.1"
mjml-head-font "4.17.1"
mjml-head-html-attributes "4.17.1"
mjml-head-preview "4.17.1"
mjml-head-style "4.17.1"
mjml-head-title "4.17.1"
mjml-hero "4.17.1"
mjml-image "4.17.1"
mjml-navbar "4.17.1"
mjml-raw "4.17.1"
mjml-section "4.17.1"
mjml-social "4.17.1"
mjml-spacer "4.17.1"
mjml-table "4.17.1"
mjml-text "4.17.1"
mjml-wrapper "4.17.1"
mjml-accordion "4.16.1"
mjml-body "4.16.1"
mjml-button "4.16.1"
mjml-carousel "4.16.1"
mjml-column "4.16.1"
mjml-divider "4.16.1"
mjml-group "4.16.1"
mjml-head "4.16.1"
mjml-head-attributes "4.16.1"
mjml-head-breakpoint "4.16.1"
mjml-head-font "4.16.1"
mjml-head-html-attributes "4.16.1"
mjml-head-preview "4.16.1"
mjml-head-style "4.16.1"
mjml-head-title "4.16.1"
mjml-hero "4.16.1"
mjml-image "4.16.1"
mjml-navbar "4.16.1"
mjml-raw "4.16.1"
mjml-section "4.16.1"
mjml-social "4.16.1"
mjml-spacer "4.16.1"
mjml-table "4.16.1"
mjml-text "4.16.1"
mjml-wrapper "4.16.1"
mjml-raw@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-raw/-/mjml-raw-4.17.1.tgz#0422013a4b8c6f35afdc624e56b47039c6c174f2"
integrity sha512-CnfgXh+c8u/jOuVjmv9N6Hxal5U4PPJFVY1JFRRJr/7Tcxl8aJUF03mBjqW9zAzoYO1bRcgyG3clchyEwwXQ8g==
mjml-raw@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-raw/-/mjml-raw-4.16.1.tgz#4b5aca950f1f9953b38136ad73e8eb654dd556a7"
integrity sha512-xQrosP9iNNCrfMnYjJzlzV6fzAysRuv3xuB/JuTuIbS74odvGItxXNnYLUEvwGnslO4ij2J4Era62ExEC3ObNQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-section@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-section/-/mjml-section-4.17.1.tgz#10339354719e7e2c02911e56510811fb5bf9fa5b"
integrity sha512-YrkvcBgJw2NBnPirjuVU4AoqwySZzOovm5sfryID9I59EmmG+lbBJOnv/v/5wXQSlw2a4n1+VX2sCUcH5/O5sA==
mjml-section@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-section/-/mjml-section-4.16.1.tgz#c276a262f60433f2c4af8a79a7b36270a1752de6"
integrity sha512-VxKc+7wEWRsAny9mT464LaaYklz20OUIRDH8XV88LK+8JSd05vcbnEI0eneye6Hly0NIwHARbOI6ssLtNPojIQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-social@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-social/-/mjml-social-4.17.1.tgz#52e9f28799a1992ae291b1b7000b7c2b58cb23be"
integrity sha512-Agp6CHJn7SwD+cckCxibZ/32luTzAiDJDlKH0SjQ+9NvSoGskkhii3yOqtYnJ+t3NmQkxpRkXOnUN4GEbupghA==
mjml-social@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-social/-/mjml-social-4.16.1.tgz#0e7ec0f29d14315dd4f3c10279a4f45b50ae2d44"
integrity sha512-u7k+s7LEY5vB0huJL1aEnkwfJmLX8mln4PDNciO+71/pbi7VRuLuUWqnxHbg7HPP130vJp0tqOrpyIIbxmHlHA==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-spacer@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-spacer/-/mjml-spacer-4.17.1.tgz#5a81281872f3f2556c1828bf24e8df475ac71463"
integrity sha512-TxXDosuRzuoQNdceG47TKy+NWbwIGZmVDV/4XRtkcPHEvlsHpIIzn2+zzj+xrA6qh5Z+zlXL+x8ZpWMqrUoKfQ==
mjml-spacer@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-spacer/-/mjml-spacer-4.16.1.tgz#081fc953ecd599419d98f11e168460be734a98b2"
integrity sha512-HZ9S2Ap3WUf5gYEzs16D8J7wxRG82ReLXd7dM8CSXcfIiqbTUYuApakNlk2cMDOskK9Od1axy8aAirDa7hzv4Q==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-table@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-table/-/mjml-table-4.17.1.tgz#0955b75ff86eb80a511cbcd7a37befca8c41101c"
integrity sha512-AcAcsNrpzTOsNc0X0i0+5+iNNGEnYjwn9qodF/413yuWDSH9p7SL8vFuI3Snmgv9s1dR+BKDiF8uPt4XTOMlzA==
mjml-table@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-table/-/mjml-table-4.16.1.tgz#c624355df126574604ba43908b45f8fa577fdca7"
integrity sha512-JCG/9JFYkx93cSNgxbPBb7KXQjJTa0roEDlKqPC6MkQ3XIy1zCS/jOdZCfhlB2Y9T/9l2AuVBheyK7f7Oftfeg==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-text@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-text/-/mjml-text-4.17.1.tgz#77e1598c1e4d98c10490d242c9928ec3aa6e3663"
integrity sha512-pOrz8tRU3hReKd+K69dJmiVndC0+gB5IfVKIK3fdvYMb9laZBAstkXW0j5wn/0Af4FZSlJkDRLM7Ylxbh1+fqQ==
mjml-text@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-text/-/mjml-text-4.16.1.tgz#44ec991595ebd31d70e77f32ef931bcf55f51f3a"
integrity sha512-BmwDXhI+HEe4klEHM9KAXzYxLoUqU97GZI3XMiNdBPSsxKve2x/PSEfRPxEyRaoIpWPsh4HnQBJANzfTgiemSQ==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-core "4.16.1"
mjml-validator@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-validator/-/mjml-validator-4.17.1.tgz#d73fb08bc368763f6bf0898a88b6b8452573b2d2"
integrity sha512-0Au5L5fIfAzOJpQG4PkpFeV0mbzCgjCTu5XbG7pJX4Wup72TGYwrA6Aq2yAdlx17kFPWThSZxeB3Xpd3/kwqOg==
mjml-validator@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-validator/-/mjml-validator-4.16.1.tgz#c49302503f2d8b1f78c1c0cc1d198a94ad4855d9"
integrity sha512-lCePRig7cTLCpkqBk1GAUs+BS3rbO+Nmle+rHLZo5rrHgJJOkozHAJbmaEs9p29KXx0OoUTj+JVMncpUQeCSFA==
dependencies:
"@babel/runtime" "^7.28.4"
mjml-wrapper@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml-wrapper/-/mjml-wrapper-4.17.1.tgz#8832cd7da08a32478189041b8b1c6538f204022a"
integrity sha512-c0bCgXCwffI4krnQYU0Zp8ifGkYMgE7a65NAWXlV3AWEfVmjDlhCcD8LBfZ8UfY8zR3Che8pnunowPZfwh0Nxg==
mjml-wrapper@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml-wrapper/-/mjml-wrapper-4.16.1.tgz#0a00a21296b1bfa7d7f5d58ce16dc0c108a8f34c"
integrity sha512-OfbKR8dym5vJ4z+n1L0vFfuGfnD8Y1WKrn4rjEuvCWWSE4BeXd/rm4OHy2JKgDo3Wg7kxLkz9ghEO4kFMOKP5g==
dependencies:
"@babel/runtime" "^7.28.4"
lodash "^4.17.21"
mjml-core "4.17.1"
mjml-section "4.17.1"
mjml-core "4.16.1"
mjml-section "4.16.1"
mjml@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/mjml/-/mjml-4.17.1.tgz#fe77de5258f31b42532f601ccb10058a2d95bdee"
integrity sha512-aqy5EVZuwXIINl+d7vC1Fn+MzMfIU4qxCx2TUHnGJxYONrtNIgSQEDlgB2ns2oK8a8WgPuEJCZBYwRE+5ZFcng==
mjml@4.16.1:
version "4.16.1"
resolved "https://registry.yarnpkg.com/mjml/-/mjml-4.16.1.tgz#8b6079fe80a9139222eaa35ac58b834bd4608b2d"
integrity sha512-urrG5JD4vmYNT6kdNHwxeCuiPPR0VFonz4slYQhCBXWS8/KsYxkY2wnYA+vfOLq91aQnMvJzVcUK+ye9z7b51w==
dependencies:
"@babel/runtime" "^7.28.4"
mjml-cli "4.17.1"
mjml-core "4.17.1"
mjml-migrate "4.17.1"
mjml-preset-core "4.17.1"
mjml-validator "4.17.1"
mjml-cli "4.16.1"
mjml-core "4.16.1"
mjml-migrate "4.16.1"
mjml-preset-core "4.16.1"
mjml-validator "4.16.1"
no-case@^2.2.0:
version "2.3.2"